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

Python中Socket.accept()陷入死循环无返回的问题求助

Python Socket服务端accept()阻塞死循环问题修复

核心问题分析

你的handleserver()方法里第一个for iteration in range(9999999)循环是伪无限循环,且循环内的tcpserversocket.accept()是阻塞式系统调用——没有新客户端发起TCP连接时,这个调用会一直挂起,不会返回,导致后续的UDP消息处理、已连接客户端数据接收逻辑完全无法执行,这就是你看到的"陷入死循环无返回"的根本原因。

解决方案

1. 用多线程分离TCP监听逻辑

把TCP新连接监听放到单独线程,这样主线程可以同时处理UDP消息和已连接客户端的交互,避免单线程下的阻塞问题。

2. 修复无限循环与阻塞逻辑

  • 替换伪无限循环range(9999999)为while True,并添加停止标志(比如self.running)方便优雅退出。
  • 对已连接客户端的recv()操作设置超时,避免单个客户端阻塞整个服务。

3. 修复代码中的其他错误

  • 处理JSON数据时未定义c和data变量,会引发NameError。
  • handlemessage中解密逻辑错误(误用self.privatekey,应该使用服务器自身的私钥)。
  • 客户端连接初始化时,发送公钥需要转为字节流(rsa.PublicKey对象不能直接sendall,需要先序列化)。

修改后的完整代码示例

import socket
import rsa
import base64
import json
import threading

class ConnectedClient():
    def __init__(self, conn, ip, server_privkey):
        self.udp_addr = ip
        self.tcp_socket = conn
        # 设置TCP socket超时,避免recv阻塞
        self.tcp_socket.settimeout(1.0)
        self.server_privkey = server_privkey
        self.connected_fully = False

class Server():
    def __init__(self, ip_str):
        self.socket_ip = self.convert_ip_with_port(ip_str)
        self.tcp_socket_ip = (self.socket_ip[0], self.socket_ip[1] + 1)
        self.next_player_id = 0
        self.running = True

        # TCP服务器初始化
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.tcp_server_socket.bind(self.tcp_socket_ip)
        self.tcp_server_socket.listen(5)
        print(f"TCP服务监听在 {self.tcp_socket_ip}")

        # UDP服务器初始化
        self.udp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.udp_server_socket.bind(self.socket_ip)
        print(f"UDP服务监听在 {self.socket_ip}")

        self.queued_messages = []
        self.connected_clients = []
        self.max_recv_bytes = 1024
        self.pubkey, self.privkey = rsa.newkeys(1024)

        # 启动TCP监听线程
        threading.Thread(target=self.tcp_accept_loop, daemon=True).start()

    def convert_ip_with_port(self, ip_str):
        # 实现你的IP端口转换逻辑,比如从"127.0.0.1:8000"拆分
        ip, port = ip_str.split(":")
        return (ip, int(port))

    def handle_message(self, message):
        msg_bytes, addr = message
        for client in self.connected_clients:
            if client.udp_addr == addr and client.connected_fully:
                try:
                    # 解密消息
                    decoded_msg = base64.b64decode(msg_bytes)
                    plain_text = rsa.decrypt(decoded_msg, self.privkey).decode()
                    print(f"收到来自 {addr} 的加密消息:{plain_text}")
                except Exception as e:
                    print(f"解密失败:{e}")

    def tcp_accept_loop(self):
        """单独线程处理TCP新连接"""
        while self.running:
            try:
                conn, addr = self.tcp_server_socket.accept()
                print(f"新TCP连接:{addr}")
                client = ConnectedClient(conn, addr, self.privkey)
                self.connected_clients.append(client)
                # 发送序列化后的公钥
                conn.sendall(self.pubkey.save_pkcs1())
            except Exception as e:
                if self.running:
                    print(f"TCP监听错误:{e}")
                break

    def handle_connected_clients(self):
        """处理已连接客户端的TCP数据"""
        for client in self.connected_clients[:]:  # 用切片避免遍历中修改列表
            dat = b""
            try:
                while True:
                    data_chunk = client.tcp_socket.recv(1024)
                    if not data_chunk:
                        # 客户端断开连接
                        client.tcp_socket.close()
                        self.connected_clients.remove(client)
                        print(f"客户端 {client.udp_addr} 断开连接")
                        break
                    dat += data_chunk
            except socket.timeout:
                # 超时表示当前没有数据,继续处理下一个客户端
                pass
            except Exception as e:
                print(f"处理客户端数据错误:{e}")
                client.tcp_socket.close()
                self.connected_clients.remove(client)
                continue

            # 解析JSON数据
            if dat:
                try:
                    dat_str = dat.decode()
                    # 简单处理JSON数组或连续JSON对象(假设每个对象用{}包裹)
                    datatable = []
                    start_idx = 0
                    for i, char in enumerate(dat_str):
                        if char == "}":
                            try:
                                json_obj = json.loads(dat_str[start_idx:i+1])
                                datatable.append(json_obj)
                                start_idx = i+1
                            except json.JSONDecodeError:
                                continue
                    # 处理解析后的数据包
                    for obj in datatable:
                        if obj.get('PT') == "CR":
                            client.connected_fully = True
                            print(f"客户端 {client.udp_addr} 已就绪")
                except Exception as e:
                    print(f"解析JSON错误:{e}")

    def handle_server(self):
        """主线程处理UDP消息和客户端数据"""
        while self.running:
            # 处理UDP消息(设置非阻塞或超时)
            self.udp_server_socket.settimeout(0.5)
            try:
                message = self.udp_server_socket.recvfrom(self.max_recv_bytes)
                self.handle_message(message)
            except socket.timeout:
                pass
            except Exception as e:
                print(f"UDP处理错误:{e}")

            # 处理已连接客户端的TCP数据
            self.handle_connected_clients()

# 启动服务示例
if __name__ == "__main__":
    server = Server("127.0.0.1:8000")
    try:
        server.handle_server()
    except KeyboardInterrupt:
        server.running = False
        print("服务已停止")

关键改动说明

  • 新增tcp_accept_loop线程函数,专门处理TCP新连接,避免阻塞主线程。
  • 给所有Socket操作添加超时或非阻塞逻辑,防止单点阻塞整个服务。
  • 修复了JSON解析中的变量未定义问题,优化了解析逻辑。
  • 修正了公钥发送的序列化问题(save_pkcs1()转为字节流)。
  • 添加了客户端断开连接的处理逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 15:14:52