多路复用IO模型
属于事件驱动模型
多个socket使用同一套处理逻辑
如果将非阻塞IO 比喻是点餐的话,相当于你每次去前台,照着菜单挨个问个遍
多路复用,直接为前台那些菜做好了,前台会给你返回一个列表,里面就是已经做好的菜
对比阻塞或非阻塞模型,增加了一个select,来帮我们检测socket的状态,从而避免了我们自己检测socket带来的开销
select会把已经就绪的放入列表中,我们需要遍历列表,分别处理读写即可
案例:
import socketimport timeimport selects = socket.socket()s.bind(("127.0.0.1",1688))# 设置为非阻塞 模型s.setblocking(True) #在多路复用中 阻塞与非阻塞没有区别 因为select会阻塞直到有数据到达为止s.listen()# 待检测是否可读的列表r_list = [s]# 待检测是否可写的列表w_list = []# 待发送的数据msgs = {}print("开始检测了")while True: read_ables, write_ables, _= select.select(r_list,w_list,[]) print("检测出结果了!") # print(read_ables,"可以收数据了") # print(write_ables,"可以发数据了") # 处理可读 也就是接收数据的 for obj in read_ables: # 拿出所有可以读数据的socket #有可能是服务器 有可能是客户端 if s == obj: # 服务器 print("来了一个客户端 要连接") client,addr = s.accept() r_list.append(client) # 新的客户端也交给select检测了 else:# 如果是客户端则执行recv 接收数据 print("客户端发来一个数据") try: data = obj.recv(1024) if not data:raise ConnectionResetError print("有个客户端说:",data) # 将要发送数据的socket加入到列表中让select检测 w_list.append(obj) # 将要发送的数据已经socket对象丢到容器中 if obj in msgs: # 由于容器是一个列表 所以需要先判断是否已经存在了列表 msgs[obj].append(data) else: msgs[obj] = [data] except ConnectionResetError: obj.close() r_list.remove(obj) # 处理可写的 也就是send发送数据 for obj in write_ables: msg_list = msgs.get(obj) if msg_list: # 遍历发送所有数据 for m in msg_list: try: obj.send(m.upper()) except ConnectionResetError: obj.close() w_list.remove(obj) break # 数据从容器中删除 msgs.pop(obj) # 将这个socket从w_list中删除 w_list.remove(obj)
多路复用对比非阻塞 ,多路复用可以极大降低CPU的占用率