Tcp学习笔记

1 minute read

Published:

TCP, UDP协议学习笔记

  • UDP校验和

    • 计算方法

      • 将报文段中所有的16比特的和进行反码运算,如果溢出则进行回卷。
        • 回卷操作:将溢出的位加到最低位
    • 报文段中的16比特字:源端口号,目的端口号,长度,原校验和

    • 例子

      0110011001100000
      0101010101010101
      1000111100001100 -> overflow
      + => 010010101000001
      回卷:010010101000010
      ~  :101101010111101
          
      RECEIVER: 在加上原校验和取反, 若不为1111111111111111, 则说明分组中有差错,未正确传输
      
    • 处理:弃用该报文段

  • TCP

    • Maximum Segment size(MSS): TCP可从缓存中取出并放入报文段的最大限度

      • 由MTU(maximum transmission unit) 确定, 最大链路层帧长度
    • 报文结构

      32 bit
      - source port: destination port (16 each)
      - sequence number (32)
      - acknowledgement number(32)
      - head length;reserved;  CWR/ECE/URG/ACK/PSH/RST/SYN/FIN; RECEIVER WINDOW
      - checksum field/ emergency data pointer
      - options (协商MSS,以及时间戳选项)
      - data
      
      • 传输较大文件:划分成若干个长度为MSS的小块
      • PSH选项:接受方立即将数据返回上层
      • RST,SYN,FIN选项:用于连接的建立和拆除
      • UGR指示紧急数据
      • CWR 拥塞窗口flag,明确拥塞报告
    • 三次握手详细:

      • 第一次:a->b 没有数据,但首部的SYN flag被标记, 随机选取初始序号(client_isn)

      • 第二次:b->a b将为此连接分配内存,发送包:SYN = 1, ACK = client_isn++, SEQ = 随机选取sever_isn (SYNACK报文段)

      • 第三次:a->b a将为此连接分配内吨,发送包:SYN = 0, ACK = server_isn++, SEQ = client_isn++;

        此次发送的包可以有负载

    • 结束连接:

      • 与握手类似,只是FINflag将被标记,服务器收到FIN将再发一个带ACK的FIN包然后关闭,最后客户将再发一个包并定时等待,确认服务器关闭(没有包过来)后则关闭
    • 序列号和确认号

      • 例子,若MSS为1000字节,则序号为0, 1000, 2000, 以此类推

        • 第一个报文段有从0开始的数据,第二个是从1000开始
      • 确认号:主机A填充进报文段的序列号是A期望从B收到的下一字节的序列号

      • 累积确认(cumulative acknowledgement): 只确认该流中第一个到丢失字节为止的字节。

        • 为保证可靠性,能完整重建数据
      • Example:

        - A: Seq=42, Ack=79, data='C'
        - B: Seq=79, Ack=43, data='C'
        - A: Seq=43, ACK=80
        
        • 起始Sequence:客户端发送第一个报文段序号为42, 服务器发送第一个序号为79。
        • 第一个包:客户将向服务器发送第42字节,期望从服务器获得第79号字节
        • 第二个包:确认已经收到用户42号字节以及之前的内容, 期待第43号字节的出现,序号79为服务器要发送的第一个字节
        • 第三个包:序列号表明将向服务器发送第43号字节,确认号表明确认已经收到字节流第79号及以前的字节,正在等待第80号出现
      • 往返时间估计与超时

        • RTT 往返时间, 数据来回一次用的时间

        • 估算:取样(Sample RTT) 注意:不会为重传包计算RTT值(猜想:有可能马上收到相应ACK,若计算会使估计值偏小)

        • 计算方式:Exponential Weighted Moving Average移动指数平均(EWMA)

          EstimatedRTT = (1-alpha)*EstimatedRTT + alpha*EstimatedRTT
          
        • 偏差值计算

          DevRTT = (1-beta)*DevRTT + beta*|SampleRTT - EstimatedRTT|
          
        • 超时区间

          TimeoutInterval = EstimatedRTT + 4 * DevRTT
          
    • 快速重传

      • duplicate ACK 冗余ACK, 再次发送确认
      • 具体方法
        • 正常接收前面的数据, 但延迟, 等待后重传
        • 带有比期望序号大的SEQ(序列号)的包出现,有一定间隔, 立即重传
        • 部分填充间隔的包出现, 如果起始于间隔的低端,则立即发送ACK (正确接收的数据到本包为止,以此为基础发送)
      • 3个duplicate ACK(接收了对方传来的三个相同ACK)
        • 确认在本报文段之后的数据已经丢失,快速重传
        • 原理
          • https://www.zhihu.com/question/21789252
    • 流量控制, 拥塞控制

      • 发送方维护接收窗口 receiver window -> 接收方还有多少缓存空间
      • 保证缓存不溢出则,上一个读取的字节号和上一个接收的字节号之差不得大于缓存容量
      • rwnd跟踪缓存剩余量,即缓存总量减去上述两个字节号之差
      • 特殊情况:
        • rwnd = 0
          • B发送rwnd=0之后如果不再发送请求,则A认为B缓存满,将不会主动发送信息,但实际上这之后B的缓存清空了,则导致A被阻塞
          • 解决:B中接收窗口为0时,A将继续发送只有一个字节数据的报文段,直到缓存清空。rwnd != 0
    • 拥塞控制

      • 在一个发送方中未被确认(被对方确认,即收到了对方对于该数据的ACK)的数据量不能超过cwnd和rwnd中的最小值

        LastByteSent - LastByteAcked <= min{cwnd, rwnd}
        

        TCP Reno

      • 慢启动:

        • cwnd = MSS
        • 初始发送速率:MSS/RTT
        • 每当报文段首次被确认就增加一个MSS
        • 每过一个RTT, 发送速率*2(每一个正确接收的包都将导致速度增加一个MSS), 指数增长
      • 丢包时间 -> cwnd = 1, 慢启动

      • ssthreash慢启动阈值 = cwnd/2

        • 超过本阈值,结束慢启动,并开启拥塞避免
        • 三个重复ACK,结束慢启动,快速重传,快速恢复
      • 避免方法

        • 通过阈值,每个RTT只将cwnd值增加一个MSS
        • 实现:每收到一个包,将速率增加1/cwnd * MSS
        • 超时的包:cwnd = 1, 丢包事件:ssthreash = cwnd/2
      • 快速恢复

        • 对于每个收到的重复ACK,cwnd += MSS, 当丢失文段的ACK到达时, TCP在降低cwnd后进入拥塞避免状态
          • 出现超时事件:cwnd = 1, 慢启动
          • 丢包: cwnd = MSS, ssthresh = cwnd/2