文章目录
本篇文字是关于IO多路复用的更深入一步的总结,上一篇 Python之进程+线程+协程(事件驱动模型、IO多路复用、select与epoll)对IO多路复用进行了概念性的分析,本篇则是对阻塞IO、非阻塞IO、与异步进行通俗性的比较和归纳。还有另外一种无阻塞IO,即为异步IO,用selectors模块来实现
一、IO多路复用
1、阻塞IO: 不停监听,遇到用户输入时,会一直卡在那里,直到接收了外部数据
2、非阻塞IO: 也可以说是间断监听,过一段时间去监听一次,不会持续卡在那里,但仍然有阻塞,只是没那么严重
3、异步最大的特点: 全程无阻塞
4、selectors模块(异步IO模块): python封装好的一个IO多路复用的模块,等待用户输入的同时让CPU能够去处理其他任务,不会卡在那里,不会有任何阻塞
二、selectors模块
用一个实例来总结selectors模块,每段代码的逻辑在旁边都注有详细的解释了,这里就不拆出来分析了。要用到socket套接字,而且重点在服务端的逻辑,客户端只是正常收发消息就够了。
1、服务端:
import selectors #导入selectors模块
import socket
#创建selectors对象
sel = selectors.DefaultSelector()
#接收信息函数
def accept(sock, mask):
conn, addr = sock.accept() #接收客户端的链接和地址
print('accepted', conn, 'from', addr) #输出收到的链接和地址
conn.setblocking(False) #设置为无阻塞IO
sel.register(conn, selectors.EVENT_READ, read)
#注册selector对象,将客户端链接和read函数绑定,执行read函数
#读取信息
def read(conn, mask):
try:
data = conn.recv(1000) #用客户端的链接读取客户端传来的信息
if not data:
raise Exception #如果监听对象里面的内容为空就触发Excetption异常
#不为空就执行输出消息
print('echoing', repr(data), 'to', conn)
conn.send(data) #输出消息后,给客户端发送一条消息,repr(data)
except Exception as e: #为空就接触selector的注册
print('closing', conn)
sel.unregister(conn)
conn.close()
#创建套接字对象
sock = socket.socket()
sock.bind(('localhost', 8090)) #绑定端口
sock.listen(100) #监听端口
sock.setblocking(False) #设置IO为非阻塞IO
#注册selector对象,
#即将selector将socket对象和accept函数绑定
sel.register(sock, selectors.EVENT_READ, accept)
print("server端已启动......")
#执行绑定的对象,开启监听
while True:
#将events作为监听结果的接收器,包含[sock,,conn1,conn2,conn3......]
events = sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)
#第一次的key.data就是sock触发的那个accept函数,执行accept函数传入两个参数
#第二次的key.data是read函数
#第一次key.fileobj就是服务端创建的sock,一个socket对象
#第二次key.fileobj是conn
2、客户端:
import socket
sk=socket.socket()
sk.connect(("127.0.0.1",8090))
while 1:
inp=input(">>>") #让用户输入内容
sk.send(inp.encode("utf8")) #发送消息
data=sk.recv(1024) #接收消息
print(data.decode("utf8"))
3、参考结果:
可以看到,三个同时聊天,中途可以随意切换,并不会出现任何IO阻塞,这也就异步的效果,得益于selectors这个python的套装模块