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

Python Socket+Selectors实现多独立消息收发的问题求助

Python Socket+Selectors实现多独立消息收发的问题求助

兄弟我太懂你这糟心的情况了!你遇到的是TCP粘包问题——TCP本身是流式协议,它完全不关心你调用了几次send,操作系统的TCP栈为了传输效率,会把连续的小数据包合并成一个发送(这就是Nagle算法搞的鬼),所以服务器那边自然就把两次发送的内容当成一坨接收到了。

要解决这个问题,核心思路是自己给消息加上边界标识,让服务器能准确区分开不同的消息。给你几个实用的方案,结合你的代码来改:

方案一:前缀消息长度(最靠谱、通用性最强)

这种方法是行业通用的标准做法:发送每条消息前,先把消息的长度用固定字节数(比如4字节的无符号整数)打包发送,服务器先读取这个长度,再精准读取对应长度的内容,完美避免粘包。

客户端修改示例

先写个辅助函数封装发送逻辑:

import struct
from datetime import datetime

def send_prefixed_message(sock, msg_bytes):
    # 把消息长度打包成4字节大端模式的整数(!I表示网络字节序的无符号int)
    len_prefix = struct.pack('!I', len(msg_bytes))
    # 用sendall确保数据全部写入操作系统缓冲区
    sock.sendall(len_prefix)
    sock.sendall(msg_bytes)

# 替换你原来的发送逻辑
if not data.outb and gvl.global_Counter == 1:
    # 发送global_Status
    status_bytes = bytes(gvl.global_Status, 'utf-8')
    send_prefixed_message(sock, status_bytes)
    
    # 发送时间戳(顺便纠正你原来的strftime格式错误!%M是分钟,小写%m才是月份)
    now = datetime.now()
    mytime_show = now.strftime('%Y-%m-%d %H:%M:%S')
    time_bytes = bytes(mytime_show, 'utf-8')
    send_prefixed_message(sock, time_bytes)
    
    # 清空outb(你的原逻辑里这部分有点绕,直接处理完就清掉就行)
    data.outb = b''

服务器端修改示例

同样写个辅助函数处理接收:

import struct

def recv_prefixed_message(sock):
    # 先读取4字节的长度前缀
    len_data = sock.recv(4)
    if not len_data:
        return None  # 连接已关闭
    # 解析出消息长度
    msg_len = struct.unpack('!I', len_data)[0]
    # 循环读取直到拿到完整的消息
    msg_content = b''
    while len(msg_content) < msg_len:
        chunk = sock.recv(msg_len - len(msg_content))
        if not chunk:
            return None  # 连接异常断开
        msg_content += chunk
    return msg_content.decode('utf-8')

# 在你的selector读事件里替换原来的接收逻辑
readyData = recv_prefixed_message(sock)
if readyData:
    print(f"收到独立消息:{readyData}")
    # 这里就可以分别处理状态消息和时间戳消息了

方案二:用特殊分隔符(适合简单场景)

如果你的消息内容里不会出现某个特殊字符/字符串(比如\r\n或者自定义的###END###),可以在每条消息末尾加上这个分隔符,服务器接收数据时不断累加,直到遇到分隔符就拆分出一条完整消息。

比如客户端发送时:

# 发送状态
sock.sendall(bytes(gvl.global_Status + '\r\n', 'utf-8'))
# 发送时间戳
sock.sendall(bytes(mytime_show + '\r\n', 'utf-8'))

服务器端需要维护一个缓冲区,每次接收数据都追加进去,然后按分隔符拆分:

# 服务器端每个连接维护一个缓冲区
data.recv_buffer += sock.recv(1024)
# 按分隔符拆分
while b'\r\n' in data.recv_buffer:
    msg_bytes, _, data.recv_buffer = data.recv_buffer.partition(b'\r\n')
    readyData = msg_bytes.decode('utf-8')
    print(f"收到独立消息:{readyData}")

这种方法实现简单,但如果消息里不小心出现了分隔符,就会导致消息拆分错误,所以只适合消息内容可控的场景。

方案三:固定消息长度(灵活性差,不推荐)

如果你的每条消息长度完全固定,服务器可以每次固定读取指定字节数,但你的场景里状态和时间戳长度显然不一样,所以这个方案对你不适用,就不多说了。

最后提一句:你原来代码里的strftime格式写错了!%M是分钟,%D是月/日/年,正确的日期时间格式应该是%Y-%m-%d %H:%M:%S(小写m是月份,小写d是日期),别让时间戳格式也出问题哦~

备注:内容来源于stack exchange,提问作者stopbanningmelmao123

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 07:58:08