IO (input/output) 多路复用
socket就是网络传输,socket也算是网络文件传输操作
所以socket也算是IO操作的一种
而 IO就是通过一种机制,可以监听多个文件描述符(文件句柄)一旦文件句柄出现变化,即可感知到。
1 # socket监听多个端口
2 import socket
3 import select
4 HOST = ('127.0.0.1', 8002)
5
6 sk1 = socket.socket()
7 sk1.bind(('127.0.0.1', 8001))
8 sk1.listen(5)
9
10 sk2 = socket.socket()
11 sk2.bind(('127.0.0.1', 8002))
12 sk2.listen(5)
13
14 sk3 = socket.socket()
15 sk3.bind(('127.0.0.1', 8003))
16 sk3.listen(5)
17 inputs = [sk1]
18 outputs = []
19 massage_dic = {}
20
21 while True:
22 # 第一个参数[sk1,sk2,sk3],select 内部自动监听sk1,sk2,sk3,三个对象,一旦某个句柄发生变化,就会把发生变化的对象加入到l_list中
23 # 第二个参数[]中 不管有没有变化,只有有值,就把值导入到 w_list
24 # 第三个参数[sk1,sk2,sk3],如果谁发生错误了,就会导入 e_list中
25 r_list, w_list, e_list = select.select(inputs, outputs, inputs, 3)
26 print(r_list)
27
28 for sk_conn in r_list:
29 # 如果说这个sk_conn和sk1 相同的话,就把这个conn添加到inputs中,这样在下次再循环的时候,也会监听这个conn的连接实例
30 if sk1 == sk_conn:
31 conn,address = sk1.accept()
32 inputs.append(conn)
33 # 将 此conn(客户端链接实例)作为key存到字典中,value的值为该conn连接实例 发来的消息。
34 massage_dic[conn] = []
35 else:
36 '''
37 data_bytes = sk_conn.recv(1024)
38 if data_bytes:
39
40 data_str = str(data_bytes, encoding='utf-8')
41 sk_conn.sendall(bytes(data_str + '好', encoding='utf-8'))
42
43 else:
44 inputs.remove(sk_conn)
45 (客户端停止链接后,会发送空的字符到服务端,在windows上会出现报错 ,而在Linux和mac下是不会报错的)
46 '''
47 # 如果说不是sk1,那么之前已经把所连接的conn实例添加到inputs中做实时监听,所以只要conn发生变化就会将conn添加到r_list中
48 try:
49 data_bytes = sk_conn.recv(1024)
50 # 如果说 conn客户端那边断开连接后,接受的消息就会出现异常,就把出现异常的conn 从inputs中移除
51 except Exception as e:
52 inputs.remove(sk_conn)
53 else:
54 # 如果正常的接受消息就把该 conn 放到 outputs 中,outputs中存储的就只有给服务端发来消息的客户端实例。
55 outputs.append(sk_conn)
56 # 将客户端发来的消息,添加到massage相应的conn中
57 massage_dic[conn].append(data_bytes)
58 # 再次循环后,w_list中存储的是 给服务端发消息的(客户端实例),massage[conn]也存储的发送的消息信息。
59 for send_conn in w_list:
60 # 每个发送消息的信息为
61 result_str = str(massage_dic[send_conn][0],encoding='utf-8')
62 # 获取到该客户端发送的什么消息,我们岂不是想干啥就干啥!
63 send_conn.sendall(bytes(result_str+'12312313',encoding='utf-8'))
64 # 发送完毕之后,要把该元素从outputs中删除掉,等下次再发消息的时候再来吧
65 outputs.remove(send_conn)
66 del massage_dic[send_conn][0] #这里可以继续优化,利用queue
67
68 for sk_conn in e_list:
69 inputs.remove(sk_conn)
70
71 # select poll epoll
72 # IO 多路复用 是系统底层调用
73 # 最开始的时候都用select(1024个),内部进行for循环检测。
74 # poll 是对select的优化,内部底层还是通过for循环来实现的
75 # epoll 是对IO的革新,底层放弃for循环,使用异步的方法,谁有变化的话,谁就通知。
76 # socketserver 就是由 sockett + select + 多线程 来实现的;