Python TCP Server 接收单个 TCP 数据包拆分成多个数据包

Posted

技术标签:

【中文标题】Python TCP Server 接收单个 TCP 数据包拆分成多个数据包【英文标题】:Python TCP Server receives single TCP packet split into multiple packets 【发布时间】:2020-11-14 22:59:41 【问题描述】:

在我托管的基于 Python 的 TCP 服务器上,我无法确定某些数据包拆分问题的原因。

我的客户端连接到服务器后,它会向服务器发送一个字符串,然后是/n

一个数据包的 Wireshark 预览如下所示:

在这种情况下,你可以看到payload是“as the extra special British baby potato/n”

我的 TCP 服务器接收代码如下:

    def listenToClient(self, client, address):
        size = 1024
        while True:
            try:
                # Receive data from the client
                data = client.recv(size)
                print('CLIENT Data Received', client)
                print(datetime.now())
                print("Raw data received: ", data.hex())
                Data: as the extra
            except ConnectionAbortedError:
                print(datetime.now())
                print('CLIENT Disconnected:', client)
                client.close()
                return False

收到上述数据包时,打印如下:

CLIENT Data Received <socket.socket fd=428, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.0.16', 12345), raddr=('192.168.0.14', 47234)>
2020-11-14 22:48:20.087507
Raw data received:  617320746865206578747261
Data: as the extra

CLIENT Data Received <socket.socket fd=428, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.0.16', 12345), raddr=('192.168.0.14', 47234)>
2020-11-14 22:48:20.092495
Raw data received:  207370656369616c2062726974697368206261627920706f7461
Data:  special british baby pota

CLIENT Data Received <socket.socket fd=428, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.0.16', 12345), raddr=('192.168.0.14', 47234)>
Raw data received:  746f65730a0a
2020-11-14 22:48:20.117488
Data: toes

不知何故,这个单一的 TCP 数据包被分成了 3 个更小的数据包。

你知道这是怎么发生的吗?

【问题讨论】:

请在此处查看我的评论...***.com/q/64790051/2836621 【参考方案1】:

TCP 不是基于消息的协议,而是字节流。单个send 并不意味着所有内容都将在单个 TCP 数据包中发送。同样,不能指望单个recv 将匹配单个send

如果您需要消息语义,您需要将这些添加到字节流之上。通常,这是通过显式长度指示符、特殊分隔符或只有一条消息来完成的,即关闭连接将结束消息。然后你需要多次调用recv,直到你得到完整的消息。

【讨论】:

感谢您的及时答复。在这种情况下,我的“特殊分隔符”是我在每个句子末尾附加的\n。有没有办法让 recv 阻塞并继续连接接收到的流,直到它遇到这个\n?我不想关闭连接,因为我需要一个长寿命的 TCP 连接:) @OscarVanL: recv 在出现特定字符之前无法读取。您需要自己实现这一点。例如,请参阅 python socket readline without socket.makefile() 了解有关如何操作的一些想法。 谢谢,这看起来很完美。我会试试的:) 我通过调整这个答案***.com/a/57164667/6008271谢谢!【参考方案2】:

根据Steffen Ullrich 的解释,您可以如何构建代码以接收整个消息:

def listenToClient(self, client, address):
        size = 1024
        while True:
            try:
                # Receive data from the client
                data = ''
                while True:
                     r = client.recv(size)
                     if not r:
                         break
                     data += r
                print('CLIENT Data Received', client)
                print(datetime.now())
                print("Raw data received: ", data.hex())
                Data: as the extra
            except ConnectionAbortedError:
                print(datetime.now())
                print('CLIENT Disconnected:', client)
                client.close()
                return False

【讨论】:

无法保证字节流不会在 UTF-8 多字节字符的中间被拆分,因此在确定您有完整的消息之前不应该对其进行解码。此代码还假设您一直接收到套接字关闭,因此只能接收一条消息。 @MarkTolonen 你对我的代码中的解码部分是绝对正确的。另一方面,关于您评论的第二部分,问题涉及单个 TCP 数据包,因此单个消息,除非我弄错了 它提到发送消息后跟一个 \n,不一定在之后关闭套接字。它确实需要一个缓冲层,从流中填充缓冲区,直到看到换行符,然后返回缓冲区的一部分直到换行符并包括换行符。此后缓冲区中剩余的任何内容都将成为下一条消息的一部分。

以上是关于Python TCP Server 接收单个 TCP 数据包拆分成多个数据包的主要内容,如果未能解决你的问题,请参考以下文章

TCP Socket 不接收数据,写入/发送不超时?

Python tcp socket 传送文件

python的socke编程

学习TCP/IP

TCP Server 只接收一条消息

解决Linux 下server和client 通过TCP通讯:accept成功接收却报错的问题