我正在使用Python进行一些线程化的异步网络实验,使用的是UDP。
我想了解轮询和select Python模块,我以前没有在C/C++中使用过它们。
它们是做什么的?我有点理解select,但它在监视资源时是否会阻塞?轮询的目的是什么?
我正在使用Python进行一些线程化的异步网络实验,使用的是UDP。
我想了解轮询和select Python模块,我以前没有在C/C++中使用过它们。
它们是做什么的?我有点理解select,但它在监视资源时是否会阻塞?轮询的目的是什么?
好的,一次只问一个问题。
下面是一个简单的套接字服务器骨架:
s_sock = socket.socket()
s_sock.bind()
s_sock.listen()
while True:
c_sock, c_addr = s_sock.accept()
process_client_sock(c_sock, c_addr)
服务器会循环并接受来自客户端的连接,然后调用其处理函数与客户端套接字进行通信。这里有一个问题:process_client_sock
可能需要很长时间,甚至包含一个循环(这通常是情况)。
def process_client_sock(c_sock, c_addr):
while True:
receive_or_send_data(c_sock)
在这种情况下,服务器无法接受更多的连接。
一个简单的解决方案是使用多进程或多线程,只需创建一个新线程来处理请求,而主循环继续监听新连接。
s_sock = socket.socket()
s_sock.bind()
s_sock.listen()
while True:
c_sock, c_addr = s_sock.accept()
thread = Thread(target=process_client_sock, args=(c_sock, c_addr))
thread.start()
这当然可以工作,但考虑到性能不够好。因为新的进程/线程需要额外的CPU和内存,对于空闲的服务器可能会有成千上万的连接。
所以,select
和poll
系统调用试图解决这个问题。您给select
设置一组文件描述符,并告诉它在任何fd准备好读取/写入/或发生异常时通知您。
是的,或者取决于您传递给它的参数。
正如select man页面所说,它会获取struct timeval
参数。
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
有三种情况:
timeout.tv_sec == 0 并且 timeout.tv_usec = 0
非阻塞,立即返回。
timeout == NULL
一直阻塞直到文件描述符就绪。
timeout 是常规值
等待一定时间,如果仍然没有可用的文件描述符,则超时并返回。
简单来说,轮询在等待IO时释放CPU以便处理其他工作。
这基于以下几个简单事实:
希望对你有所帮助。
read
或者 recv
,那么你只能等待一个连接。如果你有多个连接,就必须创建多个进程或线程,这会浪费系统资源。select
、poll
或者 epoll
,你可以用一个线程监视多个连接,并在有数据可用时得到通知,然后调用相应连接上的 read
或 recv
函数。select()需要三个套接字列表来检查三种条件(读取、写入、错误),然后返回实际准备处理这些条件的(通常较短,经常为空)套接字列表。
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.bind((Local_IP, Port1))
s1.listen(5)
s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2.bind((Local_IP, Port2))
s2.listen(5)
sockets_that_might_be_ready_to_read = [s1,s2]
sockets_that_might_be_ready_to_write_to = [s1,s2]
sockets_that_might_have_errors = [s1,s2]
([ready_to_read], [ready_to_write], [has_errors]) =
select.select([sockets_that_might_be_ready_to_read],
[sockets_that_might_be_ready_to_write_to],
[sockets_that_might_have_errors], timeout)
for sock in ready_to_read:
c,a = sock.accept()
data = sock.recv(128)
...
for sock in ready_to_write:
#process writes
...
for sock in has_errors:
#process errors
如果一个套接字在等待 timeout 秒后没有尝试连接,那么 ready_to_read 列表将为空 - 此时无论 accept() 和 recv() 是否会阻塞都无关紧要,它们不会被调用。
如果一个套接字准备好读取,那么它将有数据,因此它也不会阻塞。
data = conn.recv(bufsize, socket.MSG_PEEK)
中获取数据大小,并提到阻塞和非阻塞接收之间的区别。 - Jay-Pi