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()

不足之处

  1. 最多1024的文件描述符(即1024个连接)
  2. 当连接客户增加时,轮询一次会消耗更多时间(程序空间与内核空间的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)