答案:Python的select模块实现I/O多路复用,通过select.select()监控文件描述符的可读、可写和异常状态,适用于单线程处理多连接场景。示例中构建了非阻塞TCP服务器,监听新连接并收发数据,需维护输入、输出和异常列表,每次调用select前重新传入描述符列表。注意描述符数量限制(通常1024),推荐使用selectors或asyncio提升性能与可维护性。
Python 的 select 模块用于监控多个文件描述符(如套接字),判断它们是否可读、可写或出现异常。它常用于 I/O 多路复用,适用于需要同时处理多个网络连接但不想使用多线程或多进程的场景。
核心函数是 select.select(read_list, write_list, error_list, timeout),它接收三个列表和一个超时时间:
函数返回三个列表,分别对应当前就绪的可读、可写和出错的文件描述符。
下面是一个使用 select 实现的简单 TCP 服务器:
import select import socket创建服务端套接字
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_sock.bind(('localhost', 8888)) server_sock.listen(5) server_sock.setblocking(False) # 设为非阻塞
初始化监听列表
inputs = [server_sock] # 监听可读事件 outputs = [] # 监听可写事件(可选) excepts = [] # 异常监听
print("Server running on localhost:8888")
while True:
调用 select
readable, writable, exceptional = select.select(inputs, outputs, inputs, 1.0) for sock in readable: if sock is server_sock: # 有新连接 client_sock, addr = sock.accept() client_sock.setblocking(False) inputs.append(client_sock) print(f"New connection from {addr}") else: # 已有连接发来数据 try: data = sock.recv(1024) if data: print(f"Received: {data.decode()}") # 可以加入到输出列表,准备回传 if sock not in outputs: outputs.append(sock) else: # 客户端断开 print("Client disconnected") if sock in outputs: outputs.remove(sock) inputs.remove(sock) sock.close() except ConnectionResetError: inputs.remove(sock) if sock in outputs: outputs.remove(sock) sock.close() for sock in writable: # 这里可以发送响应 try: sock.send(b"Echo: Message received\n") outputs.remove(sock) # 发送完移除 except Exception as e: print(f"Send error: {e}") inputs.remove(sock) outputs.remove(sock) sock.close() for sock in exceptional: # 处理异常 inputs.remove(sock) if sock in outputs: outputs.remove(sock) sock.close()
对于复杂应用,建议使用更高层模块:
例如用 selectors 改写会更简洁且性能更好。
基本上就这些。select 模块不复杂但容易忽略细节,掌握它有助于理解底层网络编程机制。