网络3️⃣TCP-流协议理解
1、TCP 和 UDP
TCP 是面向字节流的协议,UDP 是面向数据报的协议。
原因:发送方操作系统对 TCP 和 UDP 协议的处理机制不同。
- UDP:每个数据包(报文)对应一个完整的用户消息(照样发送)。
- TCP:把数据当作一连串无结构的字节流,以
MSS
为单位进行分段。- TCP 有一个缓冲区,数据太多时分段后再发送。
- 数据太少时,可以积累足够多的字节再发送。
1.1、UDP 面向数据报
当应用层消息通过 UDP 传输时
-
发送方:
- 操作系统不会对消息进行拆分,组装好 UDP 首部后就交给网络层处理。
- 发出的 UDP 报文中的数据部分 = 完整的用户消息( i.e. 每个 UDP 报文就是一个用户消息的边界)。
-
接收方:
-
接收到 UDP 报文后放入队列,每个队列元素都是一个 UDP 报文。
-
应用程序调用
recvfrom()
时从队列中取出数据,从内核中拷贝到用户缓冲区。 -
读一个 UDP 报文就能读取到完整的用户消息。
-
1.2、TCP 面向字节流
当应用层消息通过 TCP 传输时
- 发送方:
- 消息可能会被操作系统分段(i.e. 以
MMS
为单位,分成多个 TCP 段)。 - 完整的用户消息 = 一个或多个 TCP 段的数据部分(因此,TCP 是面向字节流)。
- 消息可能会被操作系统分段(i.e. 以
- 接收方:假如应用程序不知道消息长度,则无法得知消息边界,无法读取到有效的消息。
- 一条用户消息可能被分到不同的 TCP 段。
- 两条消息的部分数据也可能分到同一条 TCP 段(aka. 粘包)。
解释
2、TCP 粘包
粘包:两条消息的部分数据被分到同一条 TCP 段。
而接收方不知道消息边界,从而无法获得有效的用户消息。
解决方案:由应用层解决。
-
固定消息长度:固定每个用户消息的长度。
- 当接收方收到规定的字节数后,就认为是完整且有效的消息。
- 简单,但灵活性不高。
-
特殊字符作为边界:在两条用户消息之间插入特殊字符。
- 当接收方读取到特殊字符时,就认为已经读完一个完整且有效的消息。
- 注意:假如消息内容中出现了约定的特殊字符,需要进行转义,避免解析错误。
- HTTP 的首部和正文就是通过回车换行符(CRLF)进行划分。
-
自定义消息结构:自定义一个消息结构,由包头和数据组成。
-
包头:固定大小,且有一个字段说明数据长度。
-
当接收方接收到大小固定的包头时,解析包头内容以获取数据长度,之后读取相应长度的数据以获得完整的消息。
// 示例 struct { u_int32_t message_length; char message_data[]; } message;
-