Python标准库中的select
模块,这个模块提供了select
函数,它能够监视文件描述符,等待它们变得“就绪”(即可读、可写或发生异常)。这在处理I/O、网络通信或异步操作时非常有用。
select模块的基本使用
导入模块
import select
监听文件描述符
# 创建文件描述符列表
read_fds, write_fds, err_fds = select.select(inputs, outputs, exceptions, timeout)
inputs
: 等待读就绪的文件描述符列表。outputs
: 等待写就绪的文件描述符列表。exceptions
: 等待异常的文件描述符列表。timeout
: 超时时间。
示例
import select
import socket
# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('0.0.0.0', 8080))
server_socket.listen()
# 设置为非阻塞模式
server_socket.setblocking(0)
# 准备好读取的列表
inputs = [server_socket]
while True:# 使用select监听readable, writable, exceptional = select.select(inputs, [], [])for s in readable:if s is server_socket:# 处理新的连接connection, client_address = s.accept()print("新连接来自", client_address)connection.setblocking(0)inputs.append(connection)else:# 处理客户端数据data = s.recv(1024)if data:print("收到数据:", data)else:# 没有数据表示客户端断开print("关闭连接")inputs.remove(s)s.close()
这个示例创建了一个简单的TCP服务器,它使用select
来同时处理多个连接。
Python 的 socket
模块通过使用 select
或 poll
系统调用来实现超时判断。这些系统调用允许程序在等待某个文件描述符(例如,socket)的状态变化时指定一个超时值。如果在指定的超时值内没有发生状态变化,系统调用将返回并触发超时异常。以下是这两个系统调用的工作原理和实现方式:
select
系统调用
select
系统调用允许程序监视多个文件描述符,等待它们变为可读、可写或发生异常。select
接受三个文件描述符集和一个超时值作为参数,并在文件描述符状态发生变化或超时后返回。
poll
系统调用
poll
系统调用与 select
类似,但更灵活和高效。它使用一个包含文件描述符及其感兴趣事件的数组,而不是三个独立的文件描述符集。poll
也支持更大的文件描述符数量。
实现原理
在 Python 中,socket
模块通过 _socket
模块和 selectors
模块来使用这些系统调用。以下是一个简化的示例,展示了如何使用 select
来实现超时:
import socket
import select# 创建一个 socket 对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 设置非阻塞模式
s.setblocking(0)# 设置超时(以秒为单位)
timeout = 5# 连接到服务器(注意:此处会立即返回,因为 socket 是非阻塞的)
server_address = ('example.com', 80)
s.connect_ex(server_address)# 使用 select 监视 socket 的可写性(表示连接已建立)
ready = select.select([], [s], [], timeout)if ready[1]:print("Connection established")
else:print("Connection timed out")# 发送一个请求
request = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
s.sendall(request)# 使用 select 监视 socket 的可读性(表示有响应数据可读)
ready = select.select([s], [], [], timeout)if ready[0]:response = s.recv(4096)print("Response received")print(response.decode('utf-8'))
else:print("Receiving response timed out")# 关闭 socket
s.close()
在这个示例中:
-
创建和配置 socket:
- 创建一个 TCP socket,并将其设置为非阻塞模式,以便
connect_ex
调用不会阻塞程序执行。
- 创建一个 TCP socket,并将其设置为非阻塞模式,以便
-
连接到服务器:
- 使用
connect_ex
方法发起连接请求。由于 socket 是非阻塞的,这个调用会立即返回。
- 使用
-
使用 select 监视连接状态:
- 使用
select
函数监视 socket 的可写性(表示连接已建立)。如果在指定的超时时间内 socket 变为可写,连接成功;否则,连接超时。
- 使用
-
发送请求和接收响应:
- 发送请求数据后,再次使用
select
监视 socket 的可读性(表示有数据可读)。如果在指定的超时时间内 socket 变为可读,读取响应数据;否则,接收超时。
- 发送请求数据后,再次使用
通过这种方式,Python 的 socket
模块利用 select
和 poll
系统调用实现了对网络操作的超时判断和处理。