介绍
阻塞IO模型:
当发起类似accept / recv / recvfrom / send等系统调用调用之后,进程并没有被阻塞,
内核马上返回到进程,如果数据还没准备好,此时会返回一个error。
进程在返回之后,可以干点别的事情,然后再次发起系统调用。
重复上面的过程,循环往复的进行accept / recv / recvfrom / send等系统调用调用, 这个过程通常被称之为轮询。
轮询检查内核数据,如果数据准备好了,再将数据拷贝到进程内存中,进行数据处理。
需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态, 只不过非常快而已。
优点
能够在等待任务完成的时间里做其他事情。
缺点
-
循环发起系统调用将大幅度推高CPU占用率,(因为没有阻塞,那么程序将最大限度地处于就绪状态)
在低配主机下极容易出现卡机情况。 -
任务完成的响应延迟增大了,因为每过一段时间才去轮询一次拿数据的操作,
而任务可能在两次轮询之间的任意时间完成。这会导致整体数据吞吐量的降低。
实现
Server端
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""非阻塞IO模型
Server端
非阻塞IO模型:
当发起类似accept / recv / recvfrom / send等系统调用调用之后,进程并没有被阻塞,
内核马上返回到进程,如果数据还没准备好,此时会返回一个error。
进程在返回之后,可以干点别的事情,然后再次发起系统调用。
重复上面的过程,循环往复的进行accept / recv / recvfrom / send等系统调用调用, 这个过程通常被称之为轮询。
轮询检查内核数据,如果数据准备好了,再将数据拷贝到进程内存中,进行数据处理。
需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态, 只不过非常快而已。
优点:
能够在等待任务完成的时间里做其他事情。
缺点:
1. 循环发起系统调用将大幅度推高CPU占用率,(因为没有阻塞,那么程序将最大限度地处于就绪状态)
在低配主机下极容易出现卡机情况。
2. 任务完成的响应延迟增大了,因为每过一段时间才去轮询一次拿数据的操作,
而任务可能在两次轮询之间的任意时间完成。这会导致整体数据吞吐量的降低。
"""
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() # 用于记录conn
wdict = dict() # 用于记录要send数据的conn
while 1:
try: # try代码用于接收conn
conn, addr = sk.accept()
rlist.append(conn)
except BlockingIOError: # except代码主要用于没有conn请求时,进行收/发消息的任务
del_rlist = list() # 记录主动或者异常断开的conn,以便将其移除
for conn in rlist: # for代码块用于接收每个conn发过来的数据
try: # try代码用于检测recv没有数据或者conn主动/异常断开后,去干其他的任务
recvs = conn.recv(1024)
if not recvs:
conn.close()
del_rlist.append(conn) # 类Unix下,异常断开,记录于del_rlist
continue # 下一个conn
print(recvs.decode(encoding='utf-8', errors='strict')) # 正常的conn,打印消息
wdict[conn] = recvs + b'_suffix' # 如果有消息回复,不直接在这里回复,记录到一个字典中
except BlockingIOError: # 表明当前conn的recv没有数据,继续检测下一次conn
continue
except ConnectionResetError: # Windows下, 表明conn异常断开
conn.close() # 关闭conn
del_rlist.append(conn) # 记录于del_list
del_wlist = list() # 当消息发送完成后,记录conn,用于移除conn的send数据
for conn in wdict:
try:
conn.sendall(wdict.get(conn))
del_wlist.append(conn)
except BlockingIOError:
continue
for conn in del_rlist: # 移除异常的conn
rlist.remove(conn)
for conn in del_wlist: # 移除conn的send数据
wdict.pop(conn)
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()