一、阻塞IO模型
阻塞IO基于socket程序
原理:
① recv接收数据时,不是直接接收数据,而是程序将系统调用的命令发送到操作系统
② 当操作系统收到接收数据的请求,若此时无数据,操作系统会继续等待,处于等待数据阶段(wait for data阶段),这个阶段相对漫长
③ 当数据来了,操作系统会拷贝数据(copy data 阶段),从内核copy到内存
④ 当操作系统copy结束后,程序就能获取到recv的数据
阻塞IO模型都会经历两个阶段
wait for data 阶段 阻塞
copy data 阶段 阻塞
存在的问题:一旦阻塞不能执行其他的任务
二、非阻塞IO模型
特点:
① 选择性阻塞,可以执行其他任务
② 没有并发编程的机制,是一个同步的程序
③ 由于非阻塞的特点,程序不会在某一个连接recv或者sk.accept上进行阻塞,由更多的时间来做信息的收发,用一条线程实现并发的效果
① 程序发起recv,进行系统调用
② 操作系统接收命令后,若此时无数据,操作系统会返回到程序
③ 当有数据时,操作系统从内核copy到内存里
④ 拷贝结束后,程序会接收到数据
缺点:高速运行,占用太多的CPU导致资源浪费,给CPU造成了很大的负担
程序模拟
# 非阻塞IO模型
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',9000))
sk.setblocking(False) # 设置socket server为非阻塞
sk.listen()
conn_lis = [] # 链接的列表
del_lis = [] # 断开链接的列表
while 1:
try:
conn,addr = sk.accept()
conn_lis.append(conn)
except BlockingIOError:
for conn in conn_lis:
try:
conn.send(b'hello')
print(conn.recv(1024))
except (NameError,BlockingIOError):pass
except ConnectionResetError: # 客户端端口连接后
conn.close() # 关闭链接
del_lis.append(conn) # 添加到断开链接的列表
for del_conn in del_lis: # 从链接列表里删除断开的链接
conn_lis.remove(del_conn)
del_lis.clear() # 清空断开链接的列表
三、IO多路复用模型
原理:非阻塞的情况下加上代理
代理是IO多路复用机制提供的代理,操作系统提供IO多路复用的机制,提供了一个模块select
select 没模块用来操作系统中的select(IO多路复用)机制
select.select(rlist,wlist,xlist) # 按位置传参,传入列表
rlist 读
wlist 写
xlist 特殊的条件
① 程序有一个代理(代替conn),代理进行系统调用,向操作系统发出命令,询问是否有数据
② 此时操作系统没数据,操作系统一直等待数据,代理会一直阻塞
③ 当有数据来了,数据准备好了,通知被代理的对象,conn收到信息,conn发起recv
④ 此操作系统copy数据,操作系统进入copy数据阶段
⑤ copy完成,recv拿到数据
程序
import select
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',9000))
sk.setblocking(False)
sk.listen()
r_lis = [sk] # 存的是socket对象
while 1:
r_l,_,_ = select.select(r_lis,[],[]) # 按位置参数,不需要的参数要传入空列表
for item in r_l:
if item is sk: # 判断是否有链接
conn,addr = sk.accept()
r_lis.append(conn)
else:
try:
print((item.recv(1024)).decode()) # conn有消息接收
item.send(b'hello')
except ConnectionResetError: # 客户端断开链接
item.close() # 关闭链接conn
r_lis.remove(item)
IO多路复用是操作系统的工具,模块 select、poll、epoll都是发挥代理的作用,进行监听。但在windows上只有select模块,而在mac/linux系统上,三种机制并存。
select的底层是操作系统在做轮询,有监听对象个数的限制,并且随着监听对象的个数越来越多,效率越来越低
poll 跟 select 一样,但监听的个数比 select 要多
epoll 给每一个要监听的对象都绑定了一个回调函数,数据来了直接调用对象,不再受到个数增加而降低效率的影响
sockserver 底层使用了IO多路复用 + threading线程实现的
selectors模块,在不同的系统上进行IO多路复用机制的自动筛选
IO多路复用的好处:多个conn使用一个代理,整体的程序显得不忙碌,既能实现并发,又能节省资源
四、异步IO模型
不需要做任何操作,直接发指令即可
过程:程序发起信息,操作系统立即给反应,操作系统一直在监听,当数据来了,会直接将数据放到内存中去,程序可以直接去内存读取
但是python暂时没有对应的模块去使用异步IO
等待数据阶段和拷贝数据阶段都不需要用户处理
所有的操作都由操作系统替你完成
copy数据阶段,只有异步IO是不需要阻塞的