本文章记录TCP的一些杂七杂八的知识点,比较零碎。

  1. ICMP 可用于发现链路上的最小 MTU

  2. 一条 sock 连接有五元组(Proto, SourceIp, DestIp, SourcePort, DestPort) 组成,任意一个改变都是可以是一个新的连接。
    比如两台机器都有 2个IP,2个端口,用于TCP连接。那么他们之间可以创建 (2222)2 = 32 个 TCP 连接。

  3. 传输层协议除了TCP 还有 UDP/SCTP/DCTP

  4. TCP中的序号 是字节流编号,而不是报文的序号。比如第一个报文序号是0,然后该报文有300个字节的数据,那么第二个报文的序号就该是300

  5. 确认号表示期望收到的下一个报文的第一个字节编号是多少,比如面收到第一个报文之后,确认号就回设置为300,表示接下来希望收到第一个字节编号为300的报文

  6. TCP的第一个序号通常假设为0, 但实际上可以随机的选择初始序号

  7. 如果发送端收到同一个序号的重复确认3次(冗余ACK)即可认为序号之后的报文已经丢失,可以进行快速重传

  8. TCP 接受方重传的时候可以跳过那些已被选择确认的报文,需要接收方支持选择确认。(SACK,在TCP首部的 options 字段里)

  9. TCP 接受方会维护一个 LastByteRead, 用户层已读取的最后一个字节的编号; LastByteRecv, 放入到缓存区的最后一个字节的编号。还有一个缓存区大小RecvBuffer。滑动窗口大小,就是缓存区大小(Buffer)减去已缓存的大小(Recv-Read)

  10. TCP 发送方会维护一个 LatByteSent, 已发送的序号; LastByteAcked, 已被确认的序号。Sent-Acked 需要小于滑动窗口的大小。来保证接口方的缓存区不会被溢出。

  11. 如果接收方缓存区满了之后,窗口会设置为0;之后发送方会发送只有一个字节的报文段,用来 “轮询” 窗口更新。

  12. TCP第三次握手确认的时候可以携带一些数据。

  13. 现在的主流操作系统都支持syn-cookie,在第三次握手之前,服务器并不维护客户端的信息, 可以有效的防御syn-flood攻击。

  14. MSS 最大报文段长度,避免物理层分片,通常比MTU小一点。(在options里协商)

  15. RTT 连接往返时间,即发出后到收到ACK的时间。

  16. 拥塞控制

    1. 慢启动,刚开始以一个MSS的值传输,后面2个,4个,成指数增长。直到发生拥塞,把此时窗口值的1/2,叫做慢启动阈值。
    2. 发生拥塞的时候,重新从1个MSS开始增加,直到增加到慢启动阈值, 之后不再指数增加,而是一个MSS一个MSS的增加。这个过程叫拥塞避免。
    3. 快速恢复,收到3次冗余ACK的时候,窗口不再从1个MSS开始,而是从慢启动阈值+3开始,然后开始重传。
  17. TCP 字段

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     0                   1                   2                   3   
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Source Port | Destination Port |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Sequence Number |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Acknowledgment Number |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Data | |U|A|P|R|S|F| |
    | Offset| Reserved |R|C|S|S|Y|I| Window |
    | | |G|K|H|T|N|N| |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Checksum | Urgent Pointer |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Options | Padding |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | data |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    在RFC 3168 中 TCP 的 reserved 位置又使用了两位(CWE, ECE 用于处理拥塞控制和显式拥塞通知)

    1
    2
    3
    4
    5
    6
    7
     0                   1                   2                   3   
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Data | |C|E|U|A|P|R|S|F| |
    | Offset|Reser. |W|C|R|C|S|S|Y|I| Window |
    | | |R|E|G|K|H|T|N|N| |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  18. TCP与网络编程

    1. 首先是服务端 bind,listen 初始化接受队列
    2. 客户端 connect 选择本地端口,发起 syn 握手请求,同时启动重传定时器
    3. 服务端 回应 syn ack, 将连接加入到半连接队列,启动重传定时器
    4. 客户端收到 ack,清除定时器,设置为已连接,发送ack
    5. 创建 sock 从半连接队列中取出放到全连接队列。
    6. accept 从全连接队列中取出 socket