面试_计算机网络_三次握手、四次挥手

201 阅读10分钟

三次握手、四次挥手代表TCP的连接与释放



TCP三次握手

TCP 三次握手,其实就是建立一个 TCP 连接,包括三次握手,也就需要 3 个数据包。握手的主要作用就是为了确认双方的接收和发送能力是否正常。如果在握手的某个阶段莫名中断,会再次发送相同的数据包。

假设发送端为客户端,接收端为服务端。开始时客户端状态是CLOSE, 服务器状态是 LISTEN

  • 第一次:客户端发送 SYN 报文,并进入 SYN_SENT 状态,等待服务器的确认;

  • 第二次:服务器收到 SYN 报文,给客户端发送 ACK 确认报文,同时再发送一个 SYN 报文( SYN + ACK ),此时服务器进入 SYN_RCVD (半连接)状态;

  • 第三次:客户端收到 SYN + ACK 报文,向服务器发送ACK确认报文,客户端进入 ESTABLISHED 状态。待服务器收到客户端发送的 ACK 报文也会进入 ESTABLISHED 状态,完成三次握手。




为什么要三次握手?

可以从 确认双方的收发能力防止失效的连接到达服务端 两个方面回答。


1. 确认双方的收发能力

握手最主要的目的就是双方确认自己与对方的发送与接收能力都是正常的

  1. 第一次握手:客户端发送数据,服务端收到了。这样服务端就能得出结论:客户端的发送能力、自己的接收能力是正常的。
  2. 第二次握手:服务端发送数据,客户端收到了。到此时客户端就能得出结论:自己和服务端的接收、发送能力都是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
  3. 第三次握手:服务端发送数据,客户端收到了。到此时客户端就能得出结论:自己和服务端的接收、发送能力都是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。

所以,只有三次握手才能确认双方的接收与发送能力是否正常。


2. 防止失效连接到达服务端

第三次握手是为了防止失效的连接请求到达服务端,让服务端错误打开连接。

假设现在是两次握手机制,如果客户端发送的连接请求在网络中滞留,那么在等待一个超时重传时间之后,便会重新请求连接,此后客户端和服务器经过两次握手完成连接,正常传输数据。如果此时滞留的那一次请求连接到达服务器,根据两次握手的机制还会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。

如果采用的是三次握手,就算服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认,因为现在客户端要么是close状态,要么是ESTABLISHED状态,不会是SYN_SENT状态。由于服务器收不到确认,就知道客户端并没有请求连接。




TCP三次握手中,客户端最后一次回复丢失,会发生什么?

如果最后一次ACK在网络中丢失,那么服务端的状态仍为SYN_RECV,并且根据 超时重传机制依次等待3秒、6秒、12秒后重新发送 SYN+ACK 包,以便 客户端 重新发送ACK包。如果重发指定次数后,仍然未收到ACK应答,那么一段时间后,服务端自动关闭这个连接。

但是 客户端 认为这个连接已经建立,如果 客户端向服务端发送数据,服务端将以RST包(Reset,标示复位,用于异常的关闭连接)响应,此时,客户端知道第三次握手失败。




三次握手的过程中可以携带数据吗?

第一次不可以携带数据

因为此时连接还没有建立,是不保证安全的,并且此时客户端不知道服务端的接收能力是否正常

  • 对方难道不可以将数据缓存下来,等握手成功再提交给应用程序?

这样容易发生SYN FLOOD洪泛攻击。如果攻击者伪造了成千上万的握手报文,每次都在第一次握手中的 SYN 报文中放入大量的数据,而接收方会开辟大量的缓存来容纳这些巨大数据,内存会很容易耗尽。


第三次握手是可以携带数据的。

因为第三次握手时客户端已经处于连接状态,他已经知道服务器的接收、发送能力是正常的了,所以可以携带数据是情理之中。




SYN洪泛攻击(SYN Flood,半开放攻击),怎么解决?

  • 什么是SYN洪范泛攻击?

    SYN Flood利用TCP协议缺陷,发送大量伪造的TCP连接请求,也就是第一次握手,被攻击服务器回应第二个握手包(SYN+ACK包)并进入SYN_RECV的“半连接”状态。而已请求占满队列,导致正常合法的连接请求得不到有效处理。

  • 怎样检测?

    检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击【在 Linux/Unix 上可以使用系统自带的 netstats 命令来检测 SYN 攻击】

  • 怎么解决?

    • 缩短超时时间

    • 增加最大半连接数

    • 过滤网关防护

    • SYN cookies技术




ISN 代表什么?ISN 是固定不变的吗?ISN为何要动态随机?

ISN 是什么?

答:ISN 全称是 Initial Sequence Number,是 TCP 发送方的字节数据编号的原点,告诉对方我要开始发送数据的初始化序列号


ISN 是固定不变的吗?

答:ISN 如果是固定的,攻击者很容易猜出后续的确认序号,为了安全起见,避免被第三方猜到从而发送伪造的 RST 报文,因此 ISN 是动态生成的





TCP四次挥手

刚开始时客户端和服务端都是 ESTABLISHED 状态

  • 客户端发送连接释放报文 FIN,进入 FIN_WAIT_1 状态。
  • 服务端 收到 FIN 报文后返回一 个 ACK 确认报文,然后被动进入 CLOSE-WAIT 状态 ,客户端接收到服务器端返回的 ACK 后,进入 FIN_WAIT_2 状态。此时 TCP 属于半关闭状态,客户端不能再发送消息给服务端,但是如果服务器端有数据要发送的话,客户端依然可以接收。
  • 当 服务端 不再需要连接时,发送释放报文 FIN。服务器此时进入了 LAST_ACK 状态。
  • 客户端收到服务端的 FIN 报文后返回 ACK 确认报文,之后主动进入 TIME-WAIT 状态,等待 2 倍的 MSL(最大报文存活时间)后进入 CLOSED 状态,释放连接。而服务器端收到客户端的确认报文 ACK 后就直接进入 CLOSED 状态。

至此,四次挥手完成。




为什么连接的时候是三次,关闭的时候却是四次?

多的是步骤2

其实握手的时候也可以是4次,只是服务端将 ACKSYN 报文合并发送了,所以减少了一次,三次完成握手。

但挥手时,当服务器收到客户端的 FIN 报文的时候,仅仅表示对方不再发送数据了,考虑到服务端可能还要发送数据或者还有数据没有发送完毕,就维持了一段半关闭状态。因此,服务器的 ACK和FIN是分开发送的,从而导致多了一次。




为什么客户端最后还要等待2MSL?为什么还有个TIME-WAIT的时间等待?

第四次挥手时,客户端接收到服务器端的 FIN 报文后会返回一个确认报文 ACK ,这个 ACK 报文是有可能丢失的,服务器端如果收不到 ACK 的话需要重新发送 FIN 包,TIME-WAIT的作用就是给这个ACK 报文留充分的的传输时间,保证服务器端确实收到了这个 ACK 包。

这个时间就是2MSL(ACK 到达服务器 + 服务器发送 FIN 重传包,一来一回),数值上等于最大报文存活时间,一个MSL 30 秒,2MSL = 60s。

如果 2MSL 时间后也没有收到服务器端的重传包 FIN,说明可以确认服务器已经收到客户端发送的 ACK




客户端TIME-WAIT 状态过多会产生什么后果?怎样处理?

  1. 作为服务器,短时间内关闭了大量的Client连接,就会造成服务器上出现大量的TIME_WAIT连接,严重消耗着服务器的资源,此时部分客户端就会显示连接不上
  2. 作为客户端,会大量消耗的端口,毕竟端口只有65535个,端口被耗尽了,后续就无法在发起新的连接了

  • 解决方法:
    • 用负载均衡来抗这些高并发的短请求;
    • 强制关闭,发送 RST 包越过TIMEWAIT状态,直接进入CLOSED状态



服务器出现了大量 CLOSE_WAIT 状态如何解决?

大量 CLOSE_WAIT 表示程序出现了问题,对方的 socket 已经关闭连接,而我方忙于读或写没有及时关闭连接,需要检查代码,特别是释放资源的代码,或者是处理请求的线程配置。




服务端会有一个TIME_WAIT状态吗?如果是服务端主动断开连接呢?

发起链接的主动方基本都是客户端,但是断开连接的主动方服务器和客户端都可以充当,也就是说,只要是主动断开连接的,就会有 TIME_WAIT状态。




TIME_WAIT和CLOSE_WAIT的区别在哪?

默认客户端首先发起断开连接请求

  • 从上图可以看出,CLOSE_WAIT是被动关闭形成的,当客户端发送FIN报文,服务端返回ACK报文后进入CLOSE_WAIT。
  • TIME_WAIT是主动关闭形成的,当第四次挥手完成后,客户端进入TIME_WAIT状态。



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

如果TCP连接已经建立,在通信过程中,客户端突然故障,那么服务端不会一直等下去,过一段时间就关闭连接了。具体原理是TCP有一个保活机制,主要用在服务器端,用于检测已建立TCP链接的客户端的状态,防止因客户端崩溃或者客户端网络不可达,而服务器端一直保持该TCP链接,占用服务器端的大量资源。


保活机制原理:设置TCP保活机制的保活时间keepIdle,即在TCP链接超过该时间没有任何数据交互时,发送保活探测报文;设置保活探测报文的发送时间间隔keepInterval;设置保活探测报文的总发送次数keepCount。如果在keepCount次的保活探测报文均没有收到客户端的回应,则服务器端即关闭与客户端的TCP链接。




在交互过程中如果数据传送完了,还不想断开连接,怎么维持?

在 HTTP 中响应体的 Connection 字段指定为 keep-alive




www.nowcoder.com/discuss/568… segmentfault.com/a/119000003… www.eet-china.com/mp/a44399.h…