You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Python Socket多数据收发故障:客户端接收串连问题排查与修复

Python Socket多组数据收发串接问题的解决办法

你遇到的这个串连接收问题,本质是TCP Socket的特性导致的——TCP是流式协议,它不会帮你区分不同消息之间的边界。你在服务器端连续调用send()发送的内容,操作系统会通过Nagle算法优化,把多个小数据包合并成一个TCP段发送,所以客户端第一次调用recv(60)时,直接把所有发送的内容都读走了,后面的recv()自然就拿不到数据了。

下面给你两种靠谱的修复方案,你可以根据需求选择:

方案一:用分隔符区分消息边界

这种方法简单直观,适合内容里不会出现分隔符的场景,比如文件名和普通文本数据。

修改服务器代码

给每个要发送的消息末尾加上换行符\n,作为消息结束的标记:

import socket
def send():
    host = 'localhost'
    port = 9999
    address = (host, port)
    sock = socket.socket()
    sock.bind(address)
    sock.listen(5)
    print('listining for connection...')
    con,addr = sock.accept()
    print('Got connection from',addr)
    file1name = 'file1.txt'
    # 给文件名加换行符后发送
    con.send((file1name + '\n').encode('utf-8'))
    file1data = 'this is file 1'
    con.send((file1data + '\n').encode('utf-8'))
    file2name = 'file2.txt'
    con.send((file2name + '\n').encode('utf-8'))
    file2data = 'this is file 2'
    con.send((file2data + '\n').encode('utf-8'))
    con.close()
    sock.shutdown(1)
    sock.close()
    print('connection closed!')
send()

修改客户端代码

makefile()把Socket包装成文件对象,按行读取(自动识别换行符作为边界):

import socket
host = 'localhost'
port = 9999
address = (host, port)
sock = socket.socket()
sock.connect(address)
print('connected to', address)

# 用文件对象按行读取,自动处理换行分隔符
with sock.makefile('r', encoding='utf-8') as f:
    while True:
        filename = f.readline().strip()  # strip去掉换行符
        if not filename:
            break
        print('filename -', filename)
        data = f.readline().strip()
        print('data -', data)

sock.shutdown(1)
sock.close()

方案二:先发送数据长度,再发送内容

这种方法更通用可靠,适合任何类型的数据(包括含换行符或其他特殊字符的内容),是工业级应用常用的方式。原理是先告诉对方要接收的数据长度,再发送实际内容,让客户端可以精准读取对应长度的数据。

修改服务器代码

需要导入struct模块来打包数据长度:

import socket
import struct

def send():
    host = 'localhost'
    port = 9999
    address = (host, port)
    sock = socket.socket()
    sock.bind(address)
    sock.listen(5)
    print('listining for connection...')
    con,addr = sock.accept()
    print('Got connection from',addr)

    # 封装一个发送消息的函数:先发长度,再发内容
    def send_msg(msg):
        msg_bytes = msg.encode('utf-8')
        # 把消息长度打包成4字节的大端整数(!I表示网络字节序的无符号int)
        length_packed = struct.pack('!I', len(msg_bytes))
        con.send(length_packed)
        con.send(msg_bytes)

    file1name = 'file1.txt'
    send_msg(file1name)
    file1data = 'this is file 1'
    send_msg(file1data)
    file2name = 'file2.txt'
    send_msg(file2name)
    file2data = 'this is file 2'
    send_msg(file2data)

    con.close()
    sock.shutdown(1)
    sock.close()
    print('connection closed!')
send()

修改客户端代码

同样需要导入struct模块,先读取长度,再读取对应长度的内容:

import socket
import struct

host = 'localhost'
port = 9999
address = (host, port)
sock = socket.socket()
sock.connect(address)
print('connected to', address)

# 封装一个接收消息的函数:先读长度,再读对应内容
def recv_msg():
    # 先接收4字节的长度数据
    length_bytes = sock.recv(4)
    if not length_bytes:  # 连接关闭时返回空
        return None
    # 解包得到消息长度
    msg_length = struct.unpack('!I', length_bytes)[0]
    # 接收对应长度的消息内容
    return sock.recv(msg_length).decode('utf-8')

while True:
    filename = recv_msg()
    if not filename:
        break
    print('filename -', filename)
    data = recv_msg()
    print('data -', data)

sock.shutdown(1)
sock.close()

这两种方案都能解决你现在的串接问题,方案一简单易实现,方案二更健壮通用,你可以根据自己的实际场景选择~

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

火山引擎 最新活动