Python Select 轮询
Select适用于类UNIX系统,可用于Socket,File等有文件描述符的动态轮询。
服务端
#Py3环境下,现在大部分迁移到Py3了
import socket
import select
#监听配置
s = socket.socket()
s.bind(('0.0.0.0',8500))
s.listen(1024)
#连接列表,包括服务器
conn_socket = [s,]
#存储可写连接
write_socket = []
while True:
#可读列表,可写列表,错误列表,超时时间(单位秒,空为阻塞)
read_list,write_list,error = select.select(conn_socket,write_socket,[],1)
#轮询开始,将服务器句柄或客户句柄交给内核轮询,当所轮询句柄有变化时,会出现在read_list或error中
#程序提交 -> 内核轮询 -> 程序操作
print(conn_socket)
for client in read_list:
if client == s:
#接收一个新客户,存入连接列表
conn,addr = client.accept()
conn_socket.append(conn)
else:
#接收客户数据
try:
msg = client.recv(1024)
except Exception as ex:
#客户端断线错误
conn_socket.remove(client)
write_socket.remove(client)
else:
#打印接收数据,并添加到可写列表
print(msg)
write_socket.append(client)
for client in write_list:
#向可写列表中的客户发送消息,并从可写列表移除,直到再次客户发送消息
client.sendall(bytes(('2hello2').encode('utf-8')))
write_socket.remove(client)
for client in error:
#移除发生错误的客户端
write_socket.remove(client)
conn_socket.remove(client)
客户端
import socket
s = socket.socket()
s.connect(('127.0.0.1', 8500))
while True:
i = input('> ')
s.sendall(bytes(i, encoding='utf-8'))
rx = str(s.recv(1024),encoding='utf-8')
print(rx)
s.close()
不足之处
- 最多1024的文件描述符(即1024个连接)
- 当连接客户增加时,轮询一次会消耗更多时间(程序空间与内核空间的copy,轮询的时间)
Select模块还有poll和epoll事件模型,一般选择epoll较优。
0716更新:
修复了客户端断开时服务器产生错误而崩溃:
import socket
import select
s = socket.socket()
s.bind(('0.0.0.0',8500))
s.listen(1024)
conn_socket = [s,]
write_socket = []
while True:
read_list,write_list,error = select.select(conn_socket,write_socket,[])
print('正在监听',conn_socket)
for client in read_list:
if client == s:
conn,addr = client.accept()
conn_socket.append(conn)
else:
try:
msg = client.recv(1024)
except Exception as ex:
conn_socket.remove(client)
else:
if msg != b'':
print(msg)
if client not in write_socket:
write_socket.append(client)
else:
conn_socket.remove(client)
for client in write_list:
try:
client.sendall(bytes(('2hello2').encode('utf-8')))
write_socket.remove(client)
except:
conn_socket.remove(client)
write_socket.remove(client)
for client in error:
write_socket.remove(client)
conn_socket.remove(client)