原理:I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪,能够通知程序进行相应的操作。
select执行流程
select需要提供要监控的数组,然后由用户态拷贝到内核态内核态线性循环监控数组,每次都需要遍历整个数组内核发现文件描述符状态符合操作结果,将其返回所以对于我们监控的socket都要设置为非阻塞的,只有这样才能保证不会被阻塞优点
基本各个平台都支持缺点
每次调用select,都需要把fd集合由用户态拷贝到内核态,在fd多的时候开销会很大
单个进程能够监控的fd数量存在最大限制,因为其使用的数据结构是数组。
每次select都是线性遍历整个数组,当fd很大的时候,遍历的开销也很大
所以对于我们监控的socket都要设置为非阻塞的,只有这样才能保证不会被阻塞
服务端
import select import socket socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) socket_server.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) socket_server.bind(('', 10002)) socket_server.listen(5) # 接收连接队列 rlist = [socket_server] # 应答队列 wlist = [] # 错处理的IO事件 xlist = [] while True: # 监控IO发生 rs, ws, xs = select.select(rlist, wlist, xlist) # 遍历三个返回值列表,判断哪个IO发生 for r in rs: # 如果是套接字准备就绪,处理连接 if r == socket_server: c, addr = r.accept() print("Connect from: ", addr) print("当前连接套接字的文件描述符:", c.fileno()) # 将客户端套接字加入新的关注IO rlist.append(c) # 用于沟通的新客户端套接字 else: data = r.recv(1024) if not data: # 客户端断开连接,将客户端套接字关闭,并且从监控列表中移除 rlist.remove(r) r.close() continue print(data.decode("utf-8")) # 主动处理这个IO,作应答 wlist.append(r) for w in ws: w.send(b'OK') wlist.remove(w) for x in xs: pass客户端
import socket flag = 1 s = socket.socket() s.connect(('127.0.0.1', 10002)) while flag: input_msg = input('input>>>') if input_msg == '0': break s.sendall(input_msg.encode()) msg = s.recv(1024) print(msg.decode()) s.close()