多路复用IO的select使用
select基础使用方法
########################################客户端
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('10.11.190.147',8080))
while True:
msg=input('>>: ').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))
client.close()
###########################################服务端
import select
import socket
sk=socket.socket()
sk.bind(('10.11.185.130',8080))
sk.listen(5)
while True:
r,w,e=select.select([sk,],[],[])
#select时会发生阻塞,直到套接字的状态发生变化
#如果不想阻塞,可以在最后再添加一个数字参数,如select。select.([sk,],[],[],5)表示非阻塞,每5秒监听一次
#select会监听第一个列表里的套接字,如果套接字状态变成readable则会把对应的套接字放入r列表中
#当socket连接到新的链接,或者conn的收和发生消息时,他们的状态都会变成readable
print(r)
for obj in r:
conn,addr=obj.accept() #如果socket不accept或者conn不recv,则他们会一直处于readable状态
data=conn.recv(1024)
print(data)
conn.send(data.upper())
完整客户端/服务端
###########################################服务端
import select
import socket
sk=socket.socket()
sk.bind(('10.11.190.147',8080))
sk.listen(5)
inputs=[sk,]#将需要监听的套接字放进inputs列表里
while True:
r,w,e=select.select(inputs,[],[])
for obj in r:
if obj==sk:
conn,addr=obj.accept()
print('connect',conn,'from',addr)
conn.setblocking(0) #将套接字设为非阻塞
inputs.append(conn) #将套接字放入监听列表中
else:
try:
data=obj.recv(1024)
if data:
print('message',data,'from',obj.getpeername())
conn.send(data.upper())
else: #如果接受为空则关闭链接
print('close connect %s',obj.getpeername())
obj.close()
inputs.remove(obj) #移除套接字
except Exception as e: #如果发生错误,则关闭链接
print('Error',e)
print('close connect',obj.getpeername())
obj.close()
inputs.remove(obj) #移除套接字
########################################客户端
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('10.11.190.147',8080))
while True:
msg=input('>>: ').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))
client.close()
除了Select可以实现IO多路复用,还有poll和epoll模块可以实现,它们的不同点是:
Select最多可以监听1024个连接,且管理多个描述符也是进行轮询,根据描述符的状态进行处理,但select在linux,windows,mac上都有
poll解决select的监听个数限制,但是依旧是轮询方式。
epoll解决了轮询问题,可以直接拿到就绪的套接字,但是Windows不支持。
好在我们有selectors模块,帮我们默认选择当前平台下最合适的
Selectors
############################################################服务端
import selectors
import socket
sel=selectors.DefaultSelector()#设置不同平台默认的selectoer
sk=socket.socket()
sk.bind(('10.11.190.147',8080))
sk.setblocking(0)
sk.listen(5)
def accpet(obj,mask):
print('mask is',mask)
conn,addr=obj.accept()
print('connect',conn,'from',addr)
conn.setblocking(0)
sel.register(conn,selectors.EVENT_READ,read) #将套接字放入监听列表,并绑定read函数
def read(obj,mask):
print('mask is',mask)
try:
data=obj.recv(1024)
if data:
print('message:',data,'from',obj.getpeername())
message=input('>>:').strip()
obj.send(message.encode('utf8'))
else:
print('close connect:',obj.getpeername())
sel.unregister(obj) #将套接字从监听列表中移除,不再监听
obj.close()
except Exception as e:
print(e)
print('close connect:',obj.getpeername())
sel.unregister(obj)
obj.close()
sel.register(sk,selectors.EVENT_READ,accpet)#监听套接字sk,并绑定函数accept
while True:
events=sel.select() #开始监听
for obj,mask in events:
callable=obj.data #获得套接字绑定的函数,如果是sk,则callback=accept
callable(obj.fileobj,mask) #执行绑定的函数,obj.fileobj就是拿到的套接字,如果是sk,相当于accpet(sk,mask)
########################################客户端
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('10.11.190.147',8080))
while True:
msg=input('>>: ').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))
client.close()