[Errno 54] Connection reset by peer:双客户端跳棋服务器连接中断求助
问题分析与修复方案
核心问题定位
你的服务器代码存在几个关键问题,直接引发了[Errno 54]连接重置错误:
错误缩进导致连接被意外关闭
print("Connection lost")和conn.close()被放在while循环的每次迭代末尾,无论通信是否正常,都会执行关闭操作。正常交互时连接被强制断开,客户端自然抛出连接重置错误。Pickle数据接收不完整
conn.recv(2048)只能接收最多2048字节数据,但棋盘序列化后的Pickle数据可能超过该长度,或单次recv仅拿到部分数据,直接反序列化会触发异常,进而中断连接。空数据处理逻辑顺序错误
先将data赋值给players[player],再判断if not data:,会导致空数据存入列表,引发后续逻辑混乱。宽泛的异常捕获
直接用except:捕获所有异常,无法定位具体错误原因,不利于调试。
修复后的完整代码
import socket from _thread import * from checkers.board import Board import pickle server = "192.168.1.39" port = 5050 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: s.bind((server, port)) except socket.error as e: print(f"绑定失败: {str(e)}") s.listen(2) print("服务器已启动,等待连接...") players = [Board().get_board(), Board().get_board()] def recv_full_data(conn): # 先接收数据长度(4字节大端存储) length_data = conn.recv(4) if not length_data: return None data_length = int.from_bytes(length_data, byteorder='big') # 循环接收完整数据 data = b'' while len(data) < data_length: chunk = conn.recv(min(2048, data_length - len(data))) if not chunk: return None data += chunk return data def threaded_client(conn, player): try: # 发送初始棋盘:先传长度,再传内容 initial_data = pickle.dumps(players[player]) conn.send(len(initial_data).to_bytes(4, byteorder='big')) conn.sendall(initial_data) while True: data = recv_full_data(conn) if not data: print(f"玩家{player}断开连接") break try: players[player] = pickle.loads(data) except pickle.UnpicklingError as e: print(f"反序列化失败: {str(e)}") continue # 准备回复数据 reply = players[1] if player == 0 else players[0] print(f"收到玩家{player}的数据: {reply}") print(f"向玩家{player}发送数据: {reply}") # 发送回复:先传长度,再传内容 reply_data = pickle.dumps(reply) conn.send(len(reply_data).to_bytes(4, byteorder='big')) conn.sendall(reply_data) except socket.error as e: print(f"Socket错误: {str(e)}") except Exception as e: print(f"未知错误: {str(e)}") finally: print(f"与玩家{player}的连接已关闭") conn.close() current_players = 0 while True: conn, addr = s.accept() print(f"已连接到: {addr}") start_new_thread(threaded_client, (conn, current_players)) current_players += 1
关键修复点说明
- 修正连接关闭逻辑:将连接关闭操作移到
finally块,确保仅在循环退出或异常发生时关闭连接,正常通信时不会触发。 - 实现完整数据接收:新增
recv_full_data函数,先接收数据长度,再循环接收直到拿到完整内容,避免Pickle反序列化失败。 - 调整空数据处理顺序:先判断数据是否为空,再执行反序列化和存储操作。
- 精准异常捕获:分别捕获
socket.error、pickle.UnpicklingError等特定异常,打印错误信息方便调试。 - 统一通信格式:发送数据时先传递长度,客户端也需要对应修改该逻辑,确保双方通信格式一致。
客户端对应修改提示
客户端发送和接收数据时,需匹配服务器的长度+内容格式,示例代码片段:
# 发送数据 data = pickle.dumps(board_data) conn.send(len(data).to_bytes(4, byteorder='big')) conn.sendall(data) # 接收数据 def recv_full_data(conn): length_data = conn.recv(4) if not length_data: return None data_length = int.from_bytes(length_data, byteorder='big') data = b'' while len(data) < data_length: chunk = conn.recv(min(2048, data_length - len(data))) if not chunk: return None data += chunk return pickle.loads(data)
内容的提问来源于stack exchange,提问作者Madiuso
相关产品推荐
相关产品推荐

