任何网络协议,都必须要用包头里面设置写特殊字段来标识自己,传输越复杂,越稳定,越高性能的协议,包头越复杂。我们理解这些包头中每个字段的作用要站在它们解决什么问题的角度来理解。因为没人愿意让包头那么复杂。
本篇文章会有两方面:
1. 包头协议格式讲解
2.每个字段的作用,以及它们带出相关概念。
一、以太网帧(MAC)
以太网帧是数据在以太网网络上传输时的基本单位,它定义了数据在链路层(数据链路层,OSI模型的第2层)的封装格式。以太网帧包含发送方和接收方的MAC地址、数据本身以及一些控制信息,用于保证数据的完整性和正确传输。
1. 以太网帧的结构
典型的以太网帧结构如下:
+-------------------+--------------+----------------+-----------+-----------------+--------+
| 帧前导符(Preamble) | 帧起始符(SFD) | 目的MAC地址 | 源MAC地址 | 以太网类型/长度 | 数据 | CRC |
+-------------------+--------------+----------------+-----------+-----------------+--------+
| 7字节 | 1字节 | 6字节 | 6字节 | 2字节 | ... | 4字节 |
+-------------------+--------------+----------------+-----------+-----------------+--------+
1.1. 帧前导符(Preamble):
- 长度:7字节。
- 用于同步通信,确保接收方能够正确地检测到帧的开始。前导符是一个交替的比特序列
10101010
,用于在物理层提供信号同步。
1.2. 帧起始符(SFD, Start Frame Delimiter):
- 长度:1字节。
- 标志帧的开始,通常是
10101011
。它告知接收方,接下来的信息是帧的实际数据。
1.3. 目的MAC地址:
- 长度:6字节。
- 表示接收设备的MAC地址,用于确定以太网帧应该发送到哪个网络设备。
1.4. 源MAC地址:
- 长度:6字节。
- 表示发送设备的MAC地址。
1.5. 以太网类型/长度:
- 长度:2字节。
- 用于标识数据部分承载的上层协议类型(如IP、ARP等)。例如:
0x0800
表示帧中承载的是 IPv4 数据包。0x0806
表示帧中承载的是 ARP 协议数据。
- 在以太网的IEEE 802.3标准中,这个字段也可以表示数据的长度。
1.6. 数据(Payload):
- 长度:46到1500字节。
- 实际传输的数据。以太网帧中的数据部分可以包含来自更高层协议的数据包,比如IP数据包。如果数据部分小于46字节,以太网会使用填充(padding)将其补齐到46字节。
1,7. CRC(循环冗余校验,Cyclic Redundancy Check):
- 长度:4字节。
- 用于检测帧在传输过程中是否发生了错误。发送方计算出CRC值并将其附加到帧的末尾,接收方通过计算CRC值检查帧是否有数据错误。
2. 以太网帧的最小和最大尺寸
- 最小以太网帧大小:64字节(包括帧头、数据和CRC校验)。
- 如果数据部分少于46字节,必须进行填充,以确保整个帧的长度至少为64字节。
- 最大以太网帧大小:1518字节(不包括帧前导符和帧起始符),其中:
- 数据部分最大为1500字节。
- 加上帧头(14字节)和CRC(4字节),总共1518字节。
3. 以太网帧的作用
以太网帧是以太网通信的基本单位,数据通过它在网络设备(如路由器、交换机、电脑等)之间传输。帧封装了上层协议的数据,并通过MAC地址来确定源设备和目标设备。
在传输过程中:
- 每个以太网设备都会检查帧中的目的MAC地址,以确定是否接收该帧。
- 以太网帧中的CRC用于保证数据的完整性,如果接收到的帧有误,接收设备将丢弃该帧。
4. 扩展的帧格式
- 巨型帧(Jumbo Frame):标准以太网帧的最大数据长度是1500字节,但在某些高性能网络中(如局域网或数据中心),可以使用巨型帧,其数据部分可以达到9000字节以上。这种帧可以降低协议开销,提高大数据量传输时的效率。
5. 以太网帧与MTU的关系
- MTU(最大传输单元) 定义了网络层传输数据的最大尺寸,通常为1500字节。以太网帧的最大数据部分正好是1500字节,与MTU相匹配。
- 因此,MTU包括IP头、TCP/UDP头和数据,而不包括以太网帧头和CRC。
6. 总结
以太网帧是数据在以太网网络中传输的基本单位,包含了数据、源/目的MAC地址、控制信息以及校验信息。它在链路层传输,封装了来自上层(如网络层)的数据,确保数据能够在以太网上准确地传输和接收。
二、IP头
IP头(IP Header)是IP协议数据包的首部,位于每个IP数据包的前面,用于承载与路由、传输相关的信息。IP头包含了源IP地址、目的IP地址、数据包的长度、分片信息、TTL(生存时间)等控制信息,确保数据包能在网络中正确传输和到达目标。
1. IP头的作用
IP头的主要作用是为数据包提供必要的路由信息,帮助数据包从源设备传输到目的设备。IP头告诉网络设备(如路由器)如何处理、传输和检查数据包。
2. IPv4头的结构
IPv4头的标准长度为20字节(如果没有选项字段),它由多个字段组成,每个字段提供不同的信息。下图展示了一个典型的IPv4头结构:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|版本(4位) | 头长度(4位) | 区分服务(8位) | 总长度(16位) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 标识(16位) | 标志(3位) | 片偏移(13位) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 生存时间(TTL)(8位) | 协议(8位) | 头部校验和(16位) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 源IP地址(32位) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 目的IP地址(32位) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 选项(可选字段,长度可变) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3. IP头的各个字段解释
以下是每个字段的详细解释:
-
版本(Version,4位):
- 表示IP协议的版本。对于IPv4协议,该字段的值为4。
-
头部长度(IHL,Internet Header Length,4位):
- 表示IP头的长度,以4字节(32位)为单位。最小值为5(即5 * 4 = 20字节),表示没有可选字段时IP头的长度为20字节。
-
区分服务(DSCP/服务类型,8位):
- 用于指定数据包的服务优先级。通常用来支持QoS(服务质量),区分不同的数据流优先级。
-
总长度(Total Length,16位):
- 指整个IP数据包的总长度(包括IP头和数据),单位是字节。最大值为65535字节。
-
标识(Identification,16位):
- 用于唯一标识主机发送的每个IP数据包,主要用于数据包的分片和重组。每个IP包都有一个唯一的标识号。
-
标志(Flags,3位):
- 用于控制数据包的分片。3位的标志分别为:
- 第一位:保留位,必须为0。
- 第二位:DF位(Don't Fragment),为1时表示禁止分片。
- 第三位:MF位(More Fragments),为1时表示后面还有更多的分片。
- 用于控制数据包的分片。3位的标志分别为:
-
片偏移(Fragment Offset,13位):
- 用于标识当前数据包的片在原始数据包中的位置,分片后的各部分可以通过该字段正确重组。
-
生存时间(TTL,Time to Live,8位):
- 表示数据包在网络中的生命周期,以防止数据包在网络中无限循环。每经过一个路由器,TTL值就减1,TTL为0时数据包将被丢弃。
-
协议(Protocol,8位):
- 指定上层使用的协议类型。常见的协议值有:
1
:ICMP(Internet Control Message Protocol)。6
:TCP(Transmission Control Protocol)。17
:UDP(User Datagram Protocol)。
- 指定上层使用的协议类型。常见的协议值有:
-
头部校验和(Header Checksum,16位):
- 用于校验IP头部是否在传输过程中出现错误。每个路由器在转发数据包时都会重新计算该校验和。
-
源IP地址(Source IP Address,32位):
- 表示数据包的发送方IP地址。
-
目的IP地址(Destination IP Address,32位):
- 表示数据包的接收方IP地址。
-
选项(Options,可选字段,长度可变):
- IP头部的可选字段,主要用于一些特殊场景,如记录路由、时间戳等。大多数情况下不会使用这个字段。
4. IP分片
如果一个IP数据包的总长度大于链路层的MTU(最大传输单元,通常是1500字节),那么这个数据包会被分片。分片时,IP头的标识、标志和片偏移字段会帮助接收方将这些分片重组为原始数据包。
- 标识(Identification):所有的分片都使用相同的标识号。
- 标志(Flags):
- DF:禁止分片。
- MF:如果有后续分片,MF会被设置为1,表示“更多分片”。
- 片偏移(Fragment Offset):分片在原始数据包中的位置。
5. IPv4头部的常见大小
- 标准IPv4头部长度为20字节(没有可选字段)。
- 如果有可选字段,头部长度会超过20字节,但通常不会超过60字节(头长度字段最大值为15,表示60字节)。
6. IPv4与IPv6头的区别
IPv6头与IPv4头相比简化了很多,移除了分片、头部校验和等字段,从而提高了处理效率。IPv6头固定为40字节,主要字段有版本、流标识、跳限制(类似TTL)等,IPv6的地址长度也从32位扩展到了128位。
7. 总结
IP头是IP协议数据包的控制信息部分,包含了路由、分片、错误校验等重要信息。它确保数据包能正确传输到目标设备,并为上层协议(如TCP/UDP)提供支持。在大多数场景下,标准的IPv4头部大小为20字节,但在特定情况下可能包括选项字段,使头部变得更大。
三、TCP头
TCP 帧格式(TCP Segment Format)
TCP(传输控制协议)是面向连接、可靠的数据传输协议,其数据传输是通过 TCP 段(TCP Segment) 进行的。每个 TCP 段都包含了头部(Header)和数据(Payload)两部分。头部部分包含了控制信息,用于确保数据的可靠性、顺序等特性。
以下是 TCP 帧(即 TCP 段)的格式说明,包括各个字段的意义:
1. TCP Segment Structure
字段 | 长度 | 说明 |
---|---|---|
源端口(Source Port) | 16 bit | 发送端口,用于标识源端的应用进程。 |
目标端口(Destination Port) | 16 bit | 目标端口,用于标识目标端的应用进程。 |
序列号(Sequence Number) | 32 bit | 数据的序列号,表示该段中第一个字节的数据位置。 |
确认号(Acknowledgment Number) | 32 bit | 如果 ACK 标志位被设置,该字段表示期望接收的下一个字节的序列号。 |
数据偏移(Data Offset) | 4 bit | TCP 头部的长度(以 32-bit 字为单位),即从头部开始到数据部分的偏移。 |
保留(Reserved) | 3 bit | 保留位,应该为零,供未来使用。 |
控制位(Flags) | 9 bit | 包含 TCP 标志位:URG , ACK , PSH , RST , SYN , FIN 。 |
窗口大小(Window Size) | 16 bit | 接收窗口的大小,指示可以接收的字节数。 |
校验和(Checksum) | 16 bit | 校验和,用于检验 TCP 数据的完整性。 |
紧急指针(Urgent Pointer) | 16 bit | 如果 URG 标志位被设置,表示紧急数据的偏移量。 |
选项(Options) | 可选 | 可变长字段,用于一些附加控制信息,如最大段大小(MSS)、时间戳等。 |
填充(Padding) | 可选 | 如果选项字段的长度不是 32-bit 的整数倍,则需要填充。 |
数据(Data) | 可变 | 传输的数据部分,具体长度由 TCP 窗口大小等控制。 |
2. 各字段详细说明
1. 源端口与目标端口(Source Port & Destination Port)
- 每个端口是一个 16 位数字,表示源和目标主机的应用层协议(例如 HTTP 使用端口 80)。
2. 序列号(Sequence Number)
- 用于跟踪数据流的顺序。每个 TCP 段的序列号表示该段数据中第一个字节的编号。
- 第一个数据段的序列号由应用层控制。
3. 确认号(Acknowledgment Number)
- 如果 ACK 标志位被设置,确认号指示接收方期望接收的下一个字节的序列号。
- 这是一个重要的可靠性机制,表示接收方已成功接收到的数据。
4. 数据偏移(Data Offset)
- 也叫 TCP 头部长度,指示 TCP 头部的结束位置。数据偏移是以 32 位(4 字节)为单位的,因此它的最大值是 15(即 60 字节的 TCP 头部)。
5. 控制位(Flags)
控制位包括了多个标志位,每一位都具有特定的作用。常见的标志位有:
- URG (Urgent Pointer flag):紧急指针位。如果设置该标志,表示数据是紧急的,且
Urgent Pointer
字段会被使用。 - ACK (Acknowledgment flag):确认位。如果设置该标志,表示确认号字段有效,用于确认接收到的数据。
- PSH (Push flag):推送位。表示接收方应该尽快将数据交给应用层,而不是等缓冲区填满。
- RST (Reset flag):重置位。如果连接异常或需要重新启动连接时,发送
RST
包以重置连接。 - SYN (Synchronize flag):同步位。用于初始化连接,通常在三次握手中使用。
- FIN (Finish flag):结束位。表示数据传输完毕,连接关闭。
6. 窗口大小(Window Size)
- 表示接收方可以接受的数据量,单位为字节。它是流量控制的一部分,确保发送方不会发送超出接收方处理能力的数据。
7. 校验和(Checksum)
- 用于验证数据在传输过程中是否被损坏。它覆盖了 TCP 头部、数据以及伪头部(用于校验的额外数据)。伪头部包含了源 IP 地址、目标 IP 地址、协议号等信息。
8. 紧急指针(Urgent Pointer)
- 如果
URG
标志位被设置,表示数据中有紧急数据。Urgent Pointer
指示紧急数据的最后一个字节的偏移量。
9. 选项(Options)
- 用于扩展功能,常见的选项包括:
- 最大段大小(MSS,Maximum Segment Size):指定接收方能接收的最大数据段大小。
- 时间戳(Timestamp):用于计算 Round Trip Time (RTT)。
- 窗口扩大因子(Window Scale):扩大接收窗口的大小,允许更大的窗口来提高吞吐量。
10. 填充(Padding)
- TCP 头部的总长度必须是 32 位(4 字节)的倍数,因此如果选项字段的长度不是 32 位的整数倍,会使用填充位使其对齐。
11. 数据(Data)
- 这是 TCP 段中传输的实际数据,长度可变,取决于应用程序的需求和 TCP 头部的长度。
3. TCP Segment 实际应用示例
在实际的网络传输中,TCP 段通常由协议栈自动处理,开发者通常不需要手动构造和解析 TCP 段。然而,了解它的结构对理解 TCP 的可靠性、流量控制、拥塞控制等机制非常重要。
例如,在一个三次握手的过程:
- 客户端发送带有 SYN 标志的 TCP 段,启动连接。
- 服务器响应带有 SYN 和 ACK 标志的 TCP 段,确认客户端请求。
- 客户端发送带有 ACK 标志的段,确认收到服务器的响应。
连接建立后,数据通过 TCP 段进行传输,每个数据段都会包含序列号和确认号,以确保数据的可靠传输。
4. 总结
TCP 帧(TCP Segment)由头部和数据两部分组成,头部包含了多个字段,用于控制连接的建立、维持、关闭,以及数据的可靠传输。理解这些字段对于深入掌握 TCP 协议非常重要,特别是在进行网络编程、故障排查或优化时。
四、UDP头
UDP(用户数据报协议,User Datagram Protocol)是一种无连接、不可靠的数据传输协议。与 TCP 相比,UDP 协议没有建立连接的过程,因此它的数据传输过程较为简单,但也没有像 TCP 那样提供可靠性、顺序保障和流量控制。
UDP 的头部结构相对简单,它仅包含 8 个字节(64 位)的固定字段,没有像 TCP 那样的复杂选项字段和控制位。
UDP 头部格式(UDP Header Format)
UDP 头部包含以下 4 个字段:
字段 | 长度 | 描述 |
---|---|---|
源端口(Source Port) | 16 bit | 发送端口,用于标识数据来源应用程序(可选)。 |
目标端口(Destination Port) | 16 bit | 目标端口,用于标识数据目标应用程序。 |
长度(Length) | 16 bit | UDP 数据报的总长度,包括头部和数据部分的总长度(单位为字节)。 |
校验和(Checksum) | 16 bit | 校验和,用于检验 UDP 数据报的完整性(可选)。 |
1. 源端口(Source Port) (16 bit)
-
描述:源端口号用于标识发送端应用程序的端口。每个 UDP 数据报都可以携带一个源端口号,用来表示数据来自哪个应用程序。这个字段是可选的,可以为 0,表示不使用源端口。
-
范围:0 到 65535。源端口通常由操作系统动态分配(对于客户端应用),或者由应用程序自己指定(例如,服务器通常使用固定的端口,如 HTTP 的 80 端口)。
2. 目标端口(Destination Port) (16 bit)
-
描述:目标端口号用于标识目标主机上的应用程序。接收方通过该端口号来确定将数据传递给哪个应用程序。
-
范围:0 到 65535。目标端口由应用程序或操作系统确定,例如,Web 服务器使用端口 80,DNS 服务器使用端口 53。
3. 长度(Length) (16 bit)
-
描述:表示整个 UDP 数据报的长度,包括 UDP 头部和数据部分的总长度。这个字段的值最小为 8,因为 UDP 头部本身占用 8 个字节,数据部分可以是任意长度。
-
单位:字节。
-
例子:如果 UDP 数据报的总长度为 50 字节,则该字段的值为 50。
4. 校验和(Checksum) (16 bit)
-
描述:用于验证数据在传输过程中是否被篡改或损坏。它覆盖了 UDP 头部、数据部分以及一个伪头部(包含源地址和目标地址等信息),因此提供了一种简单的完整性检查。
-
伪头部:计算校验和时,UDP 头部会与一个伪头部一起参与计算。伪头部包含以下信息:
- 源 IP 地址
- 目标 IP 地址
- 协议字段(在 UDP 中为 17)
- UDP 长度字段(UDP 数据报总长度)
-
可选性:校验和在 IPv4 中是可选的,尽管它通常会被启用;但在 IPv6 中,校验和是强制要求的。
- 如果校验和字段的值为
0
,表示没有启用校验和(适用于某些场景,如局域网内部的传输)。
- 如果校验和字段的值为
-
计算方式:校验和采用一种简单的 16 位反码求和算法,可以有效检测传输过程中数据的错误。
UDP 头部格式图示
0 7 8 15 16 23 24 31
+--------+--------+--------+--------+
| Source | Dest. | Length | Check- |
| Port | Port | | sum |
+--------+--------+--------+--------+
UDP 数据部分
-
数据部分(Data):这是 UDP 数据报的有效负载,即实际要传输的数据。数据部分的长度由
Length
字段指定。不同的应用会将不同格式的数据放入数据部分。 -
最大传输单元(MTU)限制:UDP 的数据部分通常受限于底层协议的最大传输单元(MTU)。例如,IP 层的最大传输单元通常为 1500 字节,减去 IP 和 UDP 头部后,UDP 数据的最大长度一般为 1472 字节(通常会有更多的限制,具体取决于网络层的设置)。
UDP 头部总结
-
简单性:UDP 的头部结构非常简单,仅有 8 字节(64 位),其中的字段非常直观:源端口、目标端口、长度和校验和。由于没有复杂的控制字段,UDP 的处理速度通常比 TCP 更快,延迟较低。
-
无连接性:UDP 是无连接的协议,发送方和接收方不需要建立连接,每个 UDP 数据报都独立发送,且没有任何可靠性保证。它也没有流量控制、拥塞控制和顺序控制,因此 UDP 是一种不可靠的协议。
-
应用场景:由于其低延迟和简单性,UDP 适用于实时应用(如视频流、VoIP、在线游戏等),这些应用通常对传输延迟要求较高,而不太关心偶尔丢失的数据包。
UDP 头部与 TCP 头部的对比
-
长度:UDP 头部只有 8 字节,而 TCP 头部至少 20 字节(没有选项的情况下),这是因为 TCP 需要更多的控制信息。
-
可靠性:TCP 提供了流量控制、重传机制和连接管理等功能,而 UDP 不保证可靠性,只是简单地将数据传递出去。
-
连接管理:TCP 是面向连接的协议,需要进行三次握手和四次挥手,而 UDP 是无连接的,每个数据报都是独立的。
示例:UDP 数据报结构
假设我们有一个 UDP 数据报,源端口为 12345,目标端口为 80,总长度为 32 字节,校验和为 0x1a2b:
字段 | 值 |
---|---|
源端口(Source Port) | 12345 |
目标端口(Destination Port) | 80 |
长度(Length) | 32 |
校验和(Checksum) | 0x1a2b |
数据(Data) | 24 字节的数据(例如,"Hello, UDP!") |
在这个例子中,UDP 数据报的头部总长度为 8 字节,数据部分的长度是 24 字节,总长度为 32 字节。
总结
- UDP 头部非常简洁,包含源端口、目标端口、长度和校验和字段。
- 它没有复杂的控制信息,适用于对性能要求较高且能够容忍数据丢失的场景(如视频流、DNS 查询等)。
- 校验和为可选项,但在 IPv6 中是强制要求的。
- UDP 的最大数据部分大小通常受限于 IP 层的最大传输单元(MTU),如果数据超出限制,需要分段传输。
五、MTU/MSS/wind_size
1. MTU(最大传输单元, Maximum Transmission Unit)
MTU 是网络中能够传输的最大数据包大小,单位是字节。它是链路层(如以太网)所能处理的最大数据包的大小,包括数据和协议头(如 IP 头部、TCP 或 UDP 头部等)。如果传输的数据包大于 MTU,数据包会被分段。
- 以太网的标准 MTU 为 1500 字节,但不同的网络接口可能支持不同的 MTU 大小。
2. MSS(最大报文段大小, Maximum Segment Size)
MSS 是 TCP 层的概念,它表示 TCP 数据段的最大数据部分的大小,不包括 TCP 头部。MSS 通常由发送端在连接建立时通过 TCP 选项(MSS 选项)与接收端协商。
- MSS 通常由
MSS = MTU - IP 头部大小 - TCP 头部大小
计算得出。- 例如,假设以太网的 MTU 为 1500 字节,IP 头部大小为 20 字节,TCP 头部大小为 20 字节,那么 MSS 通常是
1500 - 20 - 20 = 1460
字节。
- 例如,假设以太网的 MTU 为 1500 字节,IP 头部大小为 20 字节,TCP 头部大小为 20 字节,那么 MSS 通常是
3. 窗口大小(Window Size)
窗口大小是 TCP 流量控制的一部分,用于指示接收方能够接收的最大数据量。它通常在 TCP 头部的 窗口大小 字段中表示,单位为字节。窗口大小的作用是确保接收方不会被大量数据淹没,导致缓冲区溢出。
- 窗口大小通常与网络的带宽和延迟有关,且在流量控制中起到重要作用。
4. MTU 和 MSS 的关系
- MTU > IP 头部 + TCP 头部 + MSS:
- 如果 MTU 大于 IP 头部和 TCP 头部的大小,那么剩余的空间可以容纳更大的 MSS。这种情况下,可以传输较大的 TCP 数据段。
- MTU < IP 头部 + TCP 头部 + MSS:
- 如果 MTU 小于 IP 头部和 TCP 头部的大小之和,那么将无法传输一个完整的 TCP 数据段,可能导致分段。在这种情况下,TCP 或 IP 层会将数据报文分割成多个小的数据包进行传输,称为 IP 分片(IP Fragmentation)。
- 这会导致性能问题,因为分段后的数据包会增加额外的开销,并且如果其中的一个分段丢失,整个数据包都会丢失。
5. MTU 与 UDP/TCP 的关系
-
UDP 与 MTU 的关系:
- UDP 不提供数据重组功能,如果一个 UDP 数据包的大小超过 MTU,IP 层会将其分片。如果 UDP 数据包被分片,其中一个分片丢失,将会导致整个数据包的丢失,因此 UDP 传输通常会希望数据包大小小于或等于 MTU。
- 通常建议 UDP 数据报文的大小不超过 MSS,以避免分片。UDP 的最大数据报文大小通常是 1472 字节(假设以太网的 MTU 是 1500 字节)。
-
TCP 与 MTU 的关系:
- TCP 在传输时会考虑 MTU 和 MSS,以确保传输的数据段不会超过 MTU 的大小。TCP 会尽量避免 IP 分片,通过选择合适的 MSS 来保证数据段的大小适合传输。
- 如果发送方的 MTU 和接收方的 MTU 不同,TCP 会使用适当的 MSS 来避免 IP 分片。TCP 连接的最大数据段由发送方和接收方在连接建立时协商。
6. 发送端的 MTU 大于接收端的 MTU 会怎么样?
- 发送端 MTU 大于接收端 MTU:
- 如果发送端的 MTU 大于接收端的 MTU,通常会发生 分片(Fragmentation)。发送端会根据接收端 MTU 大小对数据进行分片。接收端需要重新组装这些分片。
- 如果数据包没有被适当分片,或者某个分片丢失,可能会导致接收端无法正确接收数据。
7. 发送端的 MTU 小于接收端的 MTU 会怎么样?
- 发送端 MTU 小于接收端 MTU:
- 这种情况通常不会有太大问题。发送端会使用它自己的 MTU 限制来发送数据,而接收端会根据自己的 MTU 设置来接收数据。
- 这不会导致分片问题,但发送端可能没有利用完整的 MTU 带宽,无法有效地传输最大的数据量。
8. UDP 的最大传输长度是多少,怎么设置?
UDP的报文长度:报头长度+载荷长度。报文长度的单位字节。 假设,报文的长度是1024,那么整个UDP数据报的长度就是1024,由于是使用2个字节表示该长度大小,那么最大值为65535,约等于64KB。(UDP是无法扩容的,因为这是协议规定好的,即使服务器扩容了,但是无法使客户端进行扩容,即使客户端都对这个服务器扩容了的,那么客户端使用其他的服务器就无法使用了,因为其他服务器都是未扩容的。)
上面的是基于UDP包头协议算出,实际发送到IP层时,要受IP层最大分片数来决定,算出来刚好也是64K(具体见下面)
9. 巨帧(Jumbo Frames)
-
定义:巨帧(Jumbo Frame)是指大于标准 MTU 的数据帧,通常是 9000 字节 或更大的数据包。
-
使用场景:
- 巨帧主要用于数据中心、存储网络(如 iSCSI)、高性能计算等场景,以提高数据传输效率。通过增加单个数据包的大小,减少了每个数据包的开销,提高了带宽利用率。
-
优势:
- 巨帧减少了每个包的头部开销,可以更高效地传输大数据量,减少了 CPU 的中断处理次数,提高了传输效率。
-
设置:
- 巨帧的使用需要网络设备支持,并且需要在发送端和接收端的网卡、交换机上都进行配置。如果设备不支持巨帧,使用巨帧可能会导致通信失败。
-
如何设置:
-
在网络设备(如网卡)和操作系统中,可以设置最大传输单元(MTU)为更大的值(如 9000 字节或更大),以启用巨帧。
-
例如,使用
ifconfig
或ip
命令设置 Linux 系统的 MTU:sudo ifconfig eth0 mtu 9000
或者
sudo ip link set dev eth0 mtu 9000
-
10.网卡的MTU怎么设置
在Linux系统和Windows系统中,MTU(最大传输单元)的设置方式有所不同。下面介绍如何在这两种系统中设置网卡的MTU。
10.1 Linux系统中设置MTU
在Linux系统中,可以使用命令行工具来设置网卡的MTU值。
1. 临时设置MTU
使用以下命令可以临时更改网卡的MTU值(系统重启后恢复默认):
sudo ip link set dev 网卡名 mtu 值
例如,将网卡 eth0
的MTU设置为 1400
:
sudo ip link set dev eth0 mtu 1400
2. 查看当前MTU
可以使用以下命令查看某个网卡的当前MTU值:
ip link show dev 网卡名
例如,查看 eth0
的MTU:
ip link show dev eth0
输出示例:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 00:11:22:33:44:55 brd ff:ff:ff:ff:ff:ff
在这里可以看到 mtu 1500
表示当前MTU值是1500字节。
3. 永久设置MTU
临时设置在系统重启后会丢失,想要永久修改MTU,可以修改网络配置文件。具体文件路径和格式根据Linux发行版有所不同。
- Debian/Ubuntu等基于
/etc/network/interfaces
的系统:
编辑 /etc/network/interfaces
文件,为对应的网卡添加 mtu
参数。例如:
auto eth0
iface eth0 inet static
address 192.168.1.100
netmask 255.255.255.0
gateway 192.168.1.1
mtu 1400
保存后,重启网络服务或系统:
sudo systemctl restart networking
- Red Hat/CentOS等基于
NetworkManager
的系统:
编辑对应网卡的配置文件,一般位于 /etc/sysconfig/network-scripts/ifcfg-网卡名
。添加或修改 MTU
选项。例如:
DEVICE=eth0
BOOTPROTO=static
ONBOOT=yes
IPADDR=192.168.1.100
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
MTU=1400
保存后,重启网络服务:
sudo systemctl restart NetworkManager
10.2 Windows系统中设置MTU
在Windows系统中,可以通过命令行或图形界面工具来设置MTU。
1. 使用命令行设置MTU
在Windows中,可以使用 netsh
命令行工具来设置网卡的MTU值。
- 查看当前网卡的MTU:
首先,打开命令提示符(以管理员身份运行),然后输入以下命令查看当前MTU:
netsh interface ipv4 show subinterfaces
输出示例:
MTU MediaSenseState Bytes In Bytes Out Interface
------ --------------- -------- --------- -------------
1500 1 0 0 Ethernet
- 设置MTU值:
使用以下命令可以设置某个网卡的MTU值:
netsh interface ipv4 set subinterface "网卡名称" mtu=值 store=persistent
例如,将网卡 Ethernet
的MTU值设置为 1400
:
netsh interface ipv4 set subinterface "Ethernet" mtu=1400 store=persistent
这里的 store=persistent
表示该设置会在系统重启后依然生效。
2. 使用图形界面设置MTU
- 打开“网络和共享中心”。
- 点击左侧的“更改适配器设置”。
- 找到需要更改的网卡,右键选择“属性”。
- 在“网络”选项卡中,点击“配置”。
- 切换到“高级”选项卡,找到
MTU
或Jumbo Frame
相关选项。 - 根据需要设置MTU值,点击“确定”保存。
10.3 注意事项
-
适配MTU值:MTU值应根据网络环境合理设置。通常,以太网的默认MTU为1500字节,但在某些情况下(如VPN或PPPoE连接)可能需要更小的MTU值。可以通过测试找到最佳MTU。
-
MTU对性能的影响:较大的MTU值可以减少分片,提高网络传输效率,但如果网络中存在不支持较大MTU的设备,可能会导致数据包无法正确传输,甚至造成丢包。
-
分片问题:如果数据包大于MTU值,网络设备可能需要将其分片。分片会增加网络负担,因此找到适当的MTU值是很重要的。
10.4 总结
- MTU:链路层的数据包最大大小,通常包括协议头。MTU“活在”IP层,收到上层包小于自己则直接发,大于自己则分片发。
- MSS:TCP 层的数据段最大大小,不包括头部。用来切割send接口的数据包。
- 窗口大小:TCP 流量控制的关键字段,指示接收方能够接收的最大数据量。
- UDP/TCP 与 MTU:发送的 UDP/TCP 数据包必须小于 MTU,以避免分片。对于TCP来讲只要MSS+TCP头长度+IP头(长度)<= MTU ,tcp 调用send接口就不用担心分片的问题。对于UDP来讲就要用户自己来控制了。所以,TCP有MSS控制一般用不上IP层的分片,UDP则会遇到IP层的分片。
- 巨帧:允许更大的数据包(通常 9000 字节或更大),在数据中心和高效网络传输中常用。
- 尽量避免IP层分片:丢失没有重传机制,收到的分片包都会丢了。TCP包被IP分片了会造成大量的重传,因为IP层会把所有分片包丢掉,这些都需要让TCP控制重传,但是TCP丢掉一包,只需要重传对应的SYN-num的一包就行。
六、send与MSS sendto与MTU
1.TCP的send与MSS的关系:
-
send的字节数小于MSS会怎么样? 如果你调用
send()
发送的字节数小于 MSS,内核不会等到达到 MSS 后才发送数据。TCP 只会发送你请求的字节数(即 30 个字节)。它不会一直等待填满整个 MSS,会把数据缓存在发送队列中,等待几十ms等待后续的数据来尽量填满 MSS。(参见下面的tcp延迟功能介绍)
2. UDP的sendto/recvfrom与MTU的关系
-
sendto的字节不够MTU会怎么样? UDP是一个无连接的协议,因此发送的字节数不需要严格匹配MTU。如果字节数小于MTU,它就会发送这些数据,不会等待。
-
等待吗,等待过久,这个等待的值怎么设置? 如果UDP发送缓冲区满了,
sendto()
会阻塞,直到缓冲区有足够空间。这可以通过setsockopt()
设置SO_RCVBUF
和SO_SNDBUF
来控制UDP缓冲区大小,避免阻塞。 -
sendto的值大于MTU会怎么样? 如果发送的数据大于MTU,UDP会因为网络协议的限制进行分片。然而,大多数网络接口和协议栈会在发送之前进行检查并拒绝发送过大的数据包。你应该确保发送的数据大小在MTU范围内,或者手动进行数据分段。
3. IP头分片与缓存区大小限制
-
IP头分片的数量和缓存区大小限制: 尽管理论上分片数量是无限的,但网络设备的缓冲区(如发送缓冲区、接收缓冲区)是有限制的。这个限制通常由操作系统、硬件设备或网络协议栈决定。你可以通过以下方式查看和设置:
-
查看缓冲区大小: 通过
sysctl
命令查看:sysctl -a | grep net.ipv4
特别关注
net.ipv4.tcp_rmem
和net.ipv4.tcp_wmem
,它们控制TCP接收和发送缓冲区的大小。 -
设置缓冲区大小: 使用
sysctl
命令设置:sysctl -w net.ipv4.tcp_rmem="4096 87380 6291456" sysctl -w net.ipv4.tcp_wmem="4096 87380 6291456"
-
代码中设置缓冲区大小: 通过
setsockopt()
函数:int size = 1048576; // 1MB setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); setsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
-
-
sendto接口超过缓存大小会怎么样? 如果调用
sendto()
时缓存区大小不足,sendto()
会阻塞,直到有足够的空间。如果缓存区无法容纳足够的数据,可能会返回ENOMEM
错误。 -
缓存区不够sendto的字节数会怎么样? 如果缓存区不足,
sendto()
会阻塞,直到有足够空间。如果缓存区没有足够空间并且阻塞超过预定时间(由SO_RCVBUF
等参数设置),会返回错误。
4. MSS的值与TCP头部
-
MSS值是否需要在TCP头部携带? MSS(最大报文段大小)是通过TCP握手过程协商的,它不会直接出现在TCP数据包的头部,而是通过
SYN
标志和MSS
选项进行协商。每个TCP连接在建立时会交换其MSS值,告知双方最大可以接收的TCP段大小。 -
不协商会怎么样? 如果没有协商MSS(通常很少见),默认的MSS值为536字节(如果没有MTU限制)。但在大多数网络中,会根据MTU自动进行调整。
-
比对方的MSS大或小会怎么样?
- 如果发送端的MSS大于接收端的MSS,接收端可能会丢弃过大的数据段,并且发送端会在后续的包传输中按接收端的MSS进行调整。
- 如果接收端的MSS大于发送端的MSS,发送端会使用较小的MSS进行发送,确保数据段大小不会超过接收端的最大可接受值。
七、IP层的拼包机制
IP 层在 数据包分片 之后,接收到分片的顺序与发送顺序不同(例如顺序是 1,2,3,但接收到的是 2,3,1)。IP层本身不会直接处理数据的乱序问题,只负责 分片和重组 数据包。但幸运的是,IP 层的 分片重组机制 可以确保即使数据包顺序混乱,最终也能正确拼接和处理数据。
IP层分片与重组机制:
IP 层的 分片与重组 主要依赖于以下几点:
-
分片标识符: 每个 IP 数据包都有一个 标识符(Identification) 字段。所有的分片都共享同一个标识符,因此接收端能够识别它们属于同一个原始数据包。当接收端的 IP 层收到多个分片时,它通过这个标识符将它们拼接起来。
-
分片标志和偏移量: 每个分片除了标识符外,还有 分片标志(Flags) 和 片段偏移(Fragment Offset) 字段。这些字段指示了当前分片的位置以及它在整个数据包中的顺序。
- 分片标志:告诉接收端这个分片后面是否还有更多的分片。
- 片段偏移:表示该分片的数据在原始数据包中的偏移量。
-
乱序和重组:
- 如果数据包的分片乱序到达,接收端 IP 层会使用 片段偏移 信息将分片正确重组。例如:
- 第一个分片的片段偏移是 0,表示它是数据包的开始部分。
- 第二个分片的片段偏移可能是 1480(假设MTU为1500,IP头部和TCP头部的大小为 20 字节,每个分片的有效数据为 1480 字节)。
- 第三个分片的片段偏移可能是 2960,以此类推。
- 即使这些分片的接收顺序是乱的(例如 2,3,1),IP层通过这些偏移量信息将它们按正确顺序重组。
因此,即使接收到的顺序是 2,3,1,接收端仍然可以根据每个分片的偏移量,将它们拼接成原始数据包。
- 如果数据包的分片乱序到达,接收端 IP 层会使用 片段偏移 信息将分片正确重组。例如:
-
重组完成后交给上层: 一旦所有分片都接收完毕,且没有丢失的分片,IP层会将重组后的数据包交给上层协议(如 TCP 或 UDP)进行处理。如果有分片丢失,接收端会丢弃该数据包并通知发送端(通过 ICMP 或 TCP 的重传机制)。
关于校验和:
IP 数据包的 校验和 是 对每个分片 计算的,而不是对整个重组后的数据包。每个分片都有自己的校验和来确保数据的完整性。如果某个分片的校验和失败,则该分片会被丢弃,接收端无法成功重组该数据包。
- 校验和仅确保 单个分片的数据完整性,而 IP层不关心数据包是否已经被重组,它只关注各个分片是否有错误。
- 只有所有分片都正确接收且重组成功后,才能传递给上层协议,应用层才会看到完整的、无错误的数据。
IP层不能识别乱序问题吗?
IP层不关心数据的顺序,它只负责分片和重组。乱序是上层协议(如 TCP 或应用层)关心的问题。IP层依赖于片段偏移和标识符来确保即使分片乱序,它仍能正确重组数据。乱序的处理和确保数据完整性是由 更高层的协议(如 TCP)来处理的。
TCP如何解决数据乱序问题?
在 IP 层已经完成重组后,接下来的数据处理由 TCP 来完成。TCP 是一个面向连接的协议,它确保了数据的 顺序性 和 完整性:
- TCP 会通过 序列号 来追踪数据的顺序。接收到的数据包会根据序列号重排,如果某些数据包丢失,TCP 会请求重传。
- 如果收到的 TCP 数据包顺序错乱,TCP 会缓存它们,直到缺失的部分被重新传送过来。
IP分片的偏移量最大值:
在 IPv4 中,IP头的 片段偏移(Fragment Offset) 字段是 13位,单位是 8字节。所以,片段偏移的最大值是:
- 最大偏移量值:
2^13 - 1 = 8191
(这个值是偏移量字段的最大值) - 最大偏移量对应的字节数:
8191 * 8 = 65528
字节(即最大为 65,528 字节)。
如何理解这个限制:
- 片段偏移单位:偏移量是以 8字节 为单位来计数的。因此,即使最大偏移量值是 8191,实际对应的字节数为
8191 * 8 = 65528
字节。 - 最大数据包大小:如果一个数据包的总大小超过 65528 字节,可能会因为 偏移量字段的限制 无法被正确分片。因此,分片后的最大数据包大小 也受到偏移量字段限制的影响。
实际影响:
- 在常见的 IPv4 中,如果一个数据包超过 65528 字节,IP层无法正确进行更多的分片,因为 片段偏移字段 的最大值为 8191(偏移量单位为 8字节)。
- 也就是说,IP协议的分片数量和总数据量是有限制的,分片的偏移量最大支持 65,528 字节的数据,超过这个字节数的数据将无法进一步分片。
- 最大偏移量 =
8191
(13位片段偏移字段最大值) - 最大分片支持的字节数 =
8191 * 8 = 65528 字节
(即约 64KB)。
拼包失败的情况
1. 丢弃不完整的数据包
IP层会等待所有的分片接收完毕,如果在一定时间内没有收到缺失的分片,IP 层会 丢弃这些不完整的数据包。这种机制是为了防止系统一直等待,造成资源浪费。
2. 超时后丢弃
通常,IP层会使用一个 超时机制,如果在一定时间内(比如几秒钟)没有收到所有必要的分片,接收端会 丢弃该数据包。这一过程依赖于 ICMP协议 发送方会收到一个 ICMP超时报文(Destination Unreachable - Fragmentation Needed),告知发送方缺少分片。发送方可以尝试重新发送丢失的分片。
3. ICMP不可达错误通知
当接收方的 IP 层无法在规定的时间内拼接出完整的数据包时,接收方会发送一个 ICMP不可达消息(Fragmentation Needed and DF set)。这个消息告诉发送方 必须分片 但由于 DF(Don't Fragment)标志设置,分片无法进行,因此数据包无法正确传输。
4. 为什么会发生拼包不成功?
拼包不成功的原因通常有以下几种:
- 分片丢失:网络传输中某些分片可能会丢失。
- 过长的延迟:由于网络延迟过长,接收方无法及时接收到所有的分片,导致超时。
- DF标志设置:如果数据包的 DF(Don't Fragment)标志 被设置了,网络中的路由器将无法对数据包进行分片。如果这个数据包超过了路径中的某个路由器的 MTU,会导致无法进行分片,从而发生丢包。
- 分片乱序:虽然 IP 层会根据偏移量来拼接分片,但如果一些分片的延迟过长,接收端可能需要等待其它分片的到来才能完成拼接。
总结:
- IP层 负责分片和重组,它通过分片标识符、片段偏移量和分片标志来保证即使分片乱序,接收端也能正确拼接数据包。
- IP校验和 是针对每个分片计算的,不关心数据是否已经重组。每个分片的完整性由 IP 校验和确保。
- IP层不处理数据的顺序问题,这交给了 TCP协议 或其他更高层协议(如应用层),它们会保证数据的顺序性和完整性。
八、IP层缓冲区、TCP层缓冲区
在网络协议栈中,IP层 和 TCP层 都有各自的缓冲区用于数据传输和重组。具体到你的问题,以下是关于 IP层拼包和缓冲区,以及 TCP层缓冲区大小 的详细解释。
1. IP层的拼包与缓冲区:
-
IP层的拼包行为: IP层负责将数据拆分成更小的分片,以适应网络接口的MTU限制。分片是在 网络层(IP层) 进行的,当数据包大于网络接口的MTU时,IP层会将其分片。每个分片会带有自己的头部信息,并且在接收端重新组装成原始数据包。
- 缓冲区:IP层的缓冲区主要用于存储待处理的分片。操作系统在收到数据包时,会将数据存储在接收缓冲区中,等待分片组装完成。如果缓冲区被填满,可能会丢失数据包。
- 查看IP层的缓冲区大小:IP层的缓冲区大小一般由操作系统内核默认设置,通常可以通过
sysctl
或相关命令查看,但在大多数系统中,IP层的缓冲区管理是隐式的,具体的大小配置不容易直接修改。(根据IP层协议分片机制来计算:64K)
2. TCP层的缓冲区:
TCP协议也有自己的缓冲区来存储发送和接收的数据。这些缓冲区在控制数据流、确保可靠性和进行数据重排时起着关键作用。
-
TCP发送缓冲区与接收缓冲区:
- 发送缓冲区:在TCP发送数据时,数据首先被写入发送缓冲区,然后由协议栈处理并通过网络发送出去。发送缓冲区的大小会影响发送速率,过小的缓冲区可能会导致频繁阻塞。
- 接收缓冲区:接收到的数据首先存储在接收缓冲区中,然后交给应用程序处理。如果接收缓冲区满了,接收端可能会丢弃数据包或通知发送端暂停发送。
-
TCP缓冲区大小的查看与设置: 可以通过以下几种方法来查看和设置TCP缓冲区的大小:
查看TCP缓冲区大小:
-
使用
sysctl
命令查看TCP缓冲区设置:sysctl net.ipv4.tcp_rmem sysctl net.ipv4.tcp_wmem
net.ipv4.tcp_rmem
:查看TCP接收缓冲区的大小配置。输出格式:min default max
net.ipv4.tcp_wmem
:查看TCP发送缓冲区的大小配置。输出格式:min default max
-
查看单个套接字的缓冲区大小: 使用
ss
或netstat
命令可以查看当前套接字的缓冲区使用情况。ss -t
或者
netstat -an | grep :<port_number>
设置TCP缓冲区大小:
-
修改
sysctl
配置:通过sysctl
命令临时调整TCP缓冲区大小:sysctl -w net.ipv4.tcp_rmem="4096 87380 6291456" sysctl -w net.ipv4.tcp_wmem="4096 87380 6291456"
这里的三个数字分别代表:
- 最小值(初始缓冲区大小)
- 默认值(操作系统默认设置的缓冲区大小)
- 最大值(缓冲区的最大值)
-
修改
/etc/sysctl.conf
永久配置: 要使设置永久生效,可以将上述sysctl
设置添加到/etc/sysctl.conf
文件中:net.ipv4.tcp_rmem = 4096 87380 6291456 net.ipv4.tcp_wmem = 4096 87380 6291456
然后运行以下命令使配置生效:
sysctl -p
-
通过
setsockopt
修改套接字缓冲区大小:应用程序可以在创建套接字时,通过setsockopt()
设置套接字的发送和接收缓冲区大小。例如:int sndbuf = 1048576; // 1MB int rcvbuf = 1048576; // 1MB setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
-
3. SYN队列和TCP重排:
-
SYN队列(SYN_NUM): TCP协议在建立连接时,使用 SYN队列 来存储尚未完成三次握手的连接请求。在三次握手过程中,SYN包会进入一个等待队列,直到握手完成。操作系统为此会为每个监听的TCP端口设置一个最大队列长度。
-
查看与设置SYN队列的大小:
-
查看当前的TCP SYN队列大小:
sysctl net.ipv4.tcp_max_syn_backlog
-
设置TCP SYN队列大小:
sysctl -w net.ipv4.tcp_max_syn_backlog=1024
-
-
SYN队列溢出:如果SYN队列满了,新到的连接请求可能会被丢弃,从而导致拒绝服务。适当调整 SYN 队列的大小,可以避免这种情况。
-
总结:
- IP层的缓冲区:IP层的缓冲区主要用于存储数据包分片,而不是直接控制数据发送的延迟。
- TCP层的缓冲区:TCP层的缓冲区用于数据的传输和重排,可以通过
sysctl
或setsockopt()
来查看和调整缓冲区大小。缓冲区的大小对发送和接收性能有重要影响,过小的缓冲区会限制吞吐量,过大的缓冲区可能会浪费内存资源。 - SYN队列(TCP层):用于管理TCP连接的建立过程,控制连接请求的队列长度,也可以通过
sysctl
调整。
九、TCP的控制(慢启动、拥塞、滑动控制、超时、延迟)
TCP(传输控制协议)是面向连接的、可靠的传输协议,它通过一系列机制来确保数据的可靠传输。下面介绍TCP的几个关键特性:慢启动、拥塞控制、滑动窗口、超时重传和延迟确认。
1. 慢启动(Slow Start)
慢启动是TCP拥塞控制的一部分,目的是防止网络突然被大量数据包堵塞。在连接建立之初,发送方会先以较小的发送速率开始传输数据,逐步增加数据的发送速率,直到找到网络的最大承载能力。
工作原理:
- 每当TCP连接建立或在发生超时重传后,慢启动会初始化一个称为**拥塞窗口(Congestion Window,CWND)**的值,通常初始值为一个最大报文段(MSS,Maximum Segment Size)。
- 随着每个ACK的接收,CWND会指数级增长。即,每收到一个ACK,CWND就增大1个MSS的大小,从而允许发送更多的数据包。
- 当CWND增大到一个网络的拥塞点时,会触发拥塞避免机制以避免网络拥堵。
示例:
假设CWND初始值为1个MSS,发送方发送一个数据包并收到ACK,CWND增加到2,发送方再发送两个数据包并收到两个ACK,CWND再增加到4,以此类推。
2. 拥塞控制(Congestion Control)
拥塞控制是TCP协议中用于防止网络过载的一组机制。拥塞控制的目标是确保发送方不会以过快的速率发送数据,从而导致网络拥塞。
拥塞控制的主要机制包括:
- 慢启动(Slow Start):如上所述,初始阶段发送速率缓慢增加。
- 拥塞避免(Congestion Avoidance):当CWND达到某个阈值(通常称为慢启动阈值,ssthresh)时,CWND不再指数增长,而是线性增长,以较慢的速度继续增大,防止网络拥塞。
- 快速重传(Fast Retransmit):当发送方收到三个重复的ACK时,它会立即认为数据包丢失,进行快速重传,而不必等待超时。
- 快速恢复(Fast Recovery):当检测到网络拥塞后,CWND会减少到慢启动阈值的一半,而不是完全恢复到最初的状态,继续进行拥塞避免。
拥塞控制示例:
在慢启动阶段,CWND逐步增大,但如果在过程中检测到数据包丢失,CWND会立即减小,进入拥塞避免阶段,以更小的速率继续增长。
3. 滑动窗口(Sliding Window)
滑动窗口是TCP中的流量控制机制,用于在发送方和接收方之间调节数据流的速率,确保发送方不会发送超过接收方处理能力的数据。
工作原理:
- TCP连接中,接收方会通过**窗口大小(Window Size)**字段告诉发送方自己当前可以接收多少字节的数据。
- 发送方根据接收方的窗口大小来控制自己可以发送的数据量。只要发送的数据量没有超过窗口大小,发送方就可以继续发送数据,而不必等待接收方的ACK。
- 随着接收方处理数据并发送ACK,窗口会向前滑动,允许发送方继续发送新的数据。
示例:
假设接收方的窗口大小为5000字节,发送方可以最多发送5000字节的数据,然后等待接收方的ACK来滑动窗口继续发送。
4. 超时重传(Timeout Retransmission)
TCP协议中,为了确保数据的可靠传输,当发送方发送一个数据包后,如果在指定的时间内没有收到对应的ACK,TCP会重传该数据包,这个机制叫做超时重传。
工作原理:
- 发送方在每次发送数据时都会设置一个定时器,如果在这个定时器到期前没有收到相应的ACK,TCP会认为数据包丢失,于是重传该数据包。
- TCP通过动态估计**往返时间(RTT,Round-Trip Time)**来设置定时器的时间,确保在网络条件变化时能够适应。
- 如果发生超时重传,TCP的拥塞控制机制会将CWND重置为初始状态(通常为1个MSS),重新进入慢启动过程。
5. 延迟确认(Delayed ACK)
延迟确认是TCP中的一种优化策略,目的是减少ACK的发送次数,减轻网络负担。
工作原理:
- TCP协议规定接收方在收到数据后并不立即发送ACK,而是等待一段时间(通常是200ms左右)。如果在这个等待时间内接收方收到了更多的数据包,它会合并这些ACK一起发送,减少ACK的总数量。
- 如果等待时间结束但没有收到新的数据包,接收方会立即发送ACK,避免丢失ACK的情况。
示例:
当接收方收到一个数据包时,它不会立即发送ACK,而是等待是否有更多数据包到达。如果有,它会合并多个ACK一同发送;如果没有,它会在超时后单独发送ACK。
6. 五个特性的关系
- 慢启动 和 拥塞控制 都是为避免网络拥塞设计的机制。慢启动是拥塞控制的初期阶段,负责逐步探测网络的最大吞吐量。
- 滑动窗口 是流量控制机制,用于调节发送方和接收方之间的数据流,确保接收方不会被大量数据压垮。
- 超时重传 是TCP的可靠性保障机制之一,确保丢失的数据包可以重新发送。
- 延迟确认 是一种优化技术,目的是减少ACK的数量,提高网络传输效率,同时确保可靠的传输。
总结
TCP通过这些机制确保了可靠、高效的传输。慢启动和拥塞控制解决了网络过载的问题,滑动窗口调节了发送和接收之间的流量,超时重传保证了数据的可靠性,而延迟确认则进一步优化了传输效率。
十、TCP--当发送方收到三个重复的ACK时,为什么它会立即认为数据包丢失?
在TCP协议中,当发送方收到三个重复的ACK时,它立即认为数据包丢失,这是因为TCP通过重复ACK来推测网络状态,从而及时响应可能的传输问题。具体原因如下:
1. ACK的工作原理
TCP协议是基于序列号的可靠传输协议,接收方每次收到一个有序的数据包后都会发送一个ACK,确认它已经收到这个序列号之前的数据。例如:
- 发送方发送了序列号为1、2、3、4的数据包。
- 接收方收到序列号为1、2的数据包后,会发送一个
ACK 3
,表示它期待接收到序列号为3的数据。
2. 重复ACK的含义
当接收方收到一个失序的包(例如,收到的包的序列号不是它期待的),它不会丢弃这个包,而是会继续发送针对之前已接收的最后一个有序数据包的ACK。
- 如果接收方期望的某个数据包没有按顺序到达,而它接收到后续的数据包,接收方会继续发送相同的ACK来表示它仍然在等待那个丢失的数据包。
- 例如,接收方收到序列号为1、2的包后,期望收到序列号为3的包。如果序列号为4的包提前到达,接收方会重复发送
ACK 3
,告知发送方序列号为3的数据包未收到。
3. 为什么是“三个重复ACK”
TCP通过接收方发出的ACK来判断数据包是否成功传输。如果发送方连续收到三个相同的ACK,即接收方反复告诉发送方某个数据包没有收到,TCP发送方就会推测该数据包可能在网络中丢失了。
- 原因:接收方发出的多个重复ACK通常是因为一个中间的数据包丢失了,而后续的数据包被接收到了(例如,序列号3丢失了,但序列号4、5成功到达了)。重复的ACK表明网络中可能存在数据包丢失。
- 当发送方收到三个重复的ACK时,它假设数据包丢失,并不会等到超时再重传该数据包,而是立即进行快速重传(Fast Retransmit)。
三个重复的ACK被认为是足够的信号,因为:
- 一个或两个重复的ACK可能是由于网络传输的延迟或者顺序颠倒导致的,并不一定意味着数据包真正丢失。
- 但是,三个重复的ACK通常被认为是强烈的丢包信号,因为它意味着后续数据已经到达,且接收方多次确认了同一个未接收到的数据包,丢包的可能性大大增加。
4. 快速重传机制的优势
- 减少等待时间:如果发送方通过超时重传来检测丢包,需要等待超时定时器到期,延迟较大。而通过快速重传机制,当收到三个重复的ACK后,发送方可以立即重传丢失的数据包,减少了等待时间。
- 减少拥塞窗口的剧烈收缩:快速重传配合**快速恢复(Fast Recovery)**机制,可以避免发送方的拥塞窗口(CWND)直接回到初始值,而是只将拥塞窗口减半,从而继续较为平稳的数据传输。
5. 总结
当发送方收到三个重复的ACK时,它认为数据包丢失是基于以下逻辑:
- 重复ACK表示失序或丢包:接收方通过重复ACK表明它在等待某个特定序列号的数据包,这通常意味着丢失或失序。
- 三个重复ACK是强丢包信号:三个重复ACK被认为是足够强的信号,能够区分丢包和偶发的网络延迟或顺序颠倒。
- 快速响应,减少重传延迟:通过快速重传,发送方可以在确认丢包后迅速采取行动,避免等待较长的超时重传。
因此,三个重复的ACK触发了发送方的快速重传机制,可以及时修复丢失的数据包,保持数据传输的高效性。
问题:一个或两个重复的ACK可能是由于网络传输的延迟或者顺序颠倒导致的,并不一定意味着数据包真正丢失,为什么会这么判断
判断一个或两个重复的ACK不一定意味着数据包真正丢失的原因,主要与网络传输中的延迟和乱序现象有关。在实际的网络环境中,TCP会遇到很多复杂的网络状况,比如数据包乱序到达、延迟、抖动等,这些都可能导致出现少量的重复ACK,但并不一定意味着数据包丢失。以下是更详细的解释:
1. 网络传输中的乱序现象
网络传输不是总是按照发送顺序接收到数据包,由于路由器的处理、负载均衡、网络中的不同路径等原因,某些数据包可能会比之前发送的包更早到达接收方。
示例:
假设发送方按顺序发送了以下序列号的数据包:
数据包序列号:1, 2, 3, 4
但由于网络中的乱序现象,接收方可能会按以下顺序收到:
接收顺序:1, 2, 4, 3
当接收方收到序列号为4
的数据包时,它发现之前期望的数据包3
没有到达,因此会发送一个重复的ACK 3
,表示它还在等待序列号为3
的数据包。此时,发送方收到的这个重复ACK并不一定表示3
号包丢失,可能只是由于网络中发生了乱序,3
号包稍后才会到达。
结论:
在这种情况下,一个或两个重复ACK很可能只是由于乱序而不是丢包引起的,等待一段时间后,可能数据包就会到达。
2. 网络中的延迟现象
在复杂的网络环境中,数据包在不同路径上传输,可能经历不同的网络设备和链路,每个设备的处理时间和路径的拥塞情况可能会导致某些数据包的延迟。
示例:
假设发送方发送了序列号为1, 2, 3, 4
的数据包,接收方以此顺序接收了数据包:
- 接收方先接收到了序列号为
1, 2
的数据包,按顺序发送了ACK 3
。 - 由于网络中某个路径发生了延迟,序列号为
3
的数据包迟迟没有到达,但序列号为4
的数据包先到达了。接收方仍然发送ACK 3
(因为它还没有收到3
号包)。
在这种情况下,重复的ACK只是表明接收方还在等待序列号为3
的数据包,但这并不一定意味着3
号包丢失,可能只是因为网络传输中的延迟使得数据包还未到达。
结论:
网络延迟也可能引发少量的重复ACK,而不是数据包丢失。
3. 重复ACK的意义
TCP中的ACK机制是累积确认的,接收方只会发送针对已按顺序收到的最后一个数据包的确认ACK。例如,如果接收方收到的序列号是1, 2, 4
,那么它将继续发送ACK 3
,因为它仍在等待序列号3
。
-
一个或两个重复ACK:通常是由于网络中的暂时性问题(如乱序、延迟)造成的。少量的重复ACK并不足以判断数据包丢失,因为这些问题在网络中时常发生,并且传输可能很快恢复正常。
-
三个及以上的重复ACK:TCP认为三个重复的ACK是一个强烈的信号,表明在网络中后续的数据包已经到达,而丢失的数据包可能不会再到达。此时触发快速重传机制,可以减少等待超时的时间,提高传输效率。
4. TCP中的鲁棒性设计
TCP的设计考虑到了实际网络环境中的各种不确定性,比如乱序、抖动、延迟等。如果仅因为一个或两个重复ACK就立即触发重传,可能会导致不必要的重传操作,增加网络负担。因此,TCP协议使用了三个重复ACK作为触发快速重传的标准,以平衡响应丢包和避免不必要重传之间的关系。
- 一个或两个重复ACK:可能只是偶发事件,如网络延迟或数据包乱序,因此TCP不会立即重传。
- 三个重复ACK:表明接收方已经多次确认没有收到特定序列号的数据包,发送方认为此时丢包的可能性较大,立即触发快速重传。
5. 总结
- 一个或两个重复ACK通常是由于网络延迟或数据包乱序引起的,TCP认为这可能是暂时性的网络现象,不一定表示数据包丢失。
- 当收到三个重复ACK时,TCP判断有很大可能是某个数据包丢失了,因为后续的数据包已经被接收,而丢失的数据包未能按时到达。这时,TCP才会触发快速重传机制,避免等待超时定时器。
这种设计提高了TCP在网络不确定性下的鲁棒性,避免了过于频繁的重传,同时也能及时响应真正的丢包情况。
问题:假如发送的是1,2,3,4 , 5; 收到的是1,2,4,5,3. 这样也会收到三次重复的ACK,但数据并未丢失,却又触发了重传机制,也造成了浪费。
这种情况确实是TCP在遇到乱序传输时的一个潜在问题。在这种情况下,发送方会收到三个重复的ACK,但实际上数据并未丢失,乱序的数据包(如3
号包)最终还是到达了接收方。然而,由于TCP协议的设计,当发送方收到三个重复的ACK时,它会触发快速重传机制,这确实会造成不必要的重传和资源浪费。
下面详细解释为什么会发生这种情况,以及TCP是如何权衡这种问题的。
1. 重复ACK与乱序传输的冲突
在TCP协议中,重复ACK通常被认为是数据包丢失的信号,但它并不能区分乱序和丢包的情况。你提到的场景:
- 发送方按序发送数据包
1, 2, 3, 4, 5
。 - 接收方按序收到
1, 2, 4, 5
,但3
号包由于乱序晚到了。 - 接收方在收到
4
和5
号包时,都会发送重复的ACK 3
,因为它还在等待3
号包。
当接收方收到乱序的4
和5
号包时,它已经发送了三个重复的ACK 3
,发送方认为3
号包可能丢失了,立即触发了快速重传,重新发送3
号包。而此时接收方其实已经收到了3
号包,这个重传包实际上是不必要的。
2. 为什么TCP仍然选择这种机制
TCP选择在三个重复ACK时触发快速重传,而不是等待更长时间,是基于以下几点考虑:
-
乱序与丢包的频率权衡:在大多数正常的网络情况下,丢包发生的频率要远高于乱序传输。虽然乱序可能导致不必要的重传,但丢包更常见。通过在三个重复ACK时进行快速重传,TCP能够迅速恢复丢失的数据,而不需要等待超时,提升了传输的效率。
-
乱序数据包不会频繁出现:现代网络中的路由算法、数据包的路径选择、网络设备的调度等,已经能很好地减少乱序到达数据包的频率。虽然乱序传输仍然可能发生,但在大多数情况下,它并不会成为主要瓶颈。因此,TCP选择在丢包比乱序更常见的假设下进行优化。
-
减少超时等待:如果不采用快速重传,而是依赖超时定时器来重传丢失的数据包,传输效率会明显下降。在网络良好的情况下,超时重传的等待时间比触发快速重传要长得多。通过使用重复ACK来迅速判断丢包,可以显著提高传输效率。
3. 解决乱序和丢包问题的后续优化
虽然TCP不能直接区分乱序和丢包,但在实际网络中,为了减少不必要的重传和提高性能,一些机制已经被引入:
1. 选择性确认(SACK,Selective Acknowledgment)
选择性确认是TCP的一种扩展,它允许接收方告诉发送方它准确接收了哪些数据包,而不仅仅是通过累积的ACK来报告。
- 在使用SACK的情况下,接收方不仅可以告知它收到了
1, 2
,并希望接收到3
,还可以告知发送方它已经成功收到了4, 5
号包。这样发送方就能清楚地知道哪些包已经到达,只需要重传丢失的3
号包,避免重传4, 5
号包。 - SACK可以有效解决乱序到达时不必要重传的问题,因为发送方能够准确地知道哪些数据包已经到达,无需重发那些已经成功接收的数据。
2. 时间戳选项(Timestamp Option)
TCP的时间戳选项是一种扩展,它可以帮助发送方更准确地估计网络延迟,并避免对旧的数据包进行误判。时间戳机制通过为每个数据包打上时间戳,接收方可以更明确地告诉发送方它所收到的数据包的顺序信息,从而进一步减少乱序引起的误判。
3. 乱序缓冲(Out-of-order Buffering)
现代操作系统和网络栈实现中,接收方通常会保留一个乱序缓冲区,用来存储乱序到达的数据包。当接收方收到乱序的数据包时,它可以暂时将其放入缓冲区中,等待丢失的数据包到达后再进行处理。
这样,即使发生了乱序情况,接收方也能有效地恢复顺序,并避免发送大量的重复ACK。
4. 总结
你所提到的乱序导致的重复ACK问题确实可能会导致不必要的重传,但TCP的设计是基于实际网络中丢包远比乱序更常见的假设。通过使用三个重复ACK来触发快速重传,TCP能够显著减少等待时间,提高传输效率,即使偶尔会触发不必要的重传。
为了解决乱序问题,现代TCP协议已经引入了如SACK、时间戳选项等机制,它们可以显著减少由于乱序引起的不必要重传,提高传输的准确性和效率。
https://github.com/0voice