Skip to content

TCP粘包拆包

image.png

发生粘包的原因

  • 要发送的数据小于 TCP 发送缓冲区的大小,TCP 将多次写入缓冲区的数据一次发送出去,将会发生粘包;
  • 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包;
  • 要发送的数据大于 TCP 发送缓冲区剩余空间大小,将会发生拆包;
  • 待发送数据大于 MSS(最大报文长度),TCP 在传输前将进行拆包。即 TCP 报文长度 - TCP 头部长度 > MSS。

解决粘包拆包问题

  • 定长消息:发送端将每个数据包封装为固定长度
  • 特殊分隔符:在数据尾部增加特殊字符进行分割
  • 消息头:将数据分为两部分,一部分是头部,一部分是内容体;其中头部结构大小固定,且有一个字段声明内容体的大小
  • 自定义协议:自定义消息格式。

TCP协议是一个面向字节流的协议,并不会提供解决粘包拆包问题。

解决粘包拆包问题都是应用层去做的。

HTTP/1.1

HTTP/1.1 是基于文本的协议,并且每个请求和响应都是独立的实体。HTTP/1.1 请求和响应的格式包括以下几个部分:

  1. 起始行(Start Line): 包含请求方法(对于请求)或状态码(对于响应)。
  2. 头部字段(Header Fields): 包括各种元数据,如 Content-Type 和 Content-Length。
  3. 空行(CRLF): \r\n\r\n,用于分隔头部和主体。
  4. 主体(Body): 可能携带的数据负载。

HTTP/1.1 中的粘包和拆包问题主要是通过以下方式解决的:

  • Content-Length:

    通过 Content-Length 头部字段来指定消息体的长度,接收端可以据此确定消息的结束位置。

  • Transfer-Encoding: chunked:

    当消息体的长度未知或较大时,可以使用 Transfer-Encoding: chunked 来指示消息体是以一系列长度已知的块来发送的,每个块都有自己的长度指示器和结束标记。

  • 起始行和头部的结束:

    起始行和头部以 \r\n\r\n 结束,这帮助接收方识别请求或响应的开始和结束。

HTTP/2

HTTP/2 是一种二进制协议,它引入了帧的概念来传输数据。HTTP/2 使用帧来封装消息的不同部分,并且这些帧总是具有明确的边界,因此不需要像 HTTP/1.1 那样依赖于消息的结束标志。

HTTP/2 中的帧类型包括但不限于:

  • HEADERS:

    用于承载请求或响应的头部信息。

  • DATA:

    用于传输请求或响应的主体数据。

  • SETTINGS:

    用于协商连接级别的参数。

  • PUSH_PROMISE:

    用于服务器推送。

  • GOAWAY:

    用于关闭连接。

HTTP/2 中的粘包和拆包问题主要是通过以下方式解决的:

  • 帧边界:

    每个帧都有一个明确的开始和结束标记,使得接收端能够容易地区分不同的帧。

  • 帧头部:

    每个帧的头部包含了必要的元数据,例如类型、长度和标识符,这些信息帮助接收方理解帧的内容和上下文。

  • 多路复用:

    HTTP/2 支持在同一连接上并行传输多个请求和响应,每个请求和响应通过不同的流来标识,流之间不会相互干扰。