TCP 连接建立和关闭的过程
2023-06-10

本文主要参考了人民邮电出版社出版的《Unix 网络编程 卷 1:套接字联网 API》

TCP 三次握手建立连接

TCP 连接建立的过程如下图所示:

TCP三次握手.jpg

具体来讲,TCP 建立连接的过程包含以下几个步骤:

  1. 服务器首先应该进入可以接受连接的就绪状态。一般来说,服务器通过调用 socket(), bind()listen() 进入可以接受连接的状态,称为被动打开。
  2. 客户端通过调用 connect() 发起连接建立请求,称为主动打开。这个步骤中,客户端会向服务器发送一个包含 SYN=j 的分节,告诉服务器接下来发送的数据的初始序列号。一般这个报文不包含额外数据。
  3. 服务器必须确认客户端的 SYN, 即 ACK=j+1, 也需要向客户端告知接下来发送的数据的初始序列号,即 SYN=k.
  4. 客户端必须确认服务器的 SYN, 即 ACK=k+1.

以上四个步骤中,至少需要交换 3 个分组,因此称 TCP 三次握手建立连接。

需要注意的几个细节是:

  1. TCP 是全双工通信,客户端发送的 SYN 和 服务端发送的 SYN 数值大小没有必然联系。对同一个方向的数据而言,SYN 和 ACK 是对应的,即对于从服务端到客户端的数据,服务端的 SYN 和 客户端的 ACK 是对应的;对于客户端到服务端的数据,客户端的 SYN 和 服务端的 ACK 是对应的。
  2. 在连接建立过程中,虽然发送的分组不包含实际数据,但是任会在每个数据传输方向上消耗一个 SYN 编号。

TCP 四次挥手关闭连接

TCP 关闭连接的过程如下图所示:

TCP四次挥手.png

TCP 的主动终止方不一定是客户端,两边都可以发起,图中为了方便表述,是客户端首先发起的请求。

具体来讲,TCP 连接关闭包含以下步骤:

  1. TCP 连接中的某一方首先调用 close(), 称为执行了主动关闭。这一方会向对方发送一个 FIN=m 的分节,表示数据发送完毕。
  2. 另一方会接收到 FIN=m 这个分节,该方称执行了被动关闭。需要用一个 ACK=m+1 的分节确认。至此,主动关闭方到被动关闭方的数据通路关闭,但是被动关闭方到主动关闭方的通路仍然可以发送数据。
  3. 当被动关闭方也无数据传送时,也会调用 close() 关闭,会发送一个 FIN=n 的分节。
  4. 主动关闭方需要确认对方的 FIN 分节,这会发送一个 ACK=n+1 分节。

需要注意一些细节:

  1. 被动关闭方有可能会把 ACK=m+1 和 FIN=n 合并起来用一个分组发送。

  2. 虽然在描述的时候,是讲 close() 调用发送了 FIN 分节,但是其实无论持有套接字(文件描述符)的进程主动(main() 返回或者调用 exit())还是被动(收到终止信号)终止,打开的描述符都会被关闭,仍然能够准确发出 FIN 分节。

  3. 关于 TIME_WAIT 状态,TIME_WAIT 状态出现的理由有两个:

    1. 可靠地实现 TCP 全双工连接的终止;

    可靠地终止是指要准确的处理四次挥手的任何一个分节因网络意外丢失的情况。假如最后一个分节 ACK = n+1 丢失,那么被动关闭方就会重发 FIN = n 那个分节,如果主动关闭方没有维护状态信息,会响应一个 RST 分节,这对被动关闭方而言意味着对端出错而不是正常结束。

    1. 允许老的重复分节在网络中消逝。

    假设一个连接正常结束,然后隔了一段时间之后,同样的两台计算机同样的端口上重新建立连接,如果不加处理地直接建立这个连接,那么可能上一次这个连接上发送的有些数据包会出现在新的连接上面。如果不给 TIME_WAIT 状态的连接重新建立连接,而且 TIME_WAIT 状态保持两个最长分节生命周期,那么足以让某个方向上的所有分组被可靠地丢弃。