计算机网络 - TCP三次握手以及四次挥手

709 阅读6分钟

TCP 三次握手

TCP 进行三次握手之前,客户端处于 Close 状态,服务器处于 Listen 状态。

以下3点分别表示三次握手的过程:

  • 第一次握手 —— 客户端发送一个 SYN 段来表示客户端打算连接的服务器的端口,以及初始序号 ISN ,这时候这个 SYN 为报文段 1 ,此时客户端处于 syn_sent 的状态。
  • 第二次握手 —— 服务器收到客户端发送的消息后,发回一个报文段 2,这个报文段包含服务器的初始序号的 SYN 报文段作为应答,同时,还会发送一个 ACK 确认码对客户端的 SYN 报文段进行确认,这个 ACK 就是客户端的初始序号 ISN 加 1 。此时服务器处于 syn_rcvd 的状态。
  • 第三次握手 —— 客户端收到服务器刚发送的 SYN 报文后会发送一个 ACK 报文来进行确认,表示收到了客户端的 SYN 报文,这个 ACK 报文的值就是 ISN + 1。这个是报文段 3。此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。

如下图所示, 我们可以从这个图里看到客户端和服务器处于分别处于什么状态。

那么问题来了,为什么建立连接需要三次握手?为什么不能两次握手就达成目的呢?

在回答这个问题之前,我们需要的是搞清楚,三次握手中每一次的目的是什么:

  • 第一次握手 —— 客户端发包,服务器收到,说明客户端的发送能力和服务器的接受能力是正常的
  • 第二次握手 —— 服务器发包,客户端收到,这时客户端才能知道:服务器和客户端的接收、发送能力是正常的,但是服务器还不能确定客户端的接受能力是正常的。
  • 第三次握手 —— 客户端发包,服务端收到,这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。

所以,需要三次握手才能确定服务器和客户端的接收、发送能力都是正常的。

由此可得,如果二次握手就会出现:

  客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求。
  后来收到了确认,建立了连接。
  数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,
  但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,
  此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接。
  不采用三次握手,只要服务端发出确认,就建立新的连接了,
  此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源。

TCP 四次挥手

TCP断开链接的过程和建立链接的过程比较类似,只不过中间的两部并不总是会合成一步走,所以它分成了 4 个动作,为什么建立一个连接需要三次握手,而终止一个连接要经过四次挥手呢?

这是由于 TCP 的半关闭( half-close )造成的。所谓的半关闭,其实就是 TCP 提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。

刚开始双方都处于 ESTABLISHED 状态:

  • 第一次挥手 —— 客户端发送一个 FIN 报文(报文中会指定一个序列号),此时客户端处于 Fin_wait_1 状态,并停止再发送数据,主动关闭TCP连接,等待服务端的确认。
  • 第二次挥手 —— 服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 + 1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。
  • 第三次挥手 —— 如果服务端也想断开连接了,和客户端的第一次挥手一样,发送 FIN 报文给客户端,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。 即服务端不会再向客户端发送数据了。
  • 第四次挥手 —— 客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要经过时间等待计时器设置的时间2MSL后,以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。

收到一个 FIN 只意味着在这一方向上没有数据流动。客户端执行主动关闭并进入 TIME_WAIT 是正常的,服务端通常执行被动关闭,不会进入 TIME_WAIT 状态。

为什么建立连接是三次握手,而关闭连接却是四次挥手呢?

这是因为服务端在 LISTEN 状态下,收到建立连接请求的 SYN 报文后,可以把 ACKSYN 放在一个报文里发送给客户端。其中 ACK 报文是用来应答的, SYN 报文是用来同步的。

而关闭连接时,当服务器收到客户端的 FIN 报文时,仅仅表示客户端不再发送数据了但是还能接收数据,服务端也未必全部数据都发送给客户端了,所以服务器可以立即 close ,也可以发送一些数据给客户端后,再发送 FIN 报文给客户端来表示同意现在关闭连接,因此,己方 ACKFIN 一般都会分开发送。

TCP 状态码释义

状态 描述
Close 阻塞或关闭状态,表示主机当前没有正在传输或者建立的链接
Listen 监听状态,表示服务器做好准备,等待建立传输链接
syn_sent 发送完第一个SYN报文,等待收到确认
syn_rcvd 收到第一次的传输请求,还未进行确认
established 链接正常建立之后进入数据传输阶段
fin_wait_1 主动发送第一个FIN报文之后进入该状态
close_wait 表示等待关闭,在 ESTABLISHED 过渡到 LAST_ACK 的一个过渡阶段,该阶段需要考虑是否还有数据发送给对方,如果没有,就可以关闭连接,发送 FIN 报文,然后进入 LAST_ACK 状态。
fin_wait_2 在 FIN_WAIT_1 之后, 当对方回应 ACK 报文之后,进入该状态。
last_ack 被动关闭一方发送 FIN 报文之后,最后等待对方的 ACK 报文所处的状态
time_wait 表示收到了对方的 FIN 报文,并发送出了 ACK 报文,就等 2MSL 之后即可回到 CLOSED 状态
close 收到 ACK 报文后,进入该状态