39.IO多路复用(用select实现伪并发)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了39.IO多路复用(用select实现伪并发)相关的知识,希望对你有一定的参考价值。
IO多路复用
1.用select实现多端口被多客户端访问的多路复用伪并发
IO多路复用服务端:既读又写
# IO多路复用实现伪并发 用多个IO,可以监听多个文件句柄(socket对象)(一般是可以读了或者可以写了), # 一旦文件句柄出现变化,就可以感应到 # 对于原生的socket 只能处理一个请求,只能监听一个端口 # 1.如何让server端监听两个端口 import socket sk1 = socket.socket() sk1.bind((‘127.0.0.1‘, 8001,)) sk1.listen() sk2 = socket.socket() sk2.bind((‘127.0.0.1‘, 8002,)) sk2.listen() sk3 = socket.socket() sk3.bind((‘127.0.0.1‘, 8003,)) sk3.listen() inputs = [sk3, sk1, sk2, ] import select # select,内部c语言实现,for循环监听多个句柄 最大1024个(window上只支持select) # poll,内部依然用for循环检测无限制 # epool内部使用异步(谁有变化,谁告诉我,不一个一个去问),内部让每个句柄发生变化的时候去告知epoll,不用循环 while True: r_list, w_list, e_list = select.select(inputs, [], [], 1) ‘‘‘ 检测句柄变化 参数1:1为等待时间 1s后进去下次循环 第三个参数:感知句柄错误,从inputs传到e_list,并将错误句柄从第一个参数中去除,程序就不会发生错误 第二个参数:参数中的值,均会传到w_list,无论其是否发生变化 第一个参数:检测句柄变化,inputs中谁变化就将其放入r_list ‘‘‘ # [sk3, sk1, sk2] 内部自动监听sk3,sk1, sk2三个对象,一旦某个句柄发生变化 # 如果有人来连sk3,r_list = [sk]接收客户端的请求 # 如果有人来连sk1,r_list = [sk1]接收客户端的请求 for sk in r_list: # 每一个连接对象 conn, address = sk.accept() conn.sendall(bytes(‘hello‘,encoding=‘utf-8‘)) conn.close()
客户端1:
import socket obj = socket.socket() obj.connect((‘127.0.0.1‘,8001,)) content = str(obj.recv(1024),encoding=‘utf-8‘) print(content) obj.close()
客户端2:
import socket obj = socket.socket() obj.connect((‘127.0.0.1‘,8002,)) content = str(obj.recv(1024),encoding=‘utf-8‘) print(content) obj.close()
2.用select实现,多个客户端的不同端口访问服务器的某一端口的伪并发,读写分离
服务端:
import socket sk1 = socket.socket() sk1.bind((‘127.0.0.1‘, 8001,)) sk1.listen() inputs = [sk1, ] outputs = [] # outputs的内容将会一直存在于w_list中 message = {} import select # select监听socket对象,一旦socket变换就能感知到 ‘‘‘ 服务端: sk1 =socket.socket() conn, address = sk1.accept() conn.recv(1024) 客户端: sk1 = socket.socket() sk1.sendall() 在服务端对象sk1只负责接收用户的连接 而conn才负责专门与每一个客户端的sk1对象通信 ‘‘‘ while True: # 如果有人连接sk1 # r_list = [sk1,] # 如果有人第一次连接,sk1发生变化 r_list, w_list, e_list = select.select(inputs, outputs, inputs, 1) print(‘正在监听的socket对象%d‘ % len(inputs)) print(r_list) for sk1_or_conn in r_list: # 每一个连接对象 if sk1_or_conn == sk1: # 表示有新用户来连接 conn, address = sk1_or_conn.accept() inputs.append(conn) # inputs = [sk1,conn] message[conn] = [] else: try: # 有老用户发消息了 ‘‘‘ 2.7中,客户端断开连接,服务器会接收到空字符串 3.6中,断开后,服务器会报错 ‘‘‘ data_bytes = sk1_or_conn.recv(1024) except Exception as ex: inputs.remove(sk1_or_conn) # 从inputs中移除,不再联系 else: data_str = str(data_bytes, encoding=‘utf-8‘) outputs.append(sk1_or_conn) #w_list记录哪个客户端跟服务器发送过消息 message[sk1_or_conn].append(data_str ) # 本质上还是一个一个处理。只是事情一发生即可处理而已 # select的难点在于它的第一个参数,既放了服务端的socket对象, # 还放了客户端与服务端的通道对象conn # w_list仅仅保存了谁给我发过消息 for conn in w_list: recv_str = message[conn][0] del message[conn][0] conn.sendall(bytes(recv_str + ‘好‘, encoding=‘utf-8‘)) outputs.remove(conn)
客户端:(在python中按快捷键shift+crtl+F10模拟不同的客户端)
import socket obj = socket.socket() obj.connect((‘127.0.0.1‘, 8001,)) while True: inp = input(‘>>>‘) obj.sendall(bytes(inp, encoding=‘utf-8‘)) ret = str(obj.recv(1024), encoding=‘utf-8‘) print(ret)
以上是关于39.IO多路复用(用select实现伪并发)的主要内容,如果未能解决你的问题,请参考以下文章
Python并发编程-IO模型-IO多路复用实现SocketServer