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

Python Socket通信异常排查:recvTillTimeOut函数抛出异常

Socket服务端-客户端数据接收异常问题排查与修复

问题描述

我是Python新手,正在开发Socket服务端-客户端程序,希望实现发送方发送消息时,接收方持续监听直至传输完成。为此编写了recvTillTimeOut函数,预期通过超时机制完成数据接收,但每次连接建立后,该函数都会抛出异常,无法正常运行。

原recvTillTimeOut函数代码

def recvTillTimeOut(s):
    s.settimeout(1)
    data = b''
    while True:
         try:
            data += s.recv(1024)
            print(data)
         except:
            print("error")
            break
    return data

完整原代码

服务端代码

import socket
import pickle

def recvTillTimeOut(s):
    s.settimeout(1)
    data = b''
    while True:
         try:
            data += s.recv(1024)
            print(data)
         except:
            print("error")
            break
    return data

PublicKey = [2342314, 422421]

mySocket = socket.socket()
host = socket.gethostname()
portNum = 12345
mySocket.bind((host, portNum))
mySocket.listen(2)

clientConn, clientAddr = mySocket.accept()

try:
    data = recvTillTimeOut(mySocket)
    clientkey = pickle.loads(data)
    print(clientkey)

    data = pickle.dumps(PublicKey)
    mySocket.send(data)

    while True:
        data = clientConn.recv(1024).decode()
        if not data:
            break
        print("from connected user: " + str(data))
        data = input(" -> ")
        clientConn.send(data.encode())

except:
    mySocket.close()

mySocket.close()

客户端代码

import socket
import pickle

def recvTillTimeOut(s):
    s.settimeout(1)
    data = b''
    while True:
         try:
            data += s.recv(1024)
         except:
            break
    return data

PublicKey = [100123120, 5124210]

host = socket.gethostname()
portNum = 12345
mySocket = socket.socket()
mySocket.connect((host, portNum))

try:
    data = pickle.dumps(PublicKey)
    mySocket.send(data)

    data = recvTillTimeOut(mySocket)
    serverkey = pickle.loads(data)
    print(serverkey)

    while True:
        message = input(" -> ")
        if message == 'bye':
            break
        mySocket.send(message.encode())
        data = mySocket.recv(1024).decode()
        print('Received from server: ' + data)

except:
    mySocket.close()

mySocket.close()

问题根源

  1. 服务端使用错误的Socket对象:服务端调用recvTillTimeOut时传入的是监听Socket(mySocket),但监听Socket只能用于接受新连接,不能直接收发数据,这是导致异常的核心原因。后续发送公钥时同样错误使用了mySocket.send,应该用clientConn(已建立连接的客户端Socket)。
  2. 异常捕获过于笼统:except捕获所有异常,无法区分是超时还是连接断开等其他错误,不利于排查问题。
  3. 依赖超时判断传输完成不可靠:超时时间设置过短可能导致数据未接收完整就停止,过长则会造成不必要的等待,且无法准确判断数据是否传输完毕。

修复方案

1. 修正recvTillTimeOut函数

明确捕获超时异常,恢复原Socket超时设置,避免影响后续操作:

import socket

def recvTillTimeOut(s):
    # 保存原超时设置
    original_timeout = s.gettimeout()
    s.settimeout(1)
    data = b''
    while True:
        try:
            chunk = s.recv(1024)
            if not chunk:
                # 客户端关闭连接,终止接收
                break
            data += chunk
        except socket.timeout:
            # 超时判定为当前无数据,结束接收
            break
        except Exception as e:
            print(f"接收异常: {str(e)}")
            break
    # 恢复原超时配置
    s.settimeout(original_timeout)
    return data

2. 修正服务端核心逻辑

将所有收发数据的操作切换到已建立连接的clientConn对象:

try:
    # 用clientConn接收客户端公钥
    data = recvTillTimeOut(clientConn)
    clientkey = pickle.loads(data)
    print(clientkey)

    # 用clientConn发送服务端公钥
    data = pickle.dumps(PublicKey)
    clientConn.send(data)

    while True:
        data = clientConn.recv(1024).decode()
        if not data:
            break
        print("from connected user: " + str(data))
        data = input(" -> ")
        clientConn.send(data.encode())

except Exception as e:
    print(f"服务端错误: {str(e)}")
    mySocket.close()

3. 更可靠的传输方案(可选)

依赖超时判断传输完成不够稳定,建议采用先发送数据长度,再发送数据的方式,确保接收方准确获取完整数据:

客户端发送数据修改

data = pickle.dumps(PublicKey)
# 先发送4字节的长度信息(大端字节序)
mySocket.send(len(data).to_bytes(4, byteorder='big'))
# 再发送实际数据
mySocket.send(data)

对应接收函数

def recv_exact(s, expected_len):
    """接收指定长度的字节数据"""
    received = b''
    while len(received) < expected_len:
        chunk = s.recv(min(expected_len - len(received), 1024))
        if not chunk:
            raise ConnectionError("连接已中断")
        received += chunk
    return received

def recv_complete(s):
    """先接收数据长度,再接收完整数据"""
    len_bytes = recv_exact(s, 4)
    data_len = int.from_bytes(len_bytes, byteorder='big')
    return recv_exact(s, data_len)

使用时直接调用recv_complete(clientConn)即可准确接收完整的pickle数据,无需依赖超时。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 22:29:59