TCP粘包拆包
发生粘包的原因
- 要发送的数据小于 TCP 发送缓冲区的大小,TCP 将多次写入缓冲区的数据一次发送出去,将会发生粘包;
- 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包;
- 要发送的数据大于 TCP 发送缓冲区剩余空间大小,将会发生拆包;
- 待发送数据大于 MSS(最大报文长度),TCP 在传输前将进行拆包。即 TCP 报文长度 - TCP 头部长度 > MSS。
解决粘包拆包问题
- 定长消息:发送端将每个数据包封装为固定长度
- 特殊分隔符:在数据尾部增加特殊字符进行分割
- 消息头:将数据分为两部分,一部分是头部,一部分是内容体;其中头部结构大小固定,且有一个字段声明内容体的大小
- 自定义协议:自定义消息格式。
TCP协议是一个面向字节流的协议,并不会提供解决粘包拆包问题。
解决粘包拆包问题都是应用层去做的。
HTTP/1.1
HTTP/1.1 是基于文本的协议,并且每个请求和响应都是独立的实体。HTTP/1.1 请求和响应的格式包括以下几个部分:
- 起始行(Start Line): 包含请求方法(对于请求)或状态码(对于响应)。
- 头部字段(Header Fields): 包括各种元数据,如 Content-Type 和 Content-Length。
- 空行(CRLF):
\r\n\r\n
,用于分隔头部和主体。 - 主体(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 支持在同一连接上并行传输多个请求和响应,每个请求和响应通过不同的流来标识,流之间不会相互干扰。