Linux网络编程——浅谈 TCP 三次握手和四次挥手

TCP通信时序

下图是一次 TCP 通讯时序图,TCP 连接建立断开。包括常见的 三次握手,四次挥手。
          image.png

  在这个例子中,首先客户端主动发起连接、发送请求,然后服务器端响应请求,然后客户端主动关闭连接。两条竖线表示通讯的两端,从上到下表示时间的先后顺序,注意,数据从一端传到网络的另一端也需要时间,所以图中的箭头都是斜的。双方发送的段按时间顺序编号为1-10,各段中的主要信息在箭头上标出,例如段2的箭头上标着SYN, 8000(0), ACK1001, ,表示该段中的SYN位置1,32位序号是8000,该段不携带有效载荷(数据字节数为0),ACK位置1,32位确认序号是1001,带有一个 mss(Maximum Segment Size,最大报文长度)选项值为1024。

建立连接(三次握手)的过程

  1. 客户端发送一个带 SYN 标志的 TCP 报文到服务器。这是三次握手过程中的段 1

  客户端发出段1,SYN 位表示连接请求。序号是1000,这个序号在网络通讯中用作临时的地址,每发一个数据字节,这个序号要加1,这样在接收端可以根据序号排出数据包的正确顺序,也可以发现丢包的情况,另外,规定 SYN 位和 FIN 位也要占一个序号,这次虽然没发数据,但是由于发了 SYN 位,因此下次再发送应该用序号1001。mss 表示最大段尺寸,如果一个段太大,封装成帧后超过了链路层的最大帧长度,就必须在IP层分片,为了避免这种情况,客户端声明自己的最大段尺寸,建议服务器端发来的段不要超过这个长度。

  1. 服务器端回应客户端,是三次握手中的第2个报文段,同时带 ACK 标志和 SYN 标志。它表示对刚才客户端 SYN 的回应;同时又发送 SYN 给客户端,询问客户端是否准备好进行数据通讯。

  服务器发出段2,也带有 SYN 位,同时置 ACK 位表示确认,确认序号是 1001,表示“我接收到序号1000及其以前所有的段,请你下次发送序号为 1001 的段”,也就是应答了客户端的连接请求,同时也给客户端发出一个连接请求,同时声明最大尺寸为 1024。

  1. 客户必须再次回应服务器端一个ACK报文,这是报文段3

  客户端发出段3,对服务器的连接请求进行应答,确认序号是8001。在这个过程中,客户端和服务器分别给对方发了连接请求,也应答了对方的连接请求,其中服务器的请求和应答在一个段中发出,因此一共有三个段用于建立连接,称为“三方握手(three-way-handshake)”。在建立连接的同时,双方协商了一些信息,例如双方发送序号的初始值、最大段尺寸等。

  在 TCP 通讯中,如果一方收到另一方发来的段,读出其中的目的端口号,发现本机并没有任何进程使用这个端口,就会应答一个包含 RST 位的段给另一方。例如,服务器并没有任何进程使用 8080 端口,我们却用 telnet 客户端去连接它,服务器收到客户端发来的 SYN 段就会应答一个 RST 段,客户端的 telnet 程序收到 RST 段后报告错误Connection refused

通过这样的三次握手,客户端与服务端建立起可靠的双工的连接,开始传送数据。 三次握手的最主要目的是保证连接是双工的,可靠更多的是通过重传机制来保证的

数据传输的过程

  1. 客户端发出段4,包含从序号 1001 开始的 20 个字节数据。
  2. 服务器发出段5,确认序号为 1021,对序号为 1001-1020 的数据表示确认收到,同时请求发送序号 1021 开始的数据,服务器在应答的同时也向客户端发送从序号 8001 开始的 10 个字节数据,这称为 piggyback
  3. 客户端发出段6,对服务器发来的序号为 8001-8010 的数据表示确认收到,请求发送序号 8011 开始的数据。

  在数据传输过程中,ACK 和确认序号是非常重要的,应用程序交给 TCP 协议发送的数据会暂存在 TCP 层的发送缓冲区中,发出数据包给对方之后,只有收到对方应答的 ACK 段才知道该数据包确实发到了对方,可以从发送缓冲区中释放掉了,如果因为网络故障丢失了数据包或者丢失了对方发回的 ACK 段,经过等待超时后 TCP 协议自动将发送缓冲区中的数据包重发。

  TCP 的每次数据传输都需要客户端发送,服务器应答,如此这样一来一回势必影响通信速度,此时引入一个滑动窗口详解 (TCP流量控制)来解决这个问题

关闭连接(四次挥手)的过程

  由于 TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个 FIN 来终止这个方向的连接。收到一个 FIN 只意味着这一方向上没有数据流动,一个 TCP 连接在收到一个 FIN 后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

  1. 客户端发出段7,FIN位表示关闭连接的请求。
  2. 服务器发出段8,应答客户端的关闭连接请求。
  3. 服务器发出段9,其中也包含FIN位,向客户端发送关闭连接请求。
  4. 客户端发出段10,应答服务器的关闭连接请求。

  建立连接的过程是三方握手,而关闭连接通常需要4个段,服务器的应答和关闭连接请求通常不合并在一个段中,因为有连接半关闭的情况,这种情况下客户端关闭连接之后就不能再发送数据给服务器了,但是服务器还可以发送数据给客户端,直到服务器也关闭连接为止。

关于半关闭的补充说明

  当客户端发送 FIN 标志位时代表客户端想要断开连接,如果接受到服务器回应的 ACK 后则客户端进入半关闭状态,而此时只是关闭了客户端套接字内的写缓冲,并没有彻底断开与服务器的连接,故此仍然能在半关闭后发送 ACK 应答。具体解请见后面的 TCP 网络状态变换。