epoll必须在linux系统下才能使用!!!
1.非阻塞套接字的弊端
虽然非阻塞套接字解决了多个客户端连接的问题,但仍存在着不完美之处
- 任何python都是要花费cpu资源的
- 如果数据还没有到达,那么accept、recv和send(在connect没有完成之前),一直在无效的浪费cpu资源
- 对应BlockingIOError的异常处理也是无效的cpu花费
2.epoll才是真正的完美的交互方式
IO多路复用技术
在这里socket则交给了操作系统去监控。以往的版本中,用户进程一直等待操作系统查询是否有数据到来,未到达则一直查询,用户进程一直等待。而epoll则是操作系统监控socket,用户进程处理自己的事务,定期去询问操作系统是否有数据到来,若未到,继续处理自己的事务,到达便接收。
epoll是惰性的事件回调
事件回调:函数中传入函数名和判断条件,若满足条件则调用函数,否则不调用
惰性:操作系统判断事件是否回调,但不会执行回调操作,提醒用户进程执行,只起通知作用
流程:用户进程创建套接字和回调函数,并通知操作系统在套接字上注册回调函数,于是操作系统开始监控提交上来的套接字
用户进程会定期查询已准备好资源的套接字(有数据传输的套接字),操作系统返回回调函数与已准备好资源的套接字
用户进程进行事件回调
epoll是目前Linux上效率最高的IO多路复用 技术!
IO多路复用选择器(epoll选择器)
只有使用epoll选择器才能实现事件回调
import selectors
epoller = selectors.EpollSelector() #实例化一个epoll选择器
注册惰性回调事件
事件回调
完整代码
服务端
import selectors
import socket
epoller = selectors.EpollSelector() #实例化一个epoll选择器
sever = socket.socket() #创建套接字
sever.bind(('',8800)) #绑定
sever.listen(100) #监听
def creat_connection(sever):
print("已连接",sever)
conn,addr=sever.accept()
epoller.register(conn, selectors.EVENT_READ, read_data) #注册到数据接收回调函数
return conn
epoller.register(sever,selectors.EVENT_READ,creat_connection) #epoll选择器注册到连接回调函数
def read_data(conn):
data=conn.recv(1024)
if data:
print(data)
conn.send(data)
else:
print("已断开",conn)
epoller.unregister(conn)
conn.close()
while True:
event = epoller.select() #查询,返回已准备好资源的打包对象
for key,mask in event: #多个客户端,多个对象
sock = key.fileobj #事件
callback = key.data #回调函数
callback(sock)
客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1',8800))
while True:
data = input('please write the message:')
client.send(data.encode())
print(client.recv(1024))
if data == 'Q' or data == 'q':
break
client.close()
运行结果