TCP 选项
时间戳选项
TCP 最早在 RFC1323 [] 中引入了 timestamp 选项, 并在后来的 RFC7323 中进行了更新。引入 timestamp 最初有两个目的:
**1.更精确地估算报文往返时间(round-trip-time, RTT) **
2. 防止陈旧的报文干扰正常的连接.
Timestamp 是作为一个 TCP 选项存在于 TCP 首部。如下图所示,一个 timestamp 选项需要占据 首部中的 10 个字节。
Kind: 8
Length: 10 bytes
+-------+-------+---------------------+---------------------+
|Kind=8 | 10 | TS Value (TSval) |TS Echo Reply (TSecr)|
+-------+-------+---------------------+---------------------+
1 1 4 4
选项的核心数据是两个 32-bit 的时间戳字段.TSval 表示发送端发出该报文时的本地时间戳, 而 TSecr 则负责回放(Echo) 最近一次收到的对端报文中的 TSval 的值。
TSval 和 TSecr 在数值上并没有绝对的大小关系。TSval 是以本地的时钟为基准的, 而 TSecr 则是以对端的时钟为基准的。
以下是一个组典型的时间戳交互过程
TCP A TCP B
<A,TSval=1,TSecr=120> ----->
<---- <ACK(A),TSval=127,TSecr=1>
<B,TSval=5,TSecr=127> ----->
<---- <ACK(B),TSval=131,TSecr=5>
启用 Timestamp 选项需要经过双方的协商,协商在三次握手时完成,如果协商成功,则在后续的报文中, 除了 RST 之外的所有报文均必须包含 Timestamp 选项。
TCP Timestamp 选项虽然能带来好处, 但并不是所有的 TCP 连接都会使用该选项,比如 Windows 系统就是默认不不启用该选项的,而 Linux 系统则是默认启用了该选项。 据 tcpm 的统计,在全球范围内,使用了 TCP Timestamp 的连接比例大概为 60%~70% 。
不支持 TCP Timestamp 的理由是该选项占用的报文长度太多了,它会占用 TCP 报文首部的 10 个字节,而且是每个报文都会有这种损耗。
虚拟时钟
前面提到过,TSval 的值是以本地时钟为基准的,更准确地说是来自一个虚拟的时间戳时钟,这个时钟的流逝必须 与真实时间成比例,但不需要一定与真实时间相同,同时它既不能走得太慢,也不能走得太快
不能走得太慢是为了能更准确地测量报文的 RTT。假设这个时钟 10s 才 tick 一下,那么对于 往返时间为 1s 的 TCP 连接,一端发送报文之后,很有可能会发现收到对端的 ACK 报文中的 TSecr 和当前时钟 的值是一样的,这说明 RTT 为 0 ! 显然,这是十分荒谬的。
不能走的太快是为了防止时间戳回绕的干扰。TCP 协议规定的最大 MSL 为 255s, 显然一次时钟的循环必须大于这个值, 换算下来,32-bit 的时钟 tick 必须大于 59ns。否则,就无法区分两个时间戳之差是否经历了时间戳的回绕。
RFC 规定虚拟时钟的频率为每 1ms 到 1s 一个 tick。 按照 1ms 计算,32-bit 的时间戳回绕一次需要 24.8 天。