You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

非阻塞Socket接收数据后函数阻塞问题排查求助

问题:非阻塞Socket接收数据后陷入阻塞的排查

我写了一段Python代码,从非阻塞Socket读取数据(有数据就打印),但接收并打印数据后,进程会陷入阻塞,只有杀死发送数据的进程才会恢复。我确认Socket已经设为非阻塞,也觉得continue语句可能多余,但就是找不到问题——Python不是我的主力语言,应该是犯了低级错误。


Socket初始化与连接监听代码

# Open socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setblocking(False) # Make port non-blocking
s.bind((HOST, PORT))
s.listen(MAX_CONN)
#print("Listening on port %d" % PORT)

connection_list = []
connection_list.append(s)

while True:
    # Get the list sockets which are ready to be read through select
    readable, writable, errored = select.select(connection_list, [], [], 0) # 0 = No timeout / do not block

    for sock in readable:
        # New connection
        if sock == s:
            # New connection received through server_socket
            sockfd, addr = s.accept()
            connection_list.append(sockfd)
            print("Client (%s, %s) connected" % addr)

接收数据的处理函数(疑似问题所在)

def handle_replies():
    global connection_list
    for sock in connection_list:
        if sock != s: # Ignore our open listening socket
            try:
                data = sock.recv(1024)
            except socket.error as e:
                err = e.args[0]
                if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
                    print("Would block") # Socket has no data for us
                else:
                    # a "real" error occurred
                    print("Error: %s" % e)
                continue # To next sock in list
            else:
                if data:
                    print("<%s> sent %s" % (str(sock.getpeername()), data))
                continue

现象

当用另一个进程发送数据时,会输出类似 <('127.0.0.1', 33196)> sent b'[HELLO:STATUS]',但之后程序就卡住,直到杀死发送进程才恢复。

发送端代码(GTK3 C语言)

GSocket *sock = g_socket_connection_get_socket(connection);
g_socket_set_blocking( sock, FALSE);

/* use the connection */
GInputStream * istream = g_io_stream_get_input_stream (G_IO_STREAM (connection));
GOutputStream * ostream = g_io_stream_get_output_stream (G_IO_STREAM (connection));

bytes = sprintf(buffer, "[HELLO:STATUS]");
g_output_stream_write (ostream, buffer, (gsize)bytes, NULL, NULL);

问题原因与解决方案

核心问题1:新连接的Socket未设置非阻塞

你只在监听Socket上设置了非阻塞,但accept()返回的新连接Socket默认是阻塞模式的!这就导致后续对这个Socket调用recv()时,会直接阻塞住进程,直到有数据或连接关闭。

核心问题2:handle_replies的调用逻辑错误

你的主循环里用select.select(..., 0)实现就绪检测,但你绕开了select的结果,单独调用handle_replies直接遍历所有连接Socket调用recv()——哪怕Socket没有数据就绪。对于非阻塞Socket会抛出EAGAIN,但对于默认阻塞的新连接Socket,就会直接卡住。

核心问题3:空数据处理缺失

当recv()返回空字节串b''时,说明客户端已经关闭了连接,此时你需要把这个Socket从connection_list中移除,否则后续会一直进行无效调用。


修正后的代码要点

  1. 给新连接Socket设置非阻塞:
sockfd, addr = s.accept()
sockfd.setblocking(False)  # 新增这一行!
connection_list.append(sockfd)
  1. 把接收逻辑整合到主循环的readable处理中:
    移除单独的handle_replies函数,直接在select返回的可读Socket列表中处理接收,避免无效操作:
while True:
    readable, writable, errored = select.select(connection_list, [], [], 0.1) # 设小超时降低CPU占用

    for sock in readable:
        if sock == s:
            # 处理新连接
            sockfd, addr = s.accept()
            sockfd.setblocking(False)
            connection_list.append(sockfd)
            print("Client (%s, %s) connected" % addr)
        else:
            # 处理已有连接的接收
            try:
                data = sock.recv(1024)
            except socket.error as e:
                err = e.args[0]
                if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
                    continue
                # 处理真实错误,移除并关闭Socket
                print("Error: %s" % e)
                connection_list.remove(sock)
                sock.close()
            else:
                if data:
                    print("<%s> sent %s" % (str(sock.getpeername()), data))
                else:
                    # 客户端关闭连接,清理资源
                    print("Client (%s, %s) disconnected" % sock.getpeername())
                    connection_list.remove(sock)
                    sock.close()
  1. 优化select超时:
    把select的超时从0改成0.1(或其他合理值),避免无意义的空轮询导致CPU占用过高。

内容的提问来源于stack exchange,提问作者John U

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 00:36:27