一、非阻塞IO
-
IO模型:
什么是IO:IO指的是输入输出,其执行速度都非常慢
模型:指固定的套路
IO模型就是所有可以实现输入输出的套路
-
IO的分类:
- 本地IO:指的是输入输出到本地计算机,比如写入硬盘
- 网络IO:指的是输入输出到网络中的计算机,速度远比本地IO慢
非阻塞IO服务端:
import socket
server = socket.socket()
server.bind(('127.0.0.1',21211))
server.listen()
#设置为非阻塞IO
server.setblocking(False)
#所有客户端
clients = []
while True:
try:
client,addr = server.accept()
clients.append(client)
except BlockingIOError:
#存储所有已经关闭的客户端
close_ls = []
#存储所有需要发送数据的客户端
msg_ls = []
for c in clients:
try:
data = c.recv(1024)
if not data:
c.close()
close_ls.append(c)
#把要发送数据的客户端和数据存储到列表中,单独发送
msg_ls.append((c,data))
except BlockingIOError:
pass
except ConnectionResetError:
c.close()
close_ls.append(c)
#处理发送数据
#已经发送完成的客户端和数据
sended_msg = []
for client_and_data in msg_ls:
c = client_and_data[0]
data = client_and_data[1]
try:
c.send(data.upper())
#加入待删除表
sended_msg.append(client_and_data)
except BlockingIOError:
pass
#将已经发送成功的数据从待发送列表中删除
#sended_msg是已经发送完成的数据
for i in sended_msg:
#msg_ls要发送的数据
msg_ls.remove(i)
sended_msg.clear()
#把已经关闭的连接从所有客户端列表中删除
#close_ls是所有关闭的客户端
for i in close_ls:
#clients是所有客户端
clients.remove(i)
close_ls.clear()
非阻塞IO客户端:
import socket
import os
import time
c = socket.socket()
c.connect(('127.0.0.1',21211))
while True:
msg = "hello i am: %s" % os.getpid()
time.sleep(1)
if not msg:
continue
c.send(msg.encode('utf-8'))
print(c.recv(1024).decode('utf-8'))
二、多路复用
-
什么的多路复用:指的是多个连接在复用一个线程,反过来说 一个线程处理多个连接,提高了单线程处理能力
总结:
多路复用提升的是单线程处理网络IO的效率
协程提升的是单线程处理所有IO的效率
多路复用服务端:
import socket
import select
server = socket.socket()
server.bind(('127.0.0.1',21211))
server.listen()
#select 是帮我们监控连接
#需要给它传两个列表,一个是检测是否可读(是否可以执行recv),一个是检测是否可写(是否可执行send)
rlist = [server,]
wlist = []
#默认select是阻塞的,会直到有其中一个或几个需要被处理
#存储要发送的数据
msg = {}
# 返回值
# 1.可读的连接(可以执行recv)
# 2.可写的连接(可以执行send)
while True:
readable_list, writeable_list, _ = select.select(rlist,wlist,[])
#处理可读可写列表
print(readable_list)
#处理可读列表
for c in readable_list:
#说明当需要被处理的是服务器
if c == server:
client, addr = c.accept()
#把客户端也教给select检测是否可读
#这是client..................%s <socket.socket fd=436, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 21211), raddr=('127.0.0.1', 59662)>
rlist.append(client)
else:
print('客户端可以recv了')
data = c.recv(1024)
print(data.decode('utf-8'))
# 把客户端也教给select检测是否可写
#这是c...................%s <socket.socket fd=436, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 21211), raddr=('127.0.0.1', 59662)>
wlist.append(c)
msg[c] = data
print(writeable_list)
print(msg)
#处理可写列表
for w in writeable_list:
w.send(msg[w].upper())
#将已经发送完成的连接从检测列表删除
wlist.remove(w)
多路复用客户端:
import socket
import os
import time
c = socket.socket()
c.connect(("127.0.0.1",21211))
while True:
msg = "hello i am: %s" % os.getpid()
time.sleep(1)
if not msg:
continue
c.send(msg.encode("utf-8"))
print(c.recv(1024).decode("utf-8"))