IO模型
所需知识
阻塞
- 线程的运行状态:运行 --> 阻塞 --> 就绪 --> 运行
- 影响效率
非阻塞
- 线程的运行状态:运行 --> 就绪 --> 运行
同步
- 提交一个任务,等待他执行完毕
- 只有提交的任务执行完毕才能执行其他操作
- 影响效率
异步
- 只管提交,不等待他执行完毕
- 可以直接进行其他操作
IO步骤
- 数据等待阶段
- 从内核拷贝到进程阶段
非阻塞IO模型
- 进行recv、recvfrom、accept等操作时不会阻塞,可以直接进行其他操作
- 请求数据时,没有数据不阻塞,但是会返回error信息
- 请求数据时,有数据是直接copy到应用程序
- 不推荐使用,轮询会大量占用CPU
非阻塞IO模型实现socket并发
server
import socket
sk = socket.socket()
sk.bind(("127.0.0.1",9000))
sk.setblocking(False)# 设置socket为非阻塞模式
sk.listen()
conn_l = []# 用于存储conn
del_l = []# 用于存储已经断开连接需要删除的连接
while True:
try:
conn,addr = sk.accept()# 不阻塞,但是没有连接会报错
conn_l.append(conn)# 将连接添加到用于统一管理的conn_l中
except BlockingIOError:
try:
for conn in conn_l:# 利用了列表为空是for不执行的原理
msg = conn.recv(1024)# 不阻塞,但是没有数据会报错
if msg == b"":# 返回的内容为空则说明连接已经断开
del_l.append(conn)# 添加到待删除的列表中
continue
print(msg)
conn.send(b"bye")
except BlockingIOError:pass
for i in del_l:
conn_l.remove(i)# 删除已经断开的连接
del_l.clear()# 清空存储已经断开的连接的列表
client
import socket
import threading
def func():
sk = socket.socket()
sk.connect(("127.0.0.1",9000))
sk.send(b"hello")
print(sk.recv(1024))
sk.close()
for i in range(20):
# 使用多线程来模拟多个客户端连接服务器
t = threading.Thread(target=func)
t.start()
IO多路复用模型
- IO多路复用是系统提供的
- 相当于一个代理,这个代理可以监听多个对象的状态,当有对象接收到信息是返回一个状态给应用程序
- 可以解决非阻塞IO高CPU占用的问题
IO多路复用实现socket并发
server
import select
import socket
sk = socket.socket()
sk.bind(("127.0.0.1",8080))
sk.setblocking(False)
sk.listen()
read_list = [sk]
while True:
r_list,w_list,x_list = select.select(read_list,[],[])
for i in r_list:
if i is sk:
conn,addr = i.accept()
read_list.append(conn)
else:
ret = i.recv(1024)
if ret == b"":
i.close()
read_list.remove(i)
continue
print(ret)
i.send(b"bye")
client
import socket
import threading
def func():
sk = socket.socket()
sk.connect(("127.0.0.1",8080))
sk.send(b"hello")
print(sk.recv(1024))
sk.close()
for i in range(20):
# 使用多线程来模拟多个客户端连接服务器
t = threading.Thread(target=func)
t.start()