IO多路复用用在单线程里,实现并发效果。但其实属于同步IO范畴,是伪并发
常用于网络通信,和socket搭配使用
第一种:
import socket
import select
import time
sk=socket.socket()
sk.bind(("127.0.0.1",9904))
sk.listen(5)
while True:
r,w,e=select.select([sk,],[],[],5) #([监听的输入],[监听的输入],[监听的reeor],[轮循监视的时间间隔],)
print("轮循")
for i in r:
conn,add=i.accept()
print(conn)
print("hello")
print('干其他事')
time.sleep(5)
实现了只能允许一个客户端连接,循环聊天
'''
上面的代码中
???在有一个客户端连接的时候,为什么如果不调用accept,会反复print?
很多答案说是什么select是水平触发方式,不太容易理解
首先一个客户端连接之后,内核态里有了一个sk的双向链接,如果不执行它的accept方法,双向链接一直存在于内核态,未被复制及删除,所以select每次轮循都会执行for循环,自然一直print,传输数据同理
'''
第二种方法:
import socket
import select
import time
sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.setblocking(False)
sk.bind(("127.0.0.1",8801))
sk.listen(5)
inputs=[sk,]
while True:
r,w,e=select.select(inputs,[],[],5)
for obj in r:
if obj==sk:
conn,add=obj.accept()
inputs.append(conn)
else:
data_byte=obj.recv(1024)
print(str(data_byte,'utf8'))
inp=input('回答%s号客户>>>'%inputs.index(obj))
obj.sendall(bytes(inp,'utf8'))
print("这里可以做其他事情")
能允许back_lock个客户端连接,也能和多个客户端循环聊天,只是一个客户端请求到达处理完毕后才能处理下一个,是一种同步模式(这里用input来模拟同步效果)
以上都用到select,除此之外,poll和epoll也可以,性能更优
第三种方法:
import selectors
import socket
sel = selectors.DefaultSelector() #根据操作系统选择最优IO多路复用方式
def accept(sock, mask):
conn, addr = sock.accept() # Should be ready
conn.setblocking(False)
sel.register(conn, selectors.EVENT_READ, read)
def read(conn, mask):
try:
data = conn.recv(1024) # Should be ready
conn.send(data) # Hope it won't block
except Exception:
sel.unregister(conn)
conn.close()
sock = socket.socket()
sock.bind(('localhost', 8090))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept) #注册,监听对象和监听对象有变化后返回值要执行的函数
while True:
events = sel.select()
for key, mask in events:
callback = key.data #key.date是监视对象有变化时返回的函数地址
callback(key.fileobj, mask) #key。fileobj是返回监视的有变化的对象
借助封装select的socketserver库来实现和第二中相同的功能