https://www.cnblogs.com/zingp/p/6863170.html
一:IO模型介绍
IO发生时涉及的对象和步骤
对于一个网络IO(network IO),它会涉及到两个系统对象,一个是调用这个IO的process (or thread),另一个就是系统内核(kernel)。
当一个read操作发生时,该操作会经历两个阶段:
1)等待数据准备 (Waiting for the data to be ready)
2)将数据从内核拷贝到进程中(Copying the data from the kernel to the process)
记住这两点很重要,因为这些IO模型的区别就是在两个阶段上各有不同的情况
1 1 import socket 2 2 server=socket.socket() 3 3 server.bind(("localhost",6969)) 4 4 server.listen() 5 5 print("等待用户链接") 6 6 while True: 7 7 conn,addr=server.accept() 8 8 while True: 9 9 conn.send(("%s have connected to server"%addr).encode()) 10 10 data=conn.recv(1024) 11 11 print("from client",data.decode()) 12 12 13 13 14 14 import socket 15 15 client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 16 16 client.connect(("localhost",6969)) 17 17 while True: 18 18 data=client.recv(1024) 19 19 print("from server:",data.decode()) 20 20 client.send("hellow".encode())
1 import socket,time 2 server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 3 server.bind(("localhost",6969)) 4 server.listen() 5 server.setblocking(False)#设置为非阻塞,默认为阻塞 6 print("等待用户链接") 7 while True: 8 try: 9 conn,addr=server.accept() 10 conn.send("you have connected to server".encode()) 11 data=conn.recv(1024) 12 print("from client",data.decode()) 13 conn.close() 14 except Exception as e: 15 print(e) 16 time.sleep(4) 17 ########## 18 import socket,time 19 client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 20 while True: 21 client.connect(("localhost", 6969)) 22 data=client.recv(1024) 23 print("from server:",data.decode()) 24 client.send("hellow".encode()) 25 time.sleep(2) 26 break
由于设置了非阻塞IO(setblocking())所以在accept()的时候会报错,因为抓住了错误,所以开始会输出错误信息 ,有个问题就是服务端接收不到了客户端的数据
IO multiplexing:包括select,epoll,有些地方也称这种IO方式为event driven IO
注意1:select函数返回结果中如果有文件可读了,那么进程就可以通过调用accept()或recv()来让kernel将位于内核中准备到的数据copy到用户区
一:流程
二:IO多路复用的触发方式
- 水平触发
- 边缘触发
三:select实例
1 import socket 2 import select 3 server=socket.socket() 4 server.bind(("localhost",6969)) 5 server.listen() 6 while True: 7 r,w,e=select.select([server,],[],[],5) 8 #rlist -- wait until ready for reading 9 #wlist -- wait until ready for writing 10 #xlist -- wait for an ``exceptional condition'' 11 #阻塞等待链接的时间 12 for i in r: 13 print(r) 14 conn,addr=i.accept() 15 print(conn) 16 print(addr) 17 print("hellow") 18 print(">>>>>") 19 #################### 20 21 import socket,time 22 client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 23 while True: 24 client.connect(("localhost", 6969)) 25 print(client) 26 data=client.recv(1024) 27 print("from server:",data.decode()) 28 client.send("hellow".encode())
在不调用accept的时候,会反复的输出hellow和>是因为select的触发方式为水平触发
1 import select,socket 2 server=socket.socket() 3 server.bind(("localhost",6969)) 4 server.listen(5) 5 inp=[server,] 6 while True: 7 r, w, e = select.select(inp, [], []) 8 for obj in r: 9 if obj == server: 10 print("r:", r) 11 print(len(r)) 12 print(obj) 13 conn, addr = obj.accept() 14 inp.append(conn) 15 else: 16 print("obj:",obj) 17 data=obj.recv(1024).decode() 18 print(">>:",data) 19 data=input("回答%s:"%str(addr)) 20 obj.send(data.encode()) 21 ################################### 22 import socket 23 client=socket.socket() 24 client.connect(("localhost",6969)) 25 print(client) 26 while True: 27 inp=input(">>>>:") 28 client.send(inp.encode()) 29 data=client.recv(1024).decode() 30 print(data)
异步IO全程无阻塞
1 import selectors 2 import socket 3 def accept(server,mask): 4 conn,addr=server.accept() 5 print("client_sock:%s client_addr:%s"%(conn,addr)) 6 sel.register(conn,selectors.EVENT_READ,read)#注册,将conn和read函数绑定 7 def read(conn,mask): 8 try:#window如果客户端断开链接会报错,但是如果是linux客户端断开会发空数据,检测断开手段不一样 9 data=conn.recv(1024) 10 print(data.decode()) 11 conn.send(data) 12 except Exception as e: 13 print("close:%s"%conn) 14 sel.unregister(conn)#解除绑定 15 conn.close() 16 if __name__ == '__main__': 17 18 sel=selectors.DefaultSelector()#生成一个selector的对象 19 print("sel:",sel) 20 server=socket.socket() 21 server.bind(("localhost",6969)) 22 server.listen() 23 sel.register(server,selectors.EVENT_READ,accept)#注册,将server与accept绑定 24 while True: 25 events=sel.select()#相当于select.select()#检测是否有链接或已连接的socket是否发送数据 26 print("events:",events) 27 print(" ") 28 for key,mask in events: 29 print("key:",type(key),key)#(SelectorKey(fileobj=<socket.socket fd=536, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969)>, fd=536, events=1, data=<function accept at 0x02C14E40>) 30 callback=key.data 31 callback(key.fileobj,mask)#调用相应的函数