IO多路复用
IO多路复用一共三种:select、poll、epoll
select,监听个数最多1024个,内部为for循环,所以效率较差,Windows只支持它。
poll为改进,没有了个数限制,内部依然是for循环
epoll,革新了多路复用,内部为异步方式,select和poll去循环监听的对象,发现有变化的对象对其进行操作,epoll不进行循环,其中的对象有变化自动告诉它。
用select使socket同时监听三个端口:
服务器端:
import socket #创建第一个端口 sk1 = socket.socket() sk1.bind(("127.0.0.1",8001),) sk2.listen(5) #创建第二个端口 sk2 = socket.socket() sk2.bind(("127.0.0.1",8002,)) sk2.listen(5) 创建第三个端口 sk3 = socket.socket() sk3.bind(("127.0.0.1",8003,)) sk3.listen(5) #将三个对象放到一个列表中 inputs = [sk1, sk2, sk3]
import select while True:
#select内部自动监听sk1、sk2、sk3三个对象,一旦那个发生变化,就对其执行相关操作
#如果有人连接8001,那么r_list = [sk1],如果8001和8002同时连接,r_list = [sk1,sk2]
r_list, w_list, e_list = seletc.select(inputs,[],[],1) for sk in r_list:
#每一个连接对象 conn,address = sk.accept()' con.sendall(bytes("hello",encoding = "utf-8" )) conn.close()
客户端1:
import socket
obj = socket.socket()
obj.connect(("127.0.0.1",8001))
obj.recv(1024)
obj.close()
这样,三个客户端同时连接服务器,都可以收到服务器回复的"hello"。
讲解一下上面服务器的select的几个参数:
import socket #创建第一个端口 sk1 = socket.socket() sk1.bind(("127.0.0.1",8001),) sk2.listen(5) #创建第二个端口 sk2 = socket.socket() sk2.bind(("127.0.0.1",8002,)) sk2.listen(5) 创建第三个端口 sk3 = socket.socket() sk3.bind(("127.0.0.1",8003,)) sk3.listen(5) #将三个对象放到一个列表中 inputs = [sk1, sk2, sk3] import select while True: #select内部自动监听sk1、sk2、sk3三个对象,一旦那个发生变化,就对其执行相关操作 #如果有人连接8001,那么r_list = [sk1],如果8001和8002同时连接,r_list = [sk1,sk2]
#第一个参数传了"inputs",这会监听它其中包含的三个对象,如果对象有变化,就将其写入r_list。第二个指定什么参数,就不再变化,第三个是如果对象socket发生错误,将其写入e_list,后面一个"1"表示停留一秒,如果没有对象变化,继续执行 r_list, w_list, e_list = seletc.select(inputs,[sk1,sk2],inputs,1) for sk in r_list: #每一个连接对象 conn,address = sk.accept()' con.sendall(bytes("hello",encoding = "utf-8" )) conn.close()
#e_list包含的是错误列表,所以可以循环这个错误列表,将其从inputs移除
for sk in e_list:
inputs.remove(sk)
下面是一个例子:
多个客户端都可以连接到服务器,但是实际上处理任务并不是并行的。
select监听服务器端和客户端的socket,服务端端socket是sk1,监听sk1,一旦有新的客户端socket连接,就获取其conn加入监听列表,如果conn有变化(发送数据),也会监听到,于是执行else模块,服务器端接收消息。
服务器端:
import socket
import select
sk1 = socket.socket()
sk1.bind(("127.0.0.1", 8009,))
sk1.listen()
inputs = [sk1, ]
while True:
r_list, w_list, e_list = select.select(inputs, [], inputs, 1)
print('正在监听%d个对象' % len(inputs))
print(r_list)
for sk_or_conn in r_list: #如果sk_or_conn = sk1,那就是有客户端第一次连接,获取连接的conn添加到select监听列表
if sk_or_conn == sk1:
conn, address = sk.accept()
inputs.append(conn)
else: #else表示sk_or_conn不等于sk1,那它就是感知到了conn有变化,就是客户端要发送数据,此时sk_or_conn里面到值是conn
try:
by = sk_or_conn.recv(1024)
except Exception as e: #客户端退出会报异常,我们截获异常,将退出的客户端移除
inputs.remove(sk_or_conn)
else:
str1 = str(by, encoding="utf-8")
sk_or_conn.sendall(bytes(str1+"ok!", encoding="utf-8"))
特别注意:r_list的值,因为select监听服务器端(sk1)和客户端两个(conn)socket,如果客户端第一次连接,select感知到到是sk1,那么r_list的值就是sk1,如果是已经连接的客户端发送消息,
那么感知到到是conn,那么r_list的值是conn
客户端1:
import socket
obj = socket.socket()
obj.connect(("127.0.0.1",8009))
while 1:
obj.sendall(bytes(input("input:"),encoding="utf-8"))
by =obj.recv(1024)
st = str(by,encoding="utf-8")
print(st)
上面的例子中,只用了select.select(arg1,arg2,arg3,agr4)第一个参数进行读写,下面用第一个参数读取客户端,第二个参数发送,实现读写分离:
服务器端: import socket import select sk = socket.socket() sk.bind(("127.0.0.1", 8129)) sk.listen() inputs = [sk] outputs = [] while True: r_list, w_list, e_list = select.select(inputs, outputs, inputs, 1) print("正在监听:", len(inputs)) print("this is outputs",len(outputs)) for con_or_sk in r_list: if con_or_sk == sk: conn, address = sk.accept() inputs.append(conn) else: try: con_or_sk.recv(1024) except Exception as ex: print("message:",ex) inputs.remove(con_or_sk) else: outputs.append(con_or_sk) #将con_or_sk添加到第二个参数outputs去发送消息给客户端 for item in w_list: try: item.sendall(bytes("hello",encoding="utf-8")) except Exception as ex: print("无发送对象",ex) outputs.remove(item) for item in e_list: inputs.remove(item) 客户端: import socket while 1: obj = socket.socket() obj.connect(("127.0.0.1", 8129)) obj.sendall(bytes(input("input:"),encoding="utf-8")) by =obj.recv(1024) st = str(by,encoding="utf-8") print(st) obj.close()
还是上面的例子扩展,服务器将客户端连接和消息放在一个字典中,服务器再调用字典将消息发送回去:
服务器端: import socket import select sk = socket.socket() sk.bind(("127.0.0.1", 8192)) sk.listen() inputs = [sk] outputs = [] #定义一个空字典接收客户端的连接和发送过来的消息,给客户端发送时获取这个字典,返回客户端发过来的消息 message = {} while True: r_list, w_list, e_list = select.select(inputs, outputs, inputs, 1) print("正在监听:", len(inputs)) print("this is outputs",len(outputs)) for con_or_sk in r_list: if con_or_sk == sk: conn, address = sk.accept() inputs.append(conn) #将客户端连接作为字典的KEY存入字典 message[conn] = [] else: try: recby = con_or_sk.recv(1024) recstr = str(recby,encoding="utf-8") except Exception as ex: print("message:",ex) inputs.remove(con_or_sk) else: message[con_or_sk].append(recstr) outputs.append(con_or_sk) print(message) for item in w_list: try: recmeg = message[item][0] print("this is recmeg",type(recmeg)) print("thhis is item", item) item.sendall(bytes(recmeg,encoding="utf-8")) except Exception as ex: print("无发送对象",ex) outputs.remove(item) else: del message[item][0] outputs.remove(item) for item in e_list: inputs.remove(item) 客户端: import socket while 1: obj = socket.socket() obj.connect(("127.0.0.1", 8192)) obj.sendall(bytes(input("input:"),encoding="utf-8")) by =obj.recv(1024) st = str(by,encoding="utf-8") print(st) obj.close()

浙公网安备 33010602011771号