目录
- 参考源
- 介绍
- Windows上使用select接口
- 基本工作原理
- 额外说明
- 该模型的优点
- 该模型的缺点
- 实现
- Server端
- Client端
参考源
- cai128118*
- 老男孩教育林海峰(Egon)老师
介绍
操作系统提供了三种IO多路复用机制:
- select
- poll
- epoll
在Windows系统上, 只支持select
在类Unix系统上, 支持以上三种
这其中, select和poll并不适用于大并发, 而epoll适用于大并发.
select
- 内部数据结构: 列表
- 检测方法: 轮询
- 检测数量: 有限
poll
- 数据结构: 不是列表
- 检测方法: 轮询
- 检测数量: 更广
epoll 检测方式并不是轮询, 而是给每个检测的对象添加了回调函数
Windows上使用select接口
基本工作原理
当用户进程调用select之后, 整个进程会阻塞,
而同时, kernel会监视所有select负责的socket对象.
当任何一个socket对象数据准备好时,kernel就会返回,
用户进程再执行拿数据的操作, 将数据拷贝至用户进程内存中.
额外说明
强调:
- 如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。
select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。 - 在多路复用模型中,对于每一个socket,一般都设置成为non-blocking,
但是,整个用户的进程其实是一直被block的。
只不过用户进程是被select这个函数block,而不是被socket IO给block。
结论: select的优势在于可以处理多个连接,不适用于单个连接
该模型的优点
相比其他模型,使用select() 的事件驱动模型只用单线程(进程)执行,占用资源少,不消耗太多 CPU,
同时能够为多客户端提供服务。如果试图建立一个简单的事件驱动的服务器程序,这个模型有一定的参考价值。
该模型的缺点
- 首先select()接口并不是实现“事件驱动”的最好选择。因为当需要探测的句柄值较大时,
select()接口本身需要消耗大量时间去轮询各个句柄。很多操作系统提供了更为高效的接口,
如linux提供了epoll,BSD提供了kqueue,Solaris提供了/dev/poll,…。如果需要实现更高效的服务器程序,类似epoll这样的接口更被推荐。遗憾的是不同的操作系统特供的epoll接口有很大差异,
所以使用类似于epoll的接口实现具有较好跨平台能力的服务器会比较困难。 - 其次,该模型将事件探测和事件响应夹杂在一起,一旦事件响应的执行体庞大,则对整个模型是灾难性的。
实现
Server端
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""IO多路复用
Server端
IO多路复用:
操作系统提供了三种IO多路复用机制:
1. select
2. poll
3. epoll
在Windows系统上, 只支持select
在类Unix系统上, 支持以上三种
这其中, select和poll并不适用于大并发, 而epoll适用于大并发.
select 内部数据结构: 列表
检测方法: 轮询
检测数量: 有限
poll 数据结构: 不是列表
检测方法: 轮询
检测数量: 更广
epoll 检测方式并不是轮询, 而是给每个检测的对象添加了回调函数
Windows上使用select接口
基本工作原理:
当用户进程调用select之后, 整个进程会阻塞, 而同时, kernel会监视
所有select负责的socket对象. 当任何一个socket对象数据准备好时,
kernel就会返回, 用户进程再执行拿数据的操作, 将数据拷贝至用户进程内存中.
强调:
1. 如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。
select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
2. 在多路复用模型中,对于每一个socket,一般都设置成为non-blocking,
但是,整个用户的进程其实是一直被block的。
只不过用户进程是被select这个函数block,而不是被socket IO给block。
结论: select的优势在于可以处理多个连接,不适用于单个连接
该模型的优点:
相比其他模型,使用select() 的事件驱动模型只用单线程(进程)执行,占用资源少,不消耗太多 CPU,
同时能够为多客户端提供服务。如果试图建立一个简单的事件驱动的服务器程序,这个模型有一定的参考价值。
该模型的缺点:
首先select()接口并不是实现“事件驱动”的最好选择。因为当需要探测的句柄值较大时,
select()接口本身需要消耗大量时间去轮询各个句柄。很多操作系统提供了更为高效的接口,
如linux提供了epoll,BSD提供了kqueue,Solaris提供了/dev/poll,…。如果需要实现更高效的服务器程序,
类似epoll这样的接口更被推荐。遗憾的是不同的操作系统特供的epoll接口有很大差异,
所以使用类似于epoll的接口实现具有较好跨平台能力的服务器会比较困难。
其次,该模型将事件探测和事件响应夹杂在一起,一旦事件响应的执行体庞大,则对整个模型是灾难性的。
"""
import select
import socket
sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sk.bind(('', 8080))
sk.listen(32)
sk.setblocking(False)
rlist = list([sk, ])
wlist = list()
xlist = list()
wdict = dict()
while 1:
rl, wl, xl = select.select(rlist, wlist, xlist, None)
for sock in rl:
if sock == sk:
conn, addr = sock.accept()
rlist.append(conn)
else:
try:
recvs = sock.recv(1024)
if not recvs:
sock.close()
rlist.remove(sock)
print(recvs.decode(encoding='utf-8'))
wlist.append(sock)
wdict[sock] = recvs + b'_suffix'
except ConnectionResetError:
sock.close()
rlist.remove(sock)
for sock in wl:
sock.sendall(wdict.get(sock))
wdict.pop(sock)
wlist.remove(sock)
Client端
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""IO多路复用
Client端
"""
import socket
import threading
def my_client():
sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
sk.connect(('localhost', 8080))
while 1:
sk.send(b'hi')
msg = sk.recv(1024)
print(msg)
for i in range(10):
threading.Thread(target=my_client).start()