IO阻塞模型、IO非阻塞模型、多路复用IO模型
IO操作主要包括两类:
本地IO
网络IO
本地IO:本地IO是指本地的文件读取等操作,本地IO的优化主要是在操作系统中进行,我们对于本地IO的优化作用十分有限
网络IO:网络IO指的是在进行网络操作时需要等待用户的输入及传输的等待等,网络IO的优化需要我们自己进行,而我们对于网络IO的优化主要在等待用户输入时程序可以继续运行
1、IO阻塞模型
什么是IO阻塞模型
在我们使用socket创建客户端、服务端时,如果不对 他们执行其他操作,那么客户端的recv、send和服务器端的accept、send、recv等都是阻塞的,只有等到有数据传输过来或者有客户端连接过来时才会操作,否则就会进入等待状态,这种模型就是IO阻塞模型
IO阻塞模型的缺点
使用IO阻塞模型时,客户端的影响较小,但是对于服务器端,由于要处理多个客户端的请求,所以如果使用阻塞模型,那么同一时间只能有一个客户端进行连接,效率十分低,不能进行并发
2、IO非阻塞模型
什么是IO非阻塞模型
由于在使用网络IO时,在不进行任何处理的情况下默认是会阻塞的,但是如果不想程序进行阻塞,此时可以通过设置setblocking
来实现,这样在进行原本会阻塞的操作时,如果有数据就会对数据进行处理,如果没有数据则会直接报错,只要进行异常的捕捉就能使程序进行后续代码的执行,这样可以实现IO非阻塞
示例代码:
客户端
import socket
client = socket.socket()
client.connect(("127.0.0.1",1688))
while True:
msg = input("msg:").strip()
if not msg:
continue
try:
client.send(msg.encode("utf-8"))
recv_msg = client.recv(2048)
print(recv_msg.decode("utf-8"))
except ConnectionResetError:
print("客户端意外关闭")
break
服务器
import socket
server = socket.socket()
server.bind(("127.0.0.1",1688))
server.listen()
server.setblocking(False)
conn_list = []
msg_list = []
while True:
try:
conn,addr = server.accept()
conn_list.append(conn)
except BlockingIOError:
print("还没有客户端连接")
for conn in conn_list:
try:
msg = conn.recv(1024)
msg_list.append((conn,msg))
except BlockingIOError:
print("该用户没有数据传输过来")
for msg_t in msg_list:
conn,msg = msg_t
try:
conn.send(msg.upper())
msg_list.remove(msg_t)
except ConnectionResetError:
print("信息无法发送")
conn.close()
conn_list.remove(conn)
IO非阻塞模型的缺点
使用IO非阻塞模型,我们解决了不能实现并发的缺点,在一个线程中实现了并发,但是IO非阻塞模型存在一些问题,最主要的问题是,在使用非阻塞模型时,由于需要不停的进行询问,所以会持续的消耗系统的CPU资源,造成不必要的CPU占用
3、多路复用IO模型
在使用非阻塞IO模型处理问题时,虽然解决了不能在单个线程中实现并发的问题,但是由于需要不停的进行询问,所以就会造成CPU的不必要占用,造成CPU占用过高的问题
什么是多路复用IO模型
多路复用IO模型指的是多个TCP连接使用一个或者少量的线程来实现通信的IO模型,在IO非阻塞模型中,我们是通过自己不停的使用send()
或者recv()
来不停的询问是否有数据需要进行操作,在多路复用IO模型中,我们使用select
统一的来进行询问,并将可以进行操作的对象放到一个列表中进行统一管理,并且select
还可以区分那些是可以发送数据的对象,哪些是可以接收数据的对象,并放在不同的列表中进行统一管理
示例代码:
客户端
import socket
client = socket.socket()
client.connect(("127.0.0.1",1688))
while True:
try:
msg = input("msg:").strip()
if not msg:
continue
client.send(msg.encode("utf-8"))
recv_msg = client.recv(1024).decode("utf-8")
print(recv_msg)
except ConnectionResetError:
print("服务器已关闭")
break
服务器端
import socket
import select
server = socket.socket()
server.bind(("127.0.0.1",1688))
server.listen()
r_list = [server,]
w_list = []
msg_list = []
while True:
readable_list,writeable_list,_ = select.select(r_list,w_list,[])
for conn in readable_list:
if conn == server:
conn,addr = conn.accept()
r_list.append(conn)
else:
try:
msg = conn.recv(1024)
if not msg:
raise ConnectionResetError
msg_list.append((conn,msg))
w_list.append(conn)
except ConnectionResetError:
print("%s客户端正常关闭" %conn)
r_list.remove(conn)
if conn in w_list:
w_list.remove(conn)
conn.close()
for conn in w_list:
for msg_t in msg_lis[:]:
connection,msg = msg_t
if conn == connection:
try:
connection.send(msg.upper())
msg_list.remove(msg_t)
except ConnectionResetError:
msg_list.remove(msg_t)
w_list.remove(conn)
msg_list.remove(msg_t)
w_list.remove(conn)
多路复用的缺点:
使用多路复用解决了在非阻塞IO中出现的CPU占用过高的问题,但是在多路复用中也出现了几个问题:
使用select时最多只能处理1024个客户端,如果数量多与此值,那么就会直接就会报错
如果发送的数据量特别大的情况下只能处理一个客户端,其它的客户端只能进行等待
最新文章
- React Native开发之npm start加速
- 我的Eclipse快捷键.
- string类find函数返回值判定
- JavaScript数组API
- android 自定义弹出框AlertDialog ,很炫的哦
- 修改RMAN list命令输出的时间格式
- Unity3D 5.1烘培 操作
- Map的遍历
- HDU4519
- 玩转Android之二维码生成与识别
- jdk1.8中的for循环
- 休息,归类一下CSS初级的东西
- linux PMBus总线及设备驱动分析
- ip地址扫描
- JavaWeb开发SSM框架搭建详解
- FL studio钢琴卷工具简介
- FJUT3703 这还是一道数论题(二分 + hash + manacher 或者 STL + hash 或者 后缀数组 + hash)题解
- python django day 5 database 1
- JS获取整个网页html代码
- Android 沉浸式顶部
热门文章
- 19、属性赋值-@PropertySource加载外部配置文件
- scrapy 4 学习 crawl spider
- appium测试android环境搭建(win7)
- D. AB-string ( 思维 )
- 【csp模拟赛6】树上统计-启发式合并,线段树合并
- open, create, close
- kubernets安装rabbitmq集群.
- 2016";百度之星"; - 初赛(Astar Round2A)1001 All X(HDU5690)——找循环节|快速幂
- 解决tomcat7控制台中文乱码问题
- 异步机制 - ReadFileEx(WriteFileEx)