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
相关产品推荐
相关产品推荐

