• Home
  • About
    • Road to Coding photo

      Road to Coding

      只要那一抹笑容尚存, 我便心无旁骛

    • Learn More
    • Email
    • Github
  • Posts
    • All Posts
    • All Tags

TCP连接与终止

17 Mar 2019

我们将TCP协议的内容分为五部分: 连接和终止, 超时重传, 流量控制, 拥塞控制, 保活机制总共5部分. 在进行TCP协议的可靠机制分析前, 我们先对TCP协议的组成进行分析.

TCP协议的组成

TCP State

如上图所示, 即为TCP头部组成的部分, 我们着重来分析的是下面几个组成部分:

  • 序列号 TCP协议为此次连接发起的每一个字节进行编码, TCP头部的序列号, 表示此报文段第一个数据字节的编码, 每个序列号标识一个字节, 总共可以表示2^32 - 1个字节,一旦超出, 就回溯为0, TCP协议可以据此进行数据的流量控制和超时处理

  • 确认号 也叫做ACK号, 表示,ACK发送方期望接收的序列号, 即最后成功接收的数据字节的序列号 + 1

  • 标志字段 标志字段是TCP协议中相当重要的部分, 最初常用的字段是6个, 现在扩充为8个. 这几个字段的基本含义如下:
  • CWR: 表示拥塞窗口减少
  • ECE: ECN回显
  • URG: 紧急指针字段有效
  • ACK: 确认, 确认号字段有效
  • PSH: 推送,
  • RST: 连接错误, 重置连接
  • SYN: 用于初始化一个连接的同步序列号
  • FIN: 结束连接

这里我们将SYN字段标志拿出来讲. 它用来表示一个连接的发起. 更重要在于: 用于初始化一个连接的序列号, 即ISN(初始化序列号). ISN一般是采用算法取得随机数, 为了安全. 当序列号耗尽时, 又重新归0, 从头继续计数 一般, 本端发送[SYN M] -> [SYN N, ACK M + 1] -> [ACK N + 1]

其中ACK都是对端SYN的初始序列号 + 1, 这里SYN消耗了序列号, 表示SYN属于可靠的传输, 有超时重传机制. 同时, ACK不消耗序列号, 说明ACK有丢失的可能性, 而且ACK实际上, 确认的是至今为止的连接数, 也就是说: 即使我们曾经丢失了一个ACK, 它也可能被下一个到来的ACK确认

最后是关于TCP协议的选项, 这部分内容, 我们一般有这样的几个选项:

  • MSS
  • SACK
  • 时间戳
  • 窗口缩放

这几个的选项, 会在后续进行解释

TCP的连接管理

TCP是面向连接的单播协议, 我们提到它的连接管理能够想到什么? 三路握手建立建立连接, 四路分组断开连接

connection-close

三路握手建立连接

三路握手的基本流程是这样的:

  1. 客户端发起连接, 传送[SYN, Seq = ISN(c)]报文, 表示发起连接, 且通告对端窗口大小
  2. 服务端回复[SYN + ACK, Seq = ISN(s), ACK = ISN(c) + 1],表示收到客户端请求, 同时发送自己的确认号, 其中窗口通告也告诉对端, 自己本端的窗口大小
  3. 客户端,回复[ACK, Seq = ISN(c) + 1, ACK = ISN(s) + 1],表示收到对端的SYN分节, 连接建立完成

上面的流程便可以完成TCP的三路握手建立连接了. 来聊几个问题:

  1. Q: 为什么建立连接需要三次分组交换? A: 因为网络交互是分组交换, A -> B, B -> A的路径可能完全不同. 所以必须互相收到ACK才行
  2. Q: 序列号值怎么改变的? A: 发起连接的时候, 各自设置ISN, 之后互相对端确认, 注意: SYN会消耗序列号, ACK则不会
  3. Q: 建立连接的时候, 都要交换什么数据? A: 在连接建立的时候, 会进行窗口大小的交换, 即窗口通告, 以及MSS
  4. Q: ISN为什么会是随机的? A: 我们对于一条连接的标识是五元组, 在一条连接断开后, 立马重新连接, 就有可能出现非同一条连接的持续情况, 而且, 可能会造成安全问题, 所以一般采用复杂的算法: 基于时钟和结合哈希, 进行Seq计算, 保证安全性

四路分组断开连接

四路分组断开连接 任意一端发起断开都可以, 建立连接后, 两端是对等, 这里仅仅以客户端举例

  1. 客户端发送[FIN, Seq = K, ACK = L]发送FIN分节表示断开连接, 同时确认上一个数据包
  2. 服务端发送[ACK, Seq = L, ACK = K + 1], 收到FIN,ACK表示确认到对端的请求,同时ACK + 1
  3. 服务端发送[FIN + ACK, Seq = L, ACK = K + 1], 发送服务端的断开连接请求, 因为ACK不消耗序列号, 所以这两个报文段的Seq是相等的, 因为没有收到对端新发送过来的报文段, 所以ACK一样的
  4. 客户端发送[ACK, Seq = K, ACK = L + 1], 客户端最后确认服务端的请求, 完全断开连接

上面的流程就是四路分组断开连接的过程了. 同样, 来聊几个问题:

  1. Q: 四路分组交换, 为什么要四次? 连接只要三次 A: 之所以需要四路分组交换, 因为TCP是全双工的协议, 两端可以各自断开连接, 所以需要四次, 网络环境紧张时, 可以是三此分组交换断开连接
  2. Q: 我们平时进行学习的时候, 都是FIN -> ACK -> FIN -> ACK的交换, 这里怎么是[FIN + ACK]? A: 其实就是一个细节的问题, 这里详细展示出, 其中的ACK, 建立连接后, 基本上正常的数据交互中, 都是会有ACK的, 用来进行确认机制, 同时进行窗口通告
  3. Q: 我们每次之后的发送ACK确认会提高效率吗, 因为其中包含的信息并不多. A: 不会, 发送ACK和完整的TCP报文段的开销是一致的, 因为确认号是TCP头部的一部分

TCP半关闭

half-close

我们讲TCP是一个全双工的协议. 那么它到底体现在哪里呢? TCP可以两端分开关闭, 仅仅关闭其中一端, 不会影响另外一端的使用情况. 半关闭没什么更好说的, 看图就完事了

同时打开, 同时关闭.

Emmm, 这两种情况十分十分罕见, 出现的机率很小. 要求是: 在一端发送SYN/FIN, 还未到达对端的时候, 另外一端同时发送了SYN/FIN. 这样就发生了同时关连接/同时关闭

其中需要注意的点在于: 因为一端还未发送到, 所以Seq的变化和正常情况下不一致 其他基本没什么区别, 只要知道有这种现象存在就可以了

TCP选项

我们上面提到过TCP选项的问题. 下面介绍几个常用的TCP选项

MSS (maxinum segment size)

MSS即最大段大小, 它表示我们所能接收的最大报文段, 也是发送时最大的报文段. 与窗口大小进行辨析: 窗口大小是协商的结果, 是动态滑动的. MSS不是协商的结果, 它是固定的. 表示此次连接中, 我们所能接收的最大报文段大小

SACK

用于在报文遗失时, 或者丢失时, 进行重传的措施

窗口缩放选项

此选项只能用在SYN报文中, 提供一个比例因子, 进行滑动窗口的左移. 最大可到1GB 在海量数据传输中及极其有用.

TCP状态转换

State

上图即为TCP的状态转换图. 我们对于TCP的状态装换过程中, 这两个状态着重进行分析:

  • CLOSE_WAIT: 此状态的产生自服务端: 接收FIN, 发送ACK之后. 服务端会出现大量CLOSE_WAIT的状态有很多原因: 比如未使用close(2)造成的大量CLOSE_WAIT状态, 导致资源占用.

  • TIME_WAIT: 此状态产生自客户端: 在发送最后一个ACK之后, 进入TIME_WAIT状态.此状态会持续2MSL时间大小

为什么会有TIME_WAIT状态? 主要是两个方面的原因:

  1. 实现可靠的连接终止 因为可能会有最后一个ACK丢失的情况, 所以本端要保持状态, 2MSL保证有足够的时间重发ACK
  2. 让老的重复报文段在网络中过期失效, 保证建立新的连接时不再接收他们.

我们使用SO_LINGER可以直接取消TIME_WAIT状态, 不过,不建议这么做, TIME_WAIT状态是我们的朋友



TCP/IP Share Tweet +1