xiaohanliang
Network
Network
  • hi
  • LOWER
    • 0. arp决定下一跳
    • 1. dns决定终点
    • 2. [WIP]dns是不是真的有这些层级
  • MIDDLE
    • 0. 如何理解tcp握手的设计
    • 1. 诡异的tcp拆包现象
    • 2. tcp是一种高效的协议吗
    • 3. 为什么说没有人可以裸用tcp
    • 4. 尝试理解tcp的设计
    • 5. 连接建立@tcp调优
    • 6. 连接断开@tcp调优
    • 7. [WIP]拥塞控制@tcp调优
    • 8. 不需要这些花里胡哨的东西
    • 9. 怎么又是socket又是tcp
  • UPPER
    • 0. 为什么大家都用http
    • 1. [WIP]为什么http也keep-alive
    • 2. 如何保证pipeline的顺序到达
    • 3. 如何保证http的安全性
    • 4. 只不过https基于tls连接
    • 5. 怎么理解get/post
    • 6. http2为什么更快
    • 7. [WIP]内置加速的http3
    • 8. 怎样制造出实时效果-ws
    • 9. kcp是如何榨干你的带宽的
  • DEVICES
    • [302] 跳转到Linux网络设备
  • KUBERNETES NETWORK
    • [302] 跳转到容器网络
Powered by GitBook
On this page
  • Introduction
  • 模糊的消息边界
  • Mongo协议结构
  • MySQL包结构
  • 最后我们还有第三种
  • 那到底什么叫粘包呢?

Was this helpful?

  1. MIDDLE

3. 为什么说没有人可以裸用tcp

Previous2. tcp是一种高效的协议吗Next4. 尝试理解tcp的设计

Last updated 4 years ago

Was this helpful?

Introduction

一个经典到暴毙的问题, 我至少花过20个小时的冤枉时间在上面, 那就是你看看TCP的包结构, 它并没有一条字段叫包长度💩 不错每个包都有包头, 但是包头根本没有一条字段叫SIZE, 他只有SEQ, 为什么? 你怎么设计的? 你给你的TCP包加了一万个字段却不加包长度?

模糊的消息边界

TCP消息本身没有消息长度概念, TCP搞的是信息流, 也就是希望你从流中一个字节一个字节的读出信息, 然后重新拼出你想要的结构体来. 流本身是没有边界的, 流没有开始与结束, 只有一大堆字节等你来拼.

在这种情况下, 如何将字节流的东西重新拼装出一条完整的消息就非常重要了. 这些是协议设计者应该要做的事情, 我们以MySQL协议以及Mongo协议作为例子, 他们分别能代表两种最经典的协议结构.

Mongo协议结构

故事开始于一个MongoDB链接, 你给服务器发送了一条查询消息, 服务器怎么从这个TCP连接中把这条消息完整无误的读出来?

IMAGE

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

MySQL包结构

MySQL与Mongo略有不同, 你看我们也有长度, 但这个长度是帧长度. 但一条MySQL消息里有好几个帧拼一起, 这种类型代表虽然不告诉你这条消息总长度, 但是我们有一些固定的规则, 告诉你应该读几帧, 比如MySQL中的Error包就只有一帧, 其他的包统统以EOF帧结尾.

最后我们还有第三种

我遇到的第三种读包是按照特殊字符作为结尾的, 比如在Mongo中解析协议的时候, 如何完整的parse出一个string? 所有的string都会以'\0'作为结尾. 提示你结束了

还有一类叫是按照另一种条件的, 比如Mongo协议中是如何从[]byte中读出一个BSON结构体的, 它会一直读, 直到读到的内容已经足够解析出一个bson结构体为止. 这种叫条件结束

那到底什么叫粘包呢?

到这里你应该也已经猜出来了, 如果协议设计者没告诉我们应该怎么从字节流中读出一条完整的消息, 那我们就没办法把合并在一起的包拆开, 因为你压根不知道到底什么才算一个完整的包, 什么时候包才算读完了. 说到底这是协议设计者的问题

IMAGE