阻塞IO模型:以前写的套接字通信都是阻塞型的。通过并发提高效率
非阻塞IO模型:
from socket import *# 并不推荐使用,一是消耗cpu资源,二是会响应延迟server = socket(AF_INET, SOCK_STREAM)server.bind(('127.0.0.1',8087))server.listen(5)server.setblocking(False)conn_list = []wlist = []while True: # 死循环,消耗cpu大 try: conn,addr = server.accept() #等待连接 conn_list.append(conn) print(conn_list) except BlockingIOError: # 如果没有客户端发送连接请求,干通信活 del_list=[] # 收消息 for conn in conn_list: # 如果有超多客户端时,可能会服务延迟 try: data = conn.recv(1024) if not data: del_list.append(conn) continue wlist.append((conn,data.upper())) except BlockingIOError: continue except Exception: conn.close() del_list.append(conn) #发消息 del_wlist = [] for item in wlist: try: conn = item[0] data = item[1] conn.send(data) del_list.append(item) except BlockingIOError: pass for item in del_wlist: wlist.remove(item) for conn in del_list: conn_list.remove(conn)server.close()
多路复用IO模型,又叫事件驱动IO,使用select模块或poll(epoll)实现。
select模块优点:只用单线程(进程)执行,占用资源少,同时能为多客户端提供服务。
缺点:select()接口并不是实现‘事件驱动’的最好选择,因为当套接字较多时,需要消耗大量时间去轮询。很多操作系统提供了更为高效的接口,如
linux提供了epoll,BSD提供了kqueue,Solaris提供了/dev/poll。。。
epoll更被推荐(采用异步方式,有回调机制,不需轮询),遗憾的是各操作系统提供的epoll接口差异很大。
selector模块能根据平台选择IO多路复用的不同机制
import socketimport selectserver = socket.socket()server.bind(('127.0.0.1', 8800))server.listen(5)# sock.setblocking(False)rlist = [server, ] # 有新客户端连接时,sock变化wlist = []wdata = {}while 1: rl, wl, el=select.select(rlist, wlist, [], 0.5) # 每过0.5s监听有变化的套接字(server或conn) print('rl', rl) print('wl', wl) for sock in rl: if sock == server: conn,addr = sock.accept() # 客户端发消息时,conn变化 rlist.append(conn) # 有变化的conn加入rlist print('server working...') else: try: data = sock.recv(1024) # linux上 if not data: sock.close() rlist.remove(sock) continue wlist.append(sock) wdata[sock]=data.upper() except Exception: sock.close() rlist.remove(sock) for sock in wl: data = wdata[sock] sock.send(data) wlist.remove(sock) wdata.pop(sock)
异步IO模型