🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[toc] 下面将以socket网络通信为例,分别通过非阻塞IO和多路复用IO来进行代码实现 ## 非阻塞IO ### server端代码 主要就是将setblocking设置为False后,通过捕获抛出的异常,以判断是否有新连接,是否有接受到数据,然后不停的while循环来实现 ~~~ import socket server=socket.socket() server.bind(('127.0.0.1',8800)) server.listen() server.setblocking(False) con_list=[] rm_list=[] while True: try: conn,addr=server.accept() con_list.append(conn) except BlockingIOError: for conn in con_list: try: msg=conn.recv(1024) conn.send(b'my server') except BlockingIOError:pass except ConnectionResetError: rm_list.append(conn) conn.close() for conn in rm_list: con_list.remove(conn) else: rm_list.clear() #删除关闭的client server.close() ~~~ ### client端代码 ~~~ import socket,os,time client=socket.socket() client.connect(('127.0.0.1',8800)) while True: msg='my client:%s'%os.getpid() print(msg) client.send(msg.encode()) time.sleep(1) print(client.recv(1024)) ~~~ ## 多路复用IO 多路复用,是使用了一个代理`select`来做监听的动作,避免了前一种方式`while循环`的资源消耗 ### server端代码 ~~~py import socket,select server = socket.socket() server.bind(('127.0.0.1', 8800)) server.listen(5) server.setblocking(False) #设置为非阻塞 print('server is start ......') rlist = [server, ] # 读列表,初始只有server wlist = [] # 写列表 xlist = [] # 异常列表 while True: rl, wl, xl = select.select(rlist, wlist, xlist) # print('rlist:', rlist) for sk in rl: if sk == server: # 如果是server则建立连接并添加conn到列表 conn, addr = sk.accept() rlist.append(conn) # else: # 如果是conn,则收发数据 try: conn = sk msg = conn.recv(1024).decode() if not msg:#客户端关闭则删除conn(linux) conn.close() rlist.remove(sock) continue print('接收消息:', msg) conn.send(msg.upper().encode()) except ConnectionResetError: #客户端关闭则删除conn(windows) conn.close() rlist.remove(conn) ~~~ ### client端代码 ~~~py import socket client=socket.socket() client.connect(('127.0.0.1',8800)) while True: msg=input('>>:') if not msg:continue client.send(msg.encode()) data=client.recv(1024) print(data.decode( )) ~~~ ## 基于selectors的IO多路复用 **selectors模块** * 此模块允许基于选择模块原语构建高级别和高效的I / O多路复用。 * 鼓励用户使用此模块,除非他们想要精确控制使用的os级别的原语。 注:selectors也是包装了select高级的包装内置函数,它包装了select与epoll,优先使用epoll,windos内只支持select。 ### server端代码 ~~~py import selectors import socket sock = socket.socket() sock.bind(('127.0.0.1', 8800)) sock.listen(5) sock.setblocking(False) sel = selectors.DefaultSelector() # 生成select实例对象 def read(conn, mask): try: data = conn.recv(1024) if not data: print('closing', conn) sel.unregister(conn) conn.close() return conn.send(data.upper() + b'_SB') except Exception: print('closing', conn) sel.unregister(conn) # 取消注册 conn.close() # 关闭连接 def accept(sock, mask): conn, addr = sock.accept() print('-------', conn) # 注册conn,回调 read函数 sel.register(conn, selectors.EVENT_READ, read) # 注册server事件: # 参数1:sock 进行监听 # 参数2:selectors.EVENT_READ 执行动作 # 参数3:accept,只要来一个链接就回调这个函数 sel.register(sock, selectors.EVENT_READ, accept) while True: print('wating....') events = sel.select() # 调用select:优先使用epoll # 默认阻塞,有活动链接就返回活动的链接列表 for key, mask in events: # callback相当于调accept函数 callback = key.data # 获取函数内存地址,加入参数 # key.fileobj = 文件句柄 callback(key.fileobj, mask) ~~~ ### client端代码 ~~~py import socket client=socket.socket() client.connect(('127.0.0.1',8800)) while True: msg=input('>>:') if not msg:continue client.send(msg.encode()) data=client.recv(1024) print(data.decode( )) ~~~