3. 为什么说没有人可以裸用tcp
Introduction
一个经典到暴毙的问题, 我至少花过20个小时的冤枉时间在上面, 那就是你看看TCP的包结构, 它并没有一条字段叫包长度💩 不错每个包都有包头, 但是包头根本没有一条字段叫SIZE, 他只有SEQ, 为什么? 你怎么设计的? 你给你的TCP包加了一万个字段却不加包长度?
模糊的消息边界
TCP消息本身没有消息长度概念, TCP搞的是信息流, 也就是希望你从流中一个字节一个字节的读出信息, 然后重新拼出你想要的结构体来. 流本身是没有边界的, 流没有开始与结束, 只有一大堆字节等你来拼.
在这种情况下, 如何将字节流的东西重新拼装出一条完整的消息就非常重要了. 这些是协议设计者应该要做的事情, 我们以MySQL协议以及Mongo协议作为例子, 他们分别能代表两种最经典的协议结构.
Mongo协议结构
故事开始于一个MongoDB链接, 你给服务器发送了一条查询消息, 服务器怎么从这个TCP连接中把这条消息完整无误的读出来?

上面这就是Mongo协议的包, 其他的你全都不用管, 你只用看蓝色的哪一行, 消息长度. 这就告诉你顺着字节流读350个字符就是本条消息的长度了. 这种算是第一类, 直接告诉你包长度的.
MySQL包结构

MySQL与Mongo略有不同, 你看我们也有长度, 但这个长度是帧长度. 但一条MySQL消息里有好几个帧拼一起, 这种类型代表虽然不告诉你这条消息总长度, 但是我们有一些固定的规则, 告诉你应该读几帧, 比如MySQL中的Error包就只有一帧, 其他的包统统以EOF帧结尾.
最后我们还有第三种
我遇到的第三种读包是按照特殊字符作为结尾的, 比如在Mongo中解析协议的时候, 如何完整的parse出一个string? 所有的string都会以'\0'作为结尾. 提示你结束了
还有一类叫是按照另一种条件的, 比如Mongo协议中是如何从[]byte中读出一个BSON结构体的, 它会一直读, 直到读到的内容已经足够解析出一个bson结构体为止. 这种叫条件结束
那到底什么叫粘包呢?
到这里你应该也已经猜出来了, 如果协议设计者没告诉我们应该怎么从字节流中读出一条完整的消息, 那我们就没办法把合并在一起的包拆开, 因为你压根不知道到底什么才算一个完整的包, 什么时候包才算读完了. 说到底这是协议设计者的问题
Last updated
Was this helpful?