04-TCP三握四挥

小林coding: 近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题

一. TCP 三次握手过程和状态变迁

04-TCP三握四挥

  • 一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态

  • 客户端会随机初始化序号(client_isn),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN 标志位置为 1 ,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于SYN-SENT 状态。

  • 服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序号(server_isn),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1, 接着把 SYNACK 标志位置为1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。

  • 客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入 server_isn + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务器的数据,之后客户端处于 ESTABLISHED 状态。

  • 服务器收到客户端的应答报文后,也进入 ESTABLISHED 状态。

一旦完成三次握手,双方都处于 ESTABLISHED 状态,此致连接就已建立完成,客户端和服务端就可以相互发送数据了。

二.为什么是三次握手?不是两次、四次?

TCP 建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。序列号能够保证数据包不重复、不丢弃和按序传输。

不使用「两次握手」和「四次握手」的原因:

  • 「两次握手」:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
  • 「四次握手」:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。
解释1:三次握手才可以阻止历史重复连接的初始化(主要原因),才能避免资源浪费

比如客户端发送了一个SYN报文。但是由于此时网络拥堵,这个包滞留在了网络中而迟迟没有到达,这样由于TCP的重传机制,他就会又发送一个Syn包过去。如果「新的 SYN报文 」被服务器处理之后, 「旧 SYN 报文」到服务器了,那么此时服务端就会回一个 SYN + ACK 报文给客户端。客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列号过期或超时),那么客户端就会发送 RST 报文给服务端,表示中止这一次连接。

如果是两次握手连接,就不能判断当前连接是否是历史连接。并且服务器不清楚客户端是否收到了自己发送的建立连接的 ACK 确认信号,所以每收到一个 SYN 就只能先主动建立一个连接,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。 并且如果在「旧 SYN 报文」到服务器之前,客户端已经断开了,此时就会出错。

解释2:三次握手才可以同步双方的初始序列号

序列号在 TCP 连接中占据着非常重要的作用。

  • 接收方可以去除重复的数据
  • 接收方可以根据数据包的序列号按序接收
  • 可以标识发送出去的数据包中, 哪些是已经被对方收到的;

,所以当客户端发送携带「初始序列号」的 SYN 报文的时候,需要服务端回一个 ACK 应答报文,表示客户端的 SYN 报文已被服务端成功接收,那当服务端发送「初始序列号」给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步。

三. 为什么客户端和服务端的初始序列号 ISN 是不相同的?

因为网络中的报文会延迟、会复制重发、也有可能丢失,这样会造成的不同连接之间产生互相影响,所以为了避免互相影响,客户端和服务端的初始序列号是随机且不同的。

四.TCP三次握手中为什么要传回SYN?

SYN是在建立连接时用到的同步信号。接收方(服务器)在第二次握手的时候回传SYN是证明双方之间通信的通道没有问题,我收到的信息确实是你(客户端)发送的信号。

五.TCP中回传了SYN信号为什么还要传ACK?

TCP是可靠连接,双方都要确保发送的信息是可靠的、准确无误的。回传了SYN只是证明服务器收到的确实是客户端发送的信号,但是服务器到客户端之间的通道还需要ACK信号来保证信息的准确无误。

六.TCP 四次挥手过程和状态变迁

04-TCP三握四挥

  • 客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文,之后客户端进入 FIN_WAIT_1 状态。

  • 服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入CLOSED_WAIT 状态。

  • 客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。

  • 等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入LAST_ACK 状态。

  • 客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入TIME_WAIT 状态

  • 服务器收到了 ACK 应答报文后,就进入了 CLOSE 状态,至此服务端已经完成连接的关闭。

  • 客户端在经过 2MSL 一段时间后,自动进入 CLOSE 状态,至此客户端也完成连接的关闭。

这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。

七.为什么挥手要4次(为什么第二次跟第三次不能合并,第二次和第三次之间的等待是什么?)

  • 关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
  • 服务器收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。

从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACKFIN 一般都会分开发送,从而比三次握手导致多了一次。

八. 为什么 TIME_WAIT (保活计时器)等待的时间是 2MSL?

MSL报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为 TCP 报文基于是 IP 协议的,而 IP 头中有一个 TTL 字段,是 IP 数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。

MSL 与 TTL 的区别:MSL 的单位是时间,而 TTL 是经过路由跳数。所以 MSL 应该要大于等于 TTL 消耗为 0 的时间,以确保报文已被自然消亡。

TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是:网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间

比如,如果被动关闭方没有收到断开连接的最后的 ACK 报文,就会触发超时重发 Fin 报文,另一方接收到 FIN 后,会重发 ACK 给被动关闭方, 一来一去正好 2 个 MSL。

2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时

九. 为什么需要 保活计时器 (TIME_WAIT 状态)?

主动发起关闭连接的一方,才会有 TIME-WAIT 状态。

需要 TIME-WAIT 状态,主要是两个原因:

  • 防止接收到旧的数据包,造成数据错乱;

    如果没有 TIME_WAIT 或者TIME_WAIT过短,此时有这样一个情况:服务端在关闭连接之前发送的报文被网络延迟了,这时有相同端口的TCP连接被复用后,被延迟的抵达了客户端,那么客户端是有可能正常接收这个过期的报文,这就会产生数据错乱等严重的问题。所以,TCP 就设计出了这么一个机制,经过 2MSL 这个时间,足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。

  • 保证「被动关闭连接」的一方能被正确的关闭,即保证最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭;

十.如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP 有一个机制是保活机制。这个机制的原理是这样的:

定义一个时间段,默认为2小时,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔75秒,发送一个探测报文,该探测报文包含的数据非常少,如果连续9个探测报文都没有得到响应,服务器就认为客户端出了故障,接着就关闭连接。

上一篇:BIO和NIO有啥区别


下一篇:关于三次握手和四次挥手,面试官想听到怎样的回答?