Python I/O多路复用模块select使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#Author :ywq
import select
import socket
import queue


server = socket.socket()
server.bind(('localhost',9000))
server.listen(1000)

server.setblocking(False) #不阻塞

msg_dic = {}

inputs = [server,]
#inputs = [server,conn] #[conn,]
#inputs = [server,conn,conn2] #[conn2,]
outputs = [] #
#outputs = [r1,] #
while True:
readable ,writeable,exceptional= select.select(inputs, outputs, inputs )


print('Readable:',readable,)
print('writable:',writeable)
print('exception:',exceptional)
print('input:',inputs)
for r in readable:
if r is server: #代表来了一个新连接
conn,addr = server.accept()
print("来了个新连接",conn)
inputs.append(conn) #是因为这个新建立的连接还没发数据过来,现在就接收的话程序就报错了
#所以要想实现这个客户端发数据来时server端能知道,就需要让select再监测这个conn
msg_dic[conn] = queue.Queue() #初始化一个队列,后面存要返回给这个客户端的数据
#print(msg_dic)
else: #conn2
data = r.recv(1024)
print("收到数据",data)
msg_dic[r].put(data)
outputs.append(r) #放入返回的连接队列里
# r.send(data)
# print("send done....")

for w in writeable: #要返回给客户端的连接列表
data_to_client = msg_dic[w].get()
w.send(data_to_client) #返回给客户端源数据

outputs.remove(w) #确保下次循环的时候writeable,不返回这个已经处理完的连接了

for e in exceptional:
if e in outputs:
outputs.remove(e)

inputs.remove(e)

del msg_dic[e]

print('Readable:',readable,)
print('writable:',writeable)
print('exception:',exceptional)

'''
1.select.select(rlist,wlist,xlist)运行后生成三个列表,对应变量readable,writeable,exceptional,
三个变量类型也均为list。
2.三个参数:rlist, wlist, xlist,分别代表需要使用select监控的三个装有fd、socket fd的列表,
select会监控列表里的fd,一旦传入的参数rlist内有可读的fd对象,则将该对象加入readable变量中。
一旦传入的参数wlist列表中有可写的fd对象,则将该对象加入writeable变量中,
一旦传入的参数xlist列表中有可连接error报错的fd对象,则将该对象加入exceptional变量中。
3.首先要把服务端socket加入inputs内让socket监控它,一旦服务端变为可读状态,即代表有新连接进来了(此时连接还未建立成功)
开始循环,遍历readable、writeable、exceptional三个列表,列表有数据则运行相应指令
与客户端连接建立后,应将客户端socket加入inputs列表,socket监控其是否可读、是否连接报错,此时刚刚建立连接,服务端
还不能直接socket.recv(1024),否则会报错,因为客户端消息还没发过来,要等待下一次循环。
连接建立后,进入下一轮循环,此时需等待客户端传消息过来,传消息过来后,if not is server,则执行else语句,
接收client端传来的消息,并且把client socket加入outputs列表,socket监控其是否可写,并且创建客户端专属队列,
准备开始传消息
4.进入for in writeable的循环,检测发现客户端已经是可写状态了,则开始传送数据。其实writeable也可以监控inputs列表
不用单独创建outputs列表,毕竟所有的连接socket都已经在inputs里边了,但是为了使遍历writeable列表速度更快,
最好把可写列表单独出来,节省资源。
5.总结:select的本质就是为了替程序快速监控fd、socket的I/O状态,以便根据I/O状态快速开始操作,可读可写时则
开始I/O操作,线程去执行其他计算操作。I/O操作时不消耗计算资源。以此交错开来尽量合理化地利用单线程资源。

'''

一个简单的echo server,使用select I/O复用模型,可以实现多并发连接请求的同时低资源消耗。

赏一瓶快乐回宅水吧~
-------------本文结束感谢您的阅读-------------