IO 多路复用
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 + 多线程 来实现的;