Python中asyncio.Protocol创建的TCP服务器为什么不存在粘包问题?

如题,测试了下,发现不会出现粘包的问题,可是没看懂是什么原因,求大神指导 🎈
Python中asyncio.Protocol创建的TCP服务器为什么不存在粘包问题?

17 回复

不太可能,可能是你本地测的,没啥压力,没遇到而已, 网络不可能理解你的协议规则, 所以肯定是存在粘包的.


这个问题问得好,关键在于 asyncio.Protocol 的设计和 TCP 本身的特性。

首先,TCP 本身是流式协议,它只保证数据字节流的顺序,不维护消息边界。从这个角度看,“粘包”是TCP的固有特性,不是问题。所谓的“粘包”其实是应用层协议设计需要处理的事情。

asyncio.Protocol 不自动解决粘包,但它提供了清晰的事件驱动模型,让你能方便地实现自己的消息边界处理。核心方法是 data_received(data)

当TCP数据到达时,data_received 会被调用,但传入的 data 是一个 bytes 对象,其长度是随机的,可能包含多条应用层消息,也可能只包含半条。处理粘包的责任完全交给了你。

举个例子,假设你的应用层协议是简单的“长度前缀法”(比如前4个字节是消息体长度):

import asyncio
import struct

class MyProtocol(asyncio.Protocol):
    def __init__(self):
        super().__init__()
        self._buffer = b''  # 累积缓冲区
        self._msg_len = None  # 当前正在解析的消息长度

    def data_received(self, data):
        # 将新数据追加到缓冲区
        self._buffer += data
        # 循环处理缓冲区中完整的消息
        while self._buffer:
            # 如果还不知道消息长度,尝试读取长度头
            if self._msg_len is None:
                if len(self._buffer) < 4:
                    return  # 数据不够,等待下次接收
                # 读取4字节的长度头(网络字节序,大端)
                self._msg_len = struct.unpack('>I', self._buffer[:4])[0]
                self._buffer = self._buffer[4:]  # 移除已处理的头
            # 检查缓冲区是否有完整的消息体
            if len(self._buffer) < self._msg_len:
                return  # 数据不够,等待下次接收
            # 提取一条完整消息
            message = self._buffer[:self._msg_len]
            self._buffer = self._buffer[self._msg_len:]  # 移除已处理的消息
            self._msg_len = None  # 重置,准备解析下一条消息
            # 处理完整的应用层消息
            self.handle_message(message)

    def handle_message(self, message: bytes):
        # 在这里实现你的业务逻辑
        print(f"收到完整消息: {message.decode()}")
        # 例如,回复一个响应
        if self.transport:
            self.transport.write(b"OK\n")

# 创建服务器
async def main():
    loop = asyncio.get_running_loop()
    server = await loop.create_server(MyProtocol, '127.0.0.1', 8888)
    async with server:
        await server.serve_forever()

if __name__ == '__main__':
    asyncio.run(main())

总结:asyncio.Protocol 把TCP流式数据切块给你,但组装成应用层消息得自己写代码。

刚看了下源码,好像是它读写都有个缓冲区去处理信息

socket 本身会有一个读写缓存区, 像 java 的 netty 里, 代码里也会有读取和写入缓冲区,

你可以试一下多写点数据再去读, 应该就有粘包了, 不过没必要,因为理论上他是一定会有粘包的 ==

tcpdump 抓一波试试就知道了~

没到瓶颈,可能是单人测试环境,试下虚拟机跑脚本请求大量数据

每次看到有人提到粘包这个词,都挺烦的,5 楼帖子里民科说得好

也许开启了 NODELAY ?

因为你发的太慢了

在十几年前我搜索网络编程相关的文章,就被 CSDN 上的文章和帖子里的“粘包”这个民科概念误导过一阵子。

那个时候刚刚接触电脑,又没有读过网络编程相关的书,编程时既想要 UDP 的效果,又想要 TCP 的可靠,还想“简单”,同时明明知道先发送个数据长度或数据类型,亦或是使用分隔符就可以解决的问题,却不知道为什么就是不愿意这么做,然后就搜索相关解决办法看看别人怎么做的,然后就不幸看到“粘包”这个愚蠢的说法,还被误导了一阵子。

现在都 8102 年了,怎么现在还有人用这个民科概念,楼主该看看书更新更新脑子啦!

呃,楼里不少人该看看书更新更新脑子了。

tcp 没有所谓的“粘包”问题,本就是流式协议

哈啊哈

谢谢大家,现在总算弄明白了,tcp 是流式协议,需要自定义发|收包的协议才能正确通讯~

以前的人水平不行,不知道 TCP 是流式协议,以为在发送端 Write 什么,接收端的 Read 也会得到同样的数据,结果在发生合并或者分割的时候,就称之为“粘包”。所以“粘包”根本不是“问题”,问题在于不懂得在应用层做切分。

粘包半包也就是个说法 拿个词来黑没什么意思吧

回到顶部