IO多路复用定义:同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO。
以此形成可以同时处理多个IO的行为,避免一个IO阻塞造成其他IO无法执行的情况,提高了IO执行效率。
具体执行方案:
1. select : windows linux unix
2. poll : linux unix
3. epoll : linux
1.select 方法
rs,ws,xs=select(rlist,wlist,xlist,[timeout])
功能:监控多个IO事件,阻塞等待IO发生,
参数: rlist 列表 存放的关注等待发生的IO事件
wlist列表:存放要主动处理的IO事件
xlist列表:存放发生异常时要处理的IO
timeout 超时时间
* wlist中如果有IO事件,则select会立即返回给ws
* 处理IO事件过程中不要出现死循环等长期占有服务端情况
* IO多路复用消耗资源较少,效率较高
1.select_server.py
from select import select
from socket import *
#创建套接字作为关注的IO
s=socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
#绑定地址
s.bind(('0.0.0.0',9610))
#设置监听
s.listen(3)
#将套接字添加到关注列表
rlist=[s]
wlist=[]
xlist=[]
#循环监控IO
while True:
rs,ws,xs=select(rlist,wlist,xlist)
遍历监听列表
for r in rs:
#s就绪说明有客户端连接
if r is s:
#建立连接
c,addr=r.accept()
print("Connect addr:"addr)
#将客户端套接字加入监听列表
rlist.append(c)
#如果是c则表示对应的客户端发送消息
else:
data=r.recv(1024)
if not data: #如果客户端退出,则将对应的套接字从监听列表中移除
rlist.remove(r)
r.close()
print(data.decode())
#把r放入wlist中表示希望主动处理
wlist.append(r)
for w in ws:
w.send(b'ok')
wlist.remove(w)
for in xs:
pass`
2.epoll方法实现:
from select import *
from socket import *
#创建需要被关注的IO对象
s=socket()
#设置允许在同一端口的同一服务器上使用多个实例
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
#绑定地址
s.bind(('0.0.0.0',9610))
#设置监听
s.listen(5)
#创建epoll对象
p=epoll()
#建立地图
fdmap={s.fileno():s}
#设置IO关注
p.register(s,EPOLLIN|EPOLLERR)
#循环监控IO:
while True:
events=p.poll()
print("你有要处理的IO哦")
#遍历events 处理IO
for fd,event in events:
if fd==s.fileno():
c,addr=fdmap[fd].accept()
print("Connect from :"addr)
#添加新的关注IO
p.register(c,EPOLLIN|EPOLLHUP|EPOLLET)
fdmap[c.fileno()] = c
elif event & EPOLLHUP: #如果监控到的事件EPOLLHUP则执行操作
print("客户端退出")
p.unregister(fd)
fdmap[fd].close()
del fdmap[fd]
#elif event & EPOLLIN:
# data = fdmap[fd].recv(1024)
#print(data.decode())
#fdmap[fd].send(b'OK')
3.poll实现方法(与epoll实现方法非常的类似)
from select import *
from socket import *
# 创建关注的IO
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8888))
s.listen(5)
# 创建poll对象
p = poll()
# 建立地图
fdmap = {s.fileno():s}
# 关注IO
p.register(s,POLLIN|POLLERR)
# 循环监控IO
while True:
events = p.poll()
# 遍历events 处理IO
for fd,event in events:
if fd == s.fileno():
c,addr = fdmap[fd].accept()
print("Connect from",addr)
# 添加新的关注IO
p.register(c,POLLIN|POLLHUP)
fdmap[c.fileno()] = c
elif event & POLLHUP:
print("客户端退出")
p.unregister(fd)
fdmap[fd].close()
del fdmap[fd]
elif event & POLLIN:
data = fdmap[fd].recv(1024)
print(data.decode())
fdmap[fd].send(b'OK')
注意:select、epoll、poll 实现IO多路复用的核心思想都是一样的,此处所指的均是流式套接字。将服务端套接字加入监听,当顺利连接客户端后,将客户端套接字加入监听行里,每次有新的客户端连接时,就加入监听队列中。服务端套接字用于进行客户端的连接,客户端套接字,用于和客户端进行消息的收发操作。