【lwip】12-一文解决TCP原理( 八 )

  • 终端B收到这个SACK后 , 重传包含Byte 1453 至 2904的TCP Segment;
  • 终端A向B发送ACK Segment , 其中ACK Num=4097 , 表明它已经收到Seq Num 4097之前的所有字节;
  • 之后,数据通信恢复正常 。
  • lwip:
    • 从lwip的tcp_enqueue_flags()函数看,如果对端不支持SACK,本地也不会支持SACK 。
    12.10.3 WSOPTWSOPT:Window Scale (WSCALE or WSopt) Option
    TCP Header的Window Size字段长度为16bit,因而正常情况下,Window Advertisement最大只能是65535 Bytes 。
    Window Scale Option用于将TCP Header的Window Size字段从16bit扩展至最多30bit,格式如下所示:
    kindLengthshift.cnt(3)(3)(范围0~14)
    1. shift.cnt的取值范围为0~14,表示将Window Advertisement的值扩展至“WindowSize × 2^shift.cnt , 这就是最终的窗口值 。
      1. 取值范围[0, 14]的原因:最大TCP序号限定为2^16 * 2^14= 2^30 < 2^31 。该限制用于防止字节序列号溢出 。
    2. WSopt只能出现在SYN Segment或SYN+ACK Segment中 , 因此shift.cnt在三次握手之后就会固定下来 。
    3. 另外,WSopt是双向独立的,因此连接的两个方向可以有不同的Shift.cnt 。但是,WSopt必须双向同时启用,也就是说,如果SYN中不带有WSopt,SYN+ACK中也不能出现WSopt;同样,如果SYN+ACK中不带有WSopt,那么发起SYN的一端就会当作自己也不曾发送过WSopt 。
    4. shift.cnt根据接收Buffer的大?。蒚CP自动选取 。接收Buffer由系统或程序设定 。
    12.10.4 TSOPTTSOPT:Timestamps Option and PAWS
    • Timestamps:时间戳 。
    • PAWS:Protect Against Wrapped Sequence Numbers:防止序列号回绕 。
      • 回绕:就是序列号溢出 , 重新从起点计算 。
    主要两个功能:计算RTT和防止序列号回绕 。
    启用Timestamp Option后,每个TCP Segment中都会带有Timestamp Option , 其中包含了两个32bit的Timestamp(TSval和TSecr) 。
    具体格式如下所示:
    Kind(8)Length(10)imestamp value(TSval)Timestamp Echo Reply(TSecr)
    1. TSval指明了发送端在发送TCP Segment时的Timestamp;接收端在对该TCP Segment做ACK时,将TSval值回显在TSecr字段中 。
      1. 注意:由于TCP连接是双向的,接收端在ACK中回显TSecr时,也会把自己当前的Timestamp放入TSval字段 。
    2. Timestamp是一个随时间单调递增的值,由于TCP接收端只需要在ACK中将TSval简单地回显,因此通信双方并不需要进行时间同步等操作 。
    3. 通过Timestamp Option,发送端再也不需要在内存中保存发送Segment的时间了,只需要将其放入TSval,然后接收端将其回显在ACK Segment即可 。当发送端收到ACK Segment后,取出TSscr,和当前时间做算术差,即可完成一次RTT的测量 。
    4. 若非通过Timestamp Option来计算RTT,大部分TCP实现只会以“每个Window采样一次”的频率来测算RTT 。因此通过Timestamp Option,可以实现更密集的RTT采样 , 使RTT的测算更精确 。
    Timestamp Option还能防止序列号回绕(PAWS) 。
    序列号回绕冲突只会出现在高速连接上 。
    序列号回绕冲突是指序列号seq[0,2^32],即是最大4G 。如果在高速的连接中,某段数据A因为路由问题出现重传(此时网络是可能出现2个以上时间段A) , 收到一个时间段A后继续接收 。seq溢出,轮回第二次序列号seq[0,2^32],如果此时上一轮回重传的数据段A也到达了,那怎么判断当前序列号seq是本次轮回的还是上次轮回的?(当然,一次seq的轮回需要在MSL内,否则这个报文段在它的TTL到期时会被某个路由器丢弃)
    Timestamp Option能解决这个冲突 。
    参考以下例子来理解:
    1. 假设TCP Window Size为1GB(使用Window Scale) , 发送者每发送一个Window的数据Timestamp值加100,数据的发送情况如下所示:
    时间点发送数据量Seq NumTimestamp接收10G:1GOG:1G0~100OK21G:2G1G:2G100~200其中某些segment丢包后重传(重传后,网络上可能会出现多个这个数据段的包;也就是说可能会因为网络延迟原因 , 接收端会收到多个这个时间段的包 。)32G:3G2G:3G200~300OK43G:4G3G: 4G300~400OK54G:5G0G:1G400~500OK65G:6G1G:2G100~200、500~600接收500~600的包 。丢弃时间戳为100~200的包,因为从400开始序列号seq就已经开始回绕了 。在时间点2的时候,发生了丢包,然后重传 。
    在时间点5 , 序列号开始回绕 。
    在时间点6,已经被认为“丢包”的Segment延迟到达了 。
    那怎么判断这个序列号seq为[1G:2G]的报文是上一轮回的还是现在需要接收的?

    推荐阅读