Python基础入门 Day46:使用 select 实现多路复用网络通信

78次阅读
没有评论

共计 1388 个字符,预计需要花费 4 分钟才能阅读完成。

在网络编程中,如果我们希望一个服务器同时响应多个客户端连接请求,并在各自的连接中处理数据,最简单的方式是为每个连接开一个线程或进程,但这样会迅速耗尽资源。更高效的做法是使用 I/O 多路复用技术。Python 提供了 select 模块来支持这种模式。

一、什么是 I/O 多路复用
I/O 多路复用(Multiplexing)是一种让单个进程可以监控多个 socket 连接的技术。它通过监听一组 socket 的状态变化,在某个 socket 可读或可写时通知应用程序执行对应操作。

二、select 模块概述
select.select(rlist, wlist, xlist[, timeout]) 接收三个列表参数,返回三个列表:

  • rlist:等待可读的 socket 列表
  • wlist:等待可写的 socket 列表
  • xlist:等待异常的 socket 列表

返回结果为三个列表,表示当前就绪的 socket。

三、TCP 多客户端通信示例

import socket
import select

# 创建 TCP 服务器 socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('127.0.0.1', 9000))
server.listen(5)
server.setblocking(False)  # 设置非阻塞模式

# 存放所有客户端 socket
inputs = [server]
print(" 服务器启动,监听端口 9000")

while True:
    readable, _, _ = select.select(inputs, [], [])
    for sock in readable:
        if sock is server:
            conn, addr = server.accept()
            print(" 新连接:", addr)
            conn.setblocking(False)
            inputs.append(conn)
        else:
            try:
                data = sock.recv(1024)
                if data:
                    print(" 收到:", data.decode())
                    sock.send(b" 服务器已收到您的消息 ")
                else:
                    print(" 客户端断开连接 ")
                    inputs.remove(sock)
                    sock.close()
            except:
                inputs.remove(sock)
                sock.close()

四、关键点解析

  • 设置 socket 为非阻塞模式:setblocking(False),防止某个连接阻塞整个程序。
  • select.select() 监听多个 socket,一旦某个 socket 可读,即执行数据处理。
  • 无需为每个连接新开线程,节省系统资源。

五、与多线程 / 多进程比较

方式 优点 缺点
多线程 简单直观,逻辑清晰 线程数多时调度复杂,性能下降
多进程 稳定性高 资源开销大
select 多路复用 高效,适合大量连接 编程复杂度略高

六、练习建议

  1. 尝试将之前的 TCP 聊天程序修改为基于 select 的方式。
  2. 结合字典管理多个客户端的昵称,实现简单群聊功能。
  3. 加入消息广播功能,让一个客户端的消息能被所有客户端接收。

多路复用技术是构建高并发服务器的核心之一,理解其原理和应用场景,将为后续学习异步框架如 asyncioTwisted 打下基础。明天我们将探索 asyncio 的使用。

正文完
 0
评论(没有评论)