2. TCP多客户端连接管理
任务目标
实现一个基于 selectors 模块的 TCP 多客户端回显服务器,能够同时处理多个客户端连接,并将每个客户端发送的消息原样返回。客户端可随时连接、发送消息并断开,服务器无需为每个连接创建独立线程。
环境依赖
仅使用 Python 3.7+ 标准库,无需安装第三方包。核心模块为 socket、selectors。
完整实现
import selectors
import socket
import sys
sel = selectors.DefaultSelector()
def accept_connection(sock):
conn, addr = sock.accept()
conn.setblocking(False)
sel.register(conn, selectors.EVENT_READ, read_message)
print(f"[连接] {addr} 已接入")
def read_message(conn):
try:
data = conn.recv(1024)
except ConnectionResetError:
data = b""
if data:
conn.sendall(data) # 回显
else:
addr = conn.getpeername()
sel.unregister(conn)
conn.close()
print(f"[断开] {addr} 已离开")
def main(host="127.0.0.1", port=8888):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((host, port))
server.listen(100)
server.setblocking(False)
sel.register(server, selectors.EVENT_READ, accept_connection)
print(f"[启动] 监听 {host}:{port}")
try:
while True:
events = sel.select(timeout=None)
for key, _ in events:
callback = key.data
callback(key.fileobj)
except KeyboardInterrupt:
print("\n[退出] 服务器关闭")
finally:
sel.close()
server.close()
if __name__ == "__main__":
if len(sys.argv) == 3:
main(sys.argv[1], int(sys.argv[2]))
else:
main()
运行与验证
启动服务器:
python server.py
默认监听 127.0.0.1:8888,也可通过命令行参数指定:python server.py 0.0.0.0 9000。
验证步骤:
- 打开两个终端,分别运行
nc 127.0.0.1 8888或telnet 127.0.0.1 8888,建立两个独立客户端连接。 - 在客户端 A 输入
hello,应立即收到hello回显;在客户端 B 输入world,同样收到world。两个连接互不阻塞。 - 关闭其中一个客户端(Ctrl+C 或输入 Ctrl+D),服务器终端应打印
[断开]信息,另一个客户端仍可正常收发。 - 服务器终端出现
[连接]与[断开]日志,且无异常堆栈,即判定通过。
关键说明
- 服务器使用
selectors实现 I/O 多路复用,单线程即可管理所有连接,避免了线程切换开销与共享内存竞争问题。 - 所有 socket 均设为非阻塞模式,配合事件循环驱动;
recv()返回空字节串表示客户端已关闭连接,此时注销并释放资源。 - 回显逻辑在
read_message中完成,sendall保证数据完整发送。若需扩展为广播或聊天室,只需在此函数中修改转发目标即可。