IO 多路复用
多路复用也是要用单线程来处理客户端并发,与其他模型相比多出了select这个模块。 程序不再直接问操作系统要数据,而是先发起一个select调用,select会阻塞直到其中某个socket准备就绪,此时应用程序再发起系统调用来获取数据,由于select已经帮我们确认了某个socket一定是就绪态,所以后续的recv,send,等操作可以立即完成,不会阻塞。简单的说,select相当于一个中间者,专门帮你看着socket,那个socket准备好了select就返回那个。
你可以把select当做托儿所,把你的socket交给它来看管,当某个socket 要上厕所或者要吃饭的时候,select就会把它交给你。
io多路复用
用一个线程来并发处理所有的客户端
原来我们是直接问操作系统要数据
如果是io操作 没有数据就会进入阻塞状态
非io操作 没有数据就会抛出异常,然后继续询问操作系统
在多路复用模型中 要先问select模块 哪些socket已经准备就绪 然后在处理这些已经就绪的select,既然是已经就绪,那么执行recv,或是send 就不会再阻塞 select模块就只有select一个函数
参数1:r_list需要被select检测是否是可读状态的客户端,把所有的socket放到该列表中,select会负责从中找出读取数据的socket
参数2:w_list需要被select检测是否可写的客户端 把所有的socket,select会负责从中找到可以写入数据的socket
参数3:x_list存储着要被检测异常条件。。。忽略即可
返回一个元组 包含三个列表
readables 已经处于可读状态的socket 即数据已经到达缓冲区
writeables 已经处于可写的状态的socket 即缓冲区没满,可以发送
从可读或可写的列表中拿出所有的socket 依次处理他们即可
服务器:
import socket
import select
s = socket.socket()
s.bind(("127.0.0.1",6666))
s.listen()
#在多路复用中 一旦交给select交给你一个socket一定意味着该socket已经准备就绪 可读或者可写
r_list = []
w_list =[]
#存储需要发送的数据 以及对应的socket 把socket作为key 数据作为value
data_dic = {}
while True:
readables,writeables,_ = select.select(r-_list,w_list,[])
#接收数据以及服务器建立连接
for i in readables:
if i ==s:#如果是服务器 就执行accept
c,addr = i.accept()
r_list.append(i)
else:#是一个客户端 那就recv收数据
try ;
data = i.recv(1024)
#Linux中对方强行下线 或是Windows中正常下线
if not data:
i.close()
r_list.remove(i)
continue
print(data)
#发送数据 不清楚目前是否可以发送数据,所以交给select来检测
w_list.append(i)
#把要发送的数据先存着,等select告诉你这个连接可以发送时在发送
data_dic[i]=data
except ConnectionResetError:#Windows强行下线
i.close()
r_list.remove(i)#从检测列表中删除
for i in writeables:
try:
i.send(data_dic[i].upper())#返回数据
#data_dic.pop(i)
#w_list.remove(i)
except ConnectionResetError:#对面下线异常
i.close()
finally:
data_dic.pop[i]
w_list.remove(i)#从检测列表中删除
#客户端
import socket
c = socket.socket()
c.connect((“127.0.0.1”,6666))
print('connect....')
while True:
msg = input(">>>>").strip()
if not msg:continue
c.send(msg.encode.('utf-8')
data = c.recv(1024)
print(data.decode.('utf-8')