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 import socket
2 server=socket.socket()
3 server.bind(("localhost",6969))
4 server.listen()
5 print("等待用户链接")
6 while True:
7 conn,addr=server.accept()
8 while True:
9 conn.send(("%s have connected to server"%addr).encode())
10 data=conn.recv(1024)
11 print("from client",data.decode())
12
13
14 import socket
15 client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
16 client.connect(("localhost",6969))
17 while True:
18 data=client.recv(1024)
19 print("from server:",data.decode())
20 client.send("hellow".encode())
阻塞IO
import socket,time
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("localhost",6969))
server.listen()
server.setblocking(False)#设置为非阻塞,默认为阻塞
print("等待用户链接")
while True:
try:
conn,addr=server.accept()
conn.send("you have connected to server".encode())
data=conn.recv(1024)
print("from client",data.decode())
conn.close()
except Exception as e:
print(e)
time.sleep(4)
##########
import socket,time
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
while True:
client.connect(("localhost", 6969))
data=client.recv(1024)
print("from server:",data.decode())
client.send("hellow".encode())
time.sleep(2)
break
非阻塞IO
由于设置了非阻塞IO(setblocking())所以在accept()的时候会报错,因为抓住了错误,所以开始会输出错误信息 ,有个问题就是服务端接收不到了客户端的数据
IO multiplexing:包括select,epoll,有些地方也称这种IO方式为event driven IO
注意1:select函数返回结果中如果有文件可读了,那么进程就可以通过调用accept()或recv()来让kernel将位于内核中准备到的数据copy到用户区
一:流程
二:IO多路复用的触发方式
- 水平触发
- 边缘触发
三:select实例
import socket
import select
server=socket.socket()
server.bind(("localhost",6969))
server.listen()
while True:
r,w,e=select.select([server,],[],[],5)
#rlist -- wait until ready for reading
#wlist -- wait until ready for writing
#xlist -- wait for an ``exceptional condition''
#阻塞等待链接的时间
for i in r:
print(r)
conn,addr=i.accept()
print(conn)
print(addr)
print("hellow")
print(">>>>>")
#################### import socket,time
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
while True:
client.connect(("localhost", 6969))
print(client)
data=client.recv(1024)
print("from server:",data.decode())
client.send("hellow".encode())
select实现IO多路复用
在不调用accept的时候,会反复的输出hellow和>是因为select的触发方式为水平触发
import select,socket
server=socket.socket()
server.bind(("localhost",6969))
server.listen(5)
inp=[server,]
while True:
r, w, e = select.select(inp, [], [])
for obj in r:
if obj == server:
print("r:", r)
print(len(r))
print(obj)
conn, addr = obj.accept()
inp.append(conn)
else:
print("obj:",obj)
data=obj.recv(1024).decode()
print(">>:",data)
data=input("回答%s:"%str(addr))
obj.send(data.encode())
###################################
import socket
client=socket.socket()
client.connect(("localhost",6969))
print(client)
while True:
inp=input(">>>>:")
client.send(inp.encode())
data=client.recv(1024).decode()
print(data)
select实现监听多链接
异步IO全程无阻塞
import selectors
import socket
def accept(server,mask):
conn,addr=server.accept()
print("client_sock:%s\nclient_addr:%s"%(conn,addr))
sel.register(conn,selectors.EVENT_READ,read)#注册,将conn和read函数绑定
def read(conn,mask):
try:#window如果客户端断开链接会报错,但是如果是linux客户端断开会发空数据,检测断开手段不一样
data=conn.recv(1024)
print(data.decode())
conn.send(data)
except Exception as e:
print("close:%s"%conn)
sel.unregister(conn)#解除绑定
conn.close()
if __name__ == '__main__': sel=selectors.DefaultSelector()#生成一个selector的对象
print("sel:",sel)
server=socket.socket()
server.bind(("localhost",6969))
server.listen()
sel.register(server,selectors.EVENT_READ,accept)#注册,将server与accept绑定
while True:
events=sel.select()#相当于select.select()#检测是否有链接或已连接的socket是否发送数据
print("events:",events)
print("\n")
for key,mask in events:
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>)
callback=key.data
callback(key.fileobj,mask)#调用相应的函数
selctor实现IO多路复用