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

[Errno 54] Connection reset by peer:双客户端跳棋服务器连接中断求助

问题分析与修复方案

核心问题定位

你的服务器代码存在几个关键问题,直接引发了[Errno 54]连接重置错误:

  1. 错误缩进导致连接被意外关闭
    print("Connection lost") 和 conn.close() 被放在while循环的每次迭代末尾,无论通信是否正常,都会执行关闭操作。正常交互时连接被强制断开,客户端自然抛出连接重置错误。

  2. Pickle数据接收不完整
    conn.recv(2048) 只能接收最多2048字节数据,但棋盘序列化后的Pickle数据可能超过该长度,或单次recv仅拿到部分数据,直接反序列化会触发异常,进而中断连接。

  3. 空数据处理逻辑顺序错误
    先将data赋值给players[player],再判断if not data:,会导致空数据存入列表,引发后续逻辑混乱。

  4. 宽泛的异常捕获
    直接用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 19:42:45