> 技术文档 > TCP三次握手与四次挥手面试(传输层面试题)回答版_11.tcp的三次握手和四次挥手面试怎么简要的回答

TCP三次握手与四次挥手面试(传输层面试题)回答版_11.tcp的三次握手和四次挥手面试怎么简要的回答

目录

1. 面试官:说一下TCP三次握手的过程

参考面试回答:

2. 为什么需要三次握手 两次不行吗

参考面试回答:

3. 如果第一次握手丢包、会发生什么

4. 如果第二次握手丢包、会发生什么

参考面试回答:

5. 如果第三次握手丢包、会发生什么

6. TCP的半连接队列和全连接队列是什么

7. 介绍一下TCP四次挥手

参考面试回答:

8. 为什么 TCP 需要四次挥手 三次挥手不行吗

9. TIME_WAIT 是如何产生的

10. TCP的四次挥手为什么要有TIME_WAIT

参考面试回答:

11. 为什么 TIME_WAIT 状态要等待 2MSL

参考面试回答:

12. TIME_WAIT 过多有什么危害

参考面试回答:

13. 怎么解决 TIME_WAIT 状态过多的问题

参考面试回答:

14. 服务端产生大量 TIME_WAIT 状态的原因是什么?

参考面试回答:

15. 服务端产生大量 CLOSE_WAIT 状态的原因是什么?

参考面试回答:

🧐TCP 与 UDP专题(重要)🧐

16.  TCP 和 UDP 有什么区别

17. 什么时候用 TCP? 什么时候用 UDP

18. 此时此刻的视频面试用的 UDP 还是 TCP?UDP丢包会有什么现象

19. UDP怎么改造变为可靠传输

20. TCP 是如何保证可靠性的

21. TCP流量控制和拥塞控制的区别

22. 滑动窗口怎么设计的 解决什么问题

23. TCP拆包粘包原因是什么 怎么解决

23.1 什么是TCP粘包

23.2 如何解决TCP粘包

24. TCP的keepalive了解吗? 说一说它和HTTP的keepalive的区别?

25. 一个服务端进程最多可以建立多少条 TCP 连接?

26. 如果已经建立了连接、但是服务端突然出现断电了会发生什么

27. 如果已经建立了连接、但是服务端的进程崩溃会发生什么

28. UDP使用connect和不使用connect有什么区别


1. 面试官:说一下TCP三次握手的过程

参考面试回答:

  • 在第一次握手的时候、客户端会随机生成初始化序号、放到TCP报文头部的序号字段中、同时把SYN标志设置为1 这样就表示SYN报文(这里是请求报文)。客户端将报文放入 TCP 报文首部的序列号字段中。接着把这个SYN报文发送给服务端、之后客户端处于SYN_SENT状态。这是第一次握手。

  • 然后第二次握手的时候:服务端收到SYN报文后、首先服务端也会随机生成初始化序号、放到TCP报文头部的序号字段中、然后对客户端的初始化序号+1作为确认号、放到TCP报文头部的确认应答字段中、并将SYN和ACK标志设置为1,这样就表示SYN-ACK报文、然后把该报文发给客户端、之后服务端处于SYN_RCVD状态。这是第二次握手

  • 最后来到第三次握手的过程:客户端收到服务端SYN-ACK报文后:客户端会回一个ACK确认报文、该报文的确认号是服务端的初始化序号+1、并且ACK标志会设置为1。表示这是一个确认报文。这是第三次握手

  • 之后客户端处于ESTABLISHED状态。

  • 服务端收到ACK确认报文后、服务端也进入处于ESTABLISHED状态。以上就是TCP三次握手的过程。

客户端 服务器 | | | SYN ----------------------> | | | SYN_SENT | | | |  | | | ESTABLISHED ESTABLISHED

补充:TCP三次握手过程的状态变化

初始阶段、客户端和服务端都处于CLOSE状态。

服务端首先通过socket_bind_listen操作进入LISTEN状态、被动等待连接。

第一次握手

客户端通过socket_connect发送SYN包、包含随机生成的初始序列号(client_isn)

并将SYN标志位设置为1。发送后客户端进入SYN_SENT状态。

第二次握手

服务端收到SYN包后、通过accept接受连接、并回复SYN+ACK包。这个包包含:

  • 服务端自己的初始序列号(server_isn)

  • 确认号设为client_isn+1

  • SYN和ACK标志位同时设为1

发送后服务端进入SYN_RCVD状态。

第三次握手

客户端收到SYN+ACK后、回复一个ACK包、确认号为server_isn+1、ACK标志位为1。发送后客户端直接进入ESTABLISHED状态。

服务端收到ACK后、通过accept返回并执行read操作、也进入ESTABLISHED状态。

此时TCP连接建立完成、双方可以开始数据传输

一句话总结一下状态变化

TCP三次握手的状态变化:

客户端从CLOSE经过SYN_SENTESTABLISHED

服务端从CLOSE经过LISTENSYN_RCVDESTABLISHED

2. 为什么需要三次握手 两次不行吗

  • 分析:面试的时候、最好说出两个原因,并且要解释一下为什么2次握手就不行。把面试官当做小白、讲到他理解这个问题的原因。

  • 分析回答:

    • 我的理解主要有两个原因:

      • 第一个原因是、三次握手可以有效防止历史连接的建立、避免资源浪费。假设网络中残留一个序号为90的SYN报文、现在客户端向服务端发起了建立连接的请求、发送了一个序号为100的SYN报文、如果这时候服务端先收到的是序号为90的SYN报文、就代表收到了历史连接、这时候服务端会回复确认号为90+1的SYN-ACK报文、客户端收到后、发现其实自己期望收到的确认号是100+1、而不是90+1、所以会断开连接、并且回RST给服务端、服务端收到RST也就会断开连接了、这样就避免了历史连接的建立。

      • 如果使用两次握手、服务器收到客户端的SYN报文后会立即建立连接。但服务器无法区分该SYN报文是新的连接请求、还是延迟到达的历史报文、如果收到的是历史SYN报文、服务器也会误以为是新的连接请求、并分配资源建立连接、造成资源浪费。这会导致服务器为无效的历史连接分配资源、造成浪费。

      • 如果是三次握手

      • 由于客户端收到的确认号是 91、与期望的 101 不符、客户端意识到这是一个历史连接的响应

      • 客户端发送一个 RST 报文给服务器、告诉服务、这是一个错误的连接、请断开

      • 服务器收到 RST 报文后、会断开连接、释放资源。

  • 第二个原因是、三次握手可以确认客户端和服务端是否同时具备发送和接收的能力。第一次握手代表客户端具有发送能力、当服务端收到第一次握手并且响应了第二次握手、实际上这里就证明了服务端具有发送和接收的能力。客户端收到了第二次握手、然后响应了第三次握手、才代表客户端有接收的能力。如果是两次握手的话、只能证明服务端具有发送和接收能力、以及客户端的发送能力、但是无法证明客户端具有接收的能力。

  • 三次握手的意义:

    • 第一次握手 (SYN): 客户端发送 SYN 报文、表明客户端具有 发送 能力。

    • 第二次握手 (SYN-ACK): 服务器收到 SYN 报文并回复 SYN-ACK 报文、表明服务器具有 接收发送 能力。

    • 第三次握手 (ACK): 客户端收到 SYN-ACK 报文并回复 ACK 报文、表明客户端具有 接收 能力。

    • 通过三次握手可以确保客户端和服务端都具备双向通信的能力。

  • 两次握手的缺陷: 两次握手只能确认服务器的发送和接收能力、以及客户端的发送能力、但无法确认客户端的接收能力。

  • 以上就是我觉得TCP需要三次握手的原因。

参考面试回答:

我的理解主要有两个原因:

1.防止历史连接的建立、避免资源浪费

三次握手可以有效防止历史连接的建立、避免服务器为过期的连接分配资源。

在没有三次握手的情况下、服务器无法区分是新的连接请求还是之前连接的残留报文。

如果客户端发送了一个连接请求、服务器无法判断它是否是一个新的连接、可能会误以为这是一个有效的请求、进而分配资源。

如果连接请求是历史报文、那么服务器响应客户端时、也就是客户端收到第二次握手的时候、客户端会发现确认号与预期不符、并会主动终止连接、发送 RST 报文通知服务器。也就是第三次握手的过程会发送一个RST重置报文给服务器。服务器收到 RST 报文后、会断开连接并释放资源。这样可以避免无效连接占用资源。

2.确保双方都具备发送和接收的能力

第二个原因是:三次握手可以确认客户端和服务端是否同时具备发送和接收的能力。第一次握手代表客户端具有发送能力、当服务端收到第一次握手并且响应了第二次握手、实际上这里就证明了服务端具有发送和接收的能力。客户端收到了第二次握手、然后响应了第三次握手、才代表客户端有接收的能力。如果是两次握手的话、只能证明服务端具有发送和接收能力、以及客户端的发送能力、但是无法证明客户端具有接收的能力。

3. 如果第一次握手丢包、会发生什么

参考面试回答:

如果第一次握手丢包了、达到超时重传的时机的话、会进行重传 SYN 报文、如果重传次数达到最大次数、还是没有收到第二次握手的话、客户端就会断开链接了。

4. 如果第二次握手丢包、会发生什么

分析

第二次握手的 SYN-ACK 报文其实有两个目的:

  • 第二次握手里的 ACK、是对第一次握手的确认报文

  • 第二次握手里的 SYN、是服务端发起建立 TCP 连接的报文

因为第二次握手报文里是包含对客户端的第一次握手的 ACK 确认报文、所以如果客户端迟迟没有收到第二次握手、那么客户端就觉得可能自己的 SYN 报文(第一次握手)丢失了、于是客户端就会触发超时重传机制

重传 SYN 报文。

然后因为第二次握手中包含服务端的 SYN 报文、所以当客户端收到后、需要给服务端发送 ACK 确认报文(第三次握手),服务端才会认为该 SYN 报文被客户端收到了。那么如果第二次握手丢失了、服务端就收不到第三次握手、于是服务端这边会触发超时重传机制,重传 SYN-ACK 报文。

参考面试回答:

我的理解第二次握手 SYN-ACK 报文其实有两个目的。

第一个是报文中的 ACK、是对第一次握手的确认报文、那么当第二次握手的丢失的时候、就会导致客户端长时间没有收到 ACK 而触发超时重传 SYN 报文(也就是会重新触发第一次握手)。

第二个是报文中的 SYN、是服务端发起建立 TCP 连接的报文、那么当第二次握手的丢失的时候、服务端就收不到第三次握手、于是服务端这边会触发超时重传机制、重传 SYN-ACK 报文。(也就是会触发第二次握手)

5. 如果第三次握手丢包、会发生什么

参考面试回答:

我的理解是第三次握手的 ACK 是对第二次握手的 SYN 报文的确认、所以当第三次握手丢失了、如果服务端那一方迟迟收不到这个确认报文、就会触发超时重传机制、重传SYN-ACK报文(也就是会触发第二次握手)、直到收到第三次握手、或者达到最大重传次数、这样就会断开连接。

6. TCP的半连接队列和全连接队列是什么

在TCP三次握手过程中、Linux内核维护两个队列:

  • 半连接队列(也称SYN队列):保存收到客户端SYN请求但连接尚未完成的状态

  • 全连接队列(也称accept队列):保存已完成三次握手的连接

连接建立流程:

  1. 服务器收到客户端的SYN请求后,内核会把连接信息存入半连接队列中

  2. 然后服务端对客户端响应SYN+ACK

  3. 然后客户端返回ACK

  4. 服务端收到第三次握手的ACK后、内核会把连接从半连接队列移除

  5. 然后创建新的完全连接并添加到accept队列

  6. 等待进程通过accept函数取出连接

参考面试回答:

  • 半连接队列: 服务端收到客户端发起的 SYN 请求后、内核会把未完成握手的连接存储到半连接队列、等待完成三次握手后转移到全连接队列。

  • 全连接队列: 服务端收到第三次握手的 ACK 后、内核会把连接从半连接队列移除、然后创建新的完全的连接、并将其添加到全连接队列、等待进程调用 accept 函数时把连接取出来。

7. 介绍一下TCP四次挥手

分析:

  • 问题:TCP四次挥手的过程

  • 客户端FIN->服务端ACK ->服务端FIN ->客户端ACK

  • 分析:要把每个阶段的TCP状态说出来。双方都都可以主动断开连接,下图是客户端主动断开连接的过程:

    • 客户端打算关闭连接、此时会发送一个FIN报文、之后客户端进入FIN_WAIT_1状态。

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

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

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

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

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

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

  • 总结:以上就是四次挥手的过程、每个方向都需要一个FIN和一个ACK、因此通常被称为四次挥手。

参考面试回答:

  1. 第一次挥手:客户端打算关闭连接、此时会发送一个FIN报文、用来关闭客户端到服务端的数据传送、并进入FIN_WAIT_1状态。 FIN报文会携带一个序列号。

  2. 第二次挥手: 服务端收到FIN报文后、就向客户端发送ACK应答报文、确认收到了客户端的关闭请求。 然后服务端进入CLOSE_WAIT状态。

  3. 客户端收到服务端的ACK应答报文后、进入FIN_WAIT_2状态、等待服务端发送FIN报文。此时客户端到服务端的连接已经释放、客户端不再发送数据、但服务端仍然可以向客户端发送数据。

  4. 第三次挥手: 等服务端处理完数据后、也向客户端发送FIN报文、用来关闭服务端到客户端的数据传送、并进入LAST_ACK状态。

  5. 第四次挥手: 客户端收到服务端的FIN报文后、回一个ACK应答报文、确认收到了服务端的关闭请求。 客户端进入TIME_WAIT状态。

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

  7. 客户端在TIME_WAIT状态经过2MSL(Maximum Segment Lifetime)一段时间后、自动进入CLOSE状态、至此客户端也完成连接的关闭。

8. 为什么 TCP 需要四次挥手 三次挥手不行吗

分析

要重点表述四次握手的原因是、TCP 是全双工的协议、需要双方确认自己不再发送数据的时候,才会主动发送 FIN 报文。

  1. 第一次挥手 (FIN、 客户端 -> 服务器): 客户端完成数据发送后、发送一个FIN报文、表示客户端不再发送数据。 这仅仅是客户端单方面不再发送数据、但客户端仍然可以接收来自服务器的数据。

  2. 第二次挥手 (ACK、服务器 -> 客户端): 服务器收到客户端的FIN报文后、立即发送一个ACK报文作为确认。 这个ACK报文表示服务器已经收到客户端的关闭请求。 此时客户端到服务器方向的连接已经释放、客户端不再发送数据。 但是服务器仍然可以向客户端发送数据。 服务器进入 CLOSE_WAIT 状态、等待应用程序决定何时关闭连接。

  3. 第三次挥手 (FIN、 服务器 -> 客户端): 服务器完成所有数据的发送后、发送一个FIN报文、表示服务器不再发送数据

  4. 第四次挥手 (ACK、客户端 -> 服务器): 客户端收到服务器的FIN报文后、发送一个ACK报文作为确认。 客户端进入 TIME_WAIT 状态、等待 2MSL 时间、然后进入 CLOSED 状态。 服务器收到ACK后、进入 CLOSED 状态。

参考面试回答:

我的理解是 TCP 是全双工协议、双方都具备发送和接收的能力、那么在断开连接的时候、要确保双方能发送完自己的数据。

当客户端发送第一次挥手后、也就是 FIN 报文的时候、其实就代表客户端告诉服务端我不会再发送数据了、服务端收到后、服务器收到客户端的 FIN 报文时、内核会马上回一个 ACK 应答报文、但是服务端应用程序可能还有数据要发送、所以并不能马上发送 FIN 报文、而是将发送 FIN 报文的控制权交给服务端应用程序、如果服务端应用程序有数据要发送的话、就发完数据后、才调用关闭连接的函数。所以第二次挥手和第三次挥手通常不会合并一起发送、而是分开发送、所以就需要四次挥手。

如果只有三次挥手、服务端在收到客户端的FIN后、必须立即发送FIN+ACK。

那么就可能出现一方还有数据没有发送完就被迫关闭连接的情况。这些数据将会丢失、导致连接非正常关闭。

9. TIME_WAIT 是如何产生的

分析

很多同学以为 TIME_WAIT 状态是发生在客户端、其实不是的、准确来说 TIME_WAIT 状态发生在主动关闭连接的一方、客户端和服务端都可以作为主动关闭连接的一方。

参考面试回答:

当TCP连接的主动关闭方关闭连接、与被动关闭方进行了四次挥手的时候、在主动关闭方发送完第四次挥手后、也就是最后一个 ACK 报文后、主动关闭方的 TCP 连接就会进入到 TIME_WAIT 状态

这个状态会持续 2MSL的时长、以确保对方已经收到了最后一个 ACK 报文

10. TCP的四次挥手为什么要有TIME_WAIT

本质上就是为什么要有TIME_WAIT 与下面的这道题问的是相同的

参考面试回答:

因为通过TIME_WAIT可以可靠地终止 TCP 连接:确保TCP连接终止

  • 在主动关闭连接的一方(通常是客户端)发送最后一个 ACK 报文后、进入 TIME_WAIT 状态。

  • 这个 ACK 报文可能因为网络原因丢失。如果 ACK 丢失、被动关闭的一方(服务器)会重传 FIN 报文。

  • TIME_WAIT 状态的存在、使得主动关闭方在 TIME_WAIT 状态等待期间会重新发送 ACK 报文、确保被动关闭方能够收到 FIN 报文的确认,从而可靠地关闭连接。

  • 如果主动关闭方不等待 TIME_WAIT、直接关闭连接、那么它就无法重传 ACK 报文、导致被动关闭方无法正常关闭连接、可能会出现连接半开的状态。

以上就是我对 TIME_WAIT 状态的理解。

11. 为什么 TIME_WAIT 状态要等待 2MSL

参考面试回答:

我的理解主要有两个原因:

  • 2MSL 的作用: 2MSL (Maximum Segment Lifetime) 是 TCP 报文在网络中存活的最长时间。 等待 2MSL 确保了:

  • 第一个原因是避免新连接收到旧连接的数据:

      1. 主要是为了避免本次连接的历史报文被新的连接接收到这些历史报文 、从而导致出错。MSL 表示 TCP 报文在网络中最大的存活时长、等待 2MSL 就可以让两个方向的报文可以在网络中自然消失、 一个 MSL 保证了从主动关闭方发出的最后一个 ACK 报文在网络中消失、另一个 MSL 保证了从被动关闭方重传的 FIN 报文在网络中消失、这样新的连接就不会接收到历史报文了。

      2. 第二个是可以可靠地终止 TCP 连接:

        1. 是为了确保第四次挥手 ACK 报文能被接收、从而帮助被动关闭方正常关闭连接。如果主动关闭方的第四次挥手的 ACK 报文丢失了、由于被动关闭方没有收到这个 ACK 报文、被动关闭方它会超时重传 FIN 包、主动关闭方 TIME_WAIT 状态等待期间会重新发送 ACK 报文、如果没有 TIME_WAIT 状态、那么就没办法重新发送 ACK 报文、也就没办法帮助被动关闭方正常关闭连接了。

        以上就是我对 TIME_WAIT 状态的理解。

        12. TIME_WAIT 过多有什么危害

        参考小林:

        4.1 TCP 三次握手与四次挥手面试题

        分析

        过多的 TIME-WAIT 状态主要的危害有两种:

        • 第一是占用系统资源、比如文件描述符、内存资源、CPU 资源、线程资源等

        • 第二是占用端口资源、端口资源也是有限的、一般可以开启的端口为 32768~61000、也可以通过 net.ipv4.ip_local_port_range 参数指定范围。

        客户端和服务端 TIME_WAIT 过多、造成的影响是不同的

        所以要从客户端过多 TIME_WAIT 状态和服务端过多 TIME_WAIT 状态来回答。

        参考面试回答:

        客户端和服务端 TIME_WAIT 过多、造成的影响是不同的。

        • 如果客户端(主动发起关闭连接方)的 TIME_WAIT 状态过多:

        就会占满了所有端口资源、端口是有数据限制的、那么就无法对「目的 IP+目的 PORT」都一样的服务器发起连接了。在四元组确定一个TCP连接的场景下、只有客户端端口的端口号是变量、其他三个都是固定的、那么随着 time_wait 连接越多、占用的端口号就越多、客户端的端口号就会成为受限。

        不过即使是在这种场景下、只要连接的是不同的服务器端、端口是可以重复使用的、所以客户端还是可以向其他服务器端发起连接的、这是因为内核在定位一个连接的时候、是通过四元组(源IP、源端口、目的IP、目的端口)信息来定位的、并不会因为客户端的端口一样、而导致连接冲突。

        • 如果服务端(主动发起关闭连接方)的 TIME_WAIT 状态过多:

        它并不会导致端口资源受限、因为服务端只监听一个端口、而且由于一个四元组唯一确定一个 TCP 连接、因此理论上服务端可以建立很多连接、但是 TCP 连接过多、会占用系统资源、比如文件描述符、内存资源、CPU 资源、线程资源等。

        13. 怎么解决 TIME_WAIT 状态过多的问题

        分析

        可以分客户端和服务端来回答

        • 客户端:开启tcp_tw_reuse参数、客户端在调用 connect() 函数时、内核会随机找一个 time_wait 状态超过 1 秒的连接给新的连接复用

        • 服务端:服务端不主动断开 tcp 连接、将断开 tcp 连接的主动权交给客户端

        参考面试回答:

        • 如果是客户端有大量 time_wait状态、可以考虑开启 tcp_tw_reuse参数、当发起新连接的时候、会复用处于 time_wait 状态的连接

        • 如果是服务端的话、尽量让主动断开连接的方式、由客户端来进行、可以在应用层设计一个逻辑、当服务端要断开连接的时候、发送一个消息给客户端、客户端收到之后、由客户端来断开 tcp 连接、这样服务端就不会有 time_wait状态了

        14. 服务端产生大量 TIME_WAIT 状态的原因是什么?

        分析

        主动关闭方才会有 TIME_WAIT 状态,所以就要分析服务端为什么会主动关闭连接?

        • 第一个场景:HTTP 没有使用长连接

        • 第二个场景:HTTP 长连接的请求数量达到上限

        参考面试回答:

        主动关闭方才会有 TIME_WAIT 状态、当服务端有大量 TIME_WAIT 状态的连接的时候、代表服务端主动关闭了连接

        我觉得可能的原因是:

        • HTTP 没有使用长连接:这时候客户端和服务端都是短连接、服务端发送完HTTP 响应后、就会主动关闭了连接。如果有大量的短连接过来、那么服务端就会出现大量的 TIME_WAIT 状态、如果是这个问题的话、解决方式就是让客户端和服务端都开启 HTTP Keep-Alive 机制。

        • HTTP 长连接的请求数量设置得过小:Web 服务端通常会有个参数、来定义一条 HTTP 长连接上最大能处理的请求数量、当超过最大限制时、就会主动关闭连接。比如 nginx 的 keepalive_requests 这个参数、默认是 100、意味着每个 HTTP 长连接最多只能跑 100 次请求、因为当 QPS 请求很高的时候、nginx 就会频繁的关闭连接、那么此时服务端上就会出大量的 TIME_WAIT 状态、解决的方式也很简单、调大 nginx 的 keepalive_requests 参数就

        15. 服务端产生大量 CLOSE_WAIT 状态的原因是什么?

        分析

        CLOSE_WAIT 状态是「被动关闭方」才会有的状态、而且如果「被动关闭方」没有调用 close 函数关闭连接、那么就无法发出 FIN 报文、从而无法使得 CLOSE_WAIT 状态的连接转变为 LAST_ACK 状态。

        所以当服务端出现大量 CLOSE_WAIT 状态的连接的时候、说明服务端的程序没有调用 close 函数关闭连接。

        参考面试回答:

        我觉得问题原因在于、是服务端没有及时调用 close 关闭连接的函数、导致出现大量 CLOSE_WAIT 状态的连接

        因为只有正确调用了 close 关闭连接函数的时候、TCP 连接状态才会有从 CLOSE_WAIT 状态变为 LAST_ACK 状态。

        我觉得大概率服务端的代码的问题、这时候我们需要针对具体的代码一步一步的进行排查和定位、主要分析的方向就是服务端为什么没有调用 close

        🧐TCP 与 UDP专题(重要)🧐

        16.  TCP 和 UDP 有什么区别

        分析
        可以从几个角度分析: 连接、可靠性、传输方式
        最后总结,这两个协议的优劣势

        参考面试回答:
        我觉得主要有这几个方面的区别:

        •  TCP 是面向连接的协议、在发送数据的时候,需要先建立 TCP 三次握手。而 UDP 无连接的协议、直接就可以发送数据。
        • TCP 会通过超时重传、流量控制、拥塞控制、滑动窗户口保证数据的可靠传输。而 UDP 并没有这些特性、UDP 不考虑数据的可靠性。
        •  TCP 发送的数据是以字节流的形式,没有边界。而 UDP 是一个一个包的发送、是没有消息边界的。

        所以综合来看:

        • TCP 的优势在于可以保证数据的可靠性、是缺陷就是实时性没有 UDP 协议好。
        • UDP 的优势在于足够简单、不用建立连接、数据直接丢过去即可、并且 UDP 包头比 TCP 包头小很多、所以 UDP 实时性和速度方面是比 TCP 好的。
        • TCP 是面向流的协议: TCP 协议将数据视为无结构的字节流、没有明确的消息边界。
           

        17. 什么时候用 TCP? 什么时候用 UDP

        分析

        考察 UDP 和 TCP 应用场景

        • TCP 传输速度慢,但是数据传输可靠性高

        • UDP 传输速度快,但是不保证传输的可靠性

        参考面试回答:

        如果主要关注数据接收的可靠性和顺序、可以选使用 TCP、比如FTP协议、HTTP协议都是基于 TCP 协议进行传输数据。

        如果主要关注的是速度和实时性、而且并不在意某些数据包的丢失、可以选使用 UDP 协议、比如直播、视频会议场景。

        18. 此时此刻的视频面试用的 UDP 还是 TCP?UDP丢包会有什么现象

        分析

        考察 UDP 和 TCP 应用场景

        参考面试回答:

        我觉得是UDP协议。因为视频会议这个场景下、重要的是实时性、UDP协议实时性比TCP好。采用UDP协议传输音视频数据的话、如果发生了丢包、只是就丢失某一瞬间的画面和语音、然后还可以继续进行会议沟通、不会太影响视频会议的体验

        如果是采用 TCP协议的、由于 TCP是可靠传输、如果发生了丢包、可能画面就卡住不动、等丢包重传才会推进画面这样实时性就比较差了。

        19. UDP怎么改造变为可靠传输

        分析

        按照 TCP 协议怎么实现可靠传输的方式、在应用层实现一遍就好了、最后可以补充说明基于 UDP 协议实现的可靠传输相比于 TCP 有什么优势。

        参考面试回答:

        • 我会在应用层增加序列号字段 用来确保UDP的数据可以按序接收。同时还会增加确认号 用来实现超时重传机制、当超过一定时间内没收到已发送数据的确认号、就重传该数据包。
        • 我还会在应用层开辟一个缓冲区、用来实现滑动窗口、有了滑动窗口这样发送数据可以先批量发送数据、不需要等上一个数据的确认了才能发送、提高了发送速率
        • 同时还可以基于滑动窗口实现流量控制、用来保证发送方能按接收方的接收能力发送数据、避免发送的数据对方接收不了而发生数据丢失。
        • 最后为了保证整个网络的带宽环境、还需要实现拥塞控制、确保发送方的数据、不会占满整个带宽。

        总结就是:增加序列号字段和确认号字段 用于实现超时重传

        增加缓冲区用于实现滑动窗口 基于滑动窗口实现流量控制

        还可以补充一下:

        我觉得 UDP 实现可靠传输相比 TCP 可靠传输有几点优势:

        自定义拥塞控制:应用可以根据自身需求选择合适的拥塞控制算法、而 TCP 采用统一的拥塞控制策略、无法针对不同应用进行优化。

        升级维护灵活:TCP 是在操作系统内核中实现的、升级 TCP 需要更新操作系统,而 UDP 可靠传输是在应用层实现的,协议升级就像更新软件一样简单、无需修改内核。

        支持网络连接迁移:UDP 可靠传输可以通过应用层的 连接 ID 来唯一标识连接、而 TCP 依赖四元组(源 IP、源端口、目的 IP、目的端口)确定连接、一旦四元组信息发生变化、就必须重新建立连接。因此UDP 可靠传输更适用于网络环境动态变化的场景、如移动设备的网络切换。

        20. TCP 是如何保证可靠性的

        分析

        从连接建立、序列号与确认应答、数据包超时重传、滑动窗口机制、流量控制、拥塞控制方面回答。

        参考面试回答:

        • 连接建立:TCP 建立连接的时候、需要三次握手、这个过程可以证明双方都具有发送和接收的能力、并且能避免历史连接的建立。

        • 序列号与确认应答:TCP 头部有序列字段、可以保证数据的有序性、还有确认号、用来确认收到了数据。

        • 数据包超时重传:如果发送方发送的数据、长时间没有收到确认报文,就会触发超时重传、重传丢失的报文。

        • 滑动窗口机制:有了滑动窗口这样发送数据可以先批量发送数据、不需要等上一个数据的确认了才能发送、提高了发送速率

        • 流量控制的机制:发送方会根据接收方的接收窗口来发送数据报文、可以避免发送方发送的数据报文太大、导致接收方接收不了而丢包的问题。

        • 拥塞控制:TCP 有拥塞控制的机制、通过慢启动、拥塞避免、拥塞发生,快速重传和快速恢复等算法调整发送速率来避免网络拥塞。当网络出现拥塞时、TCP 会降低发送速率、以减少网络负载、保证数据的可靠传输。

        21. TCP流量控制和拥塞控制的区别

        分析

        控制的层面不一样、流量控制是端到端、拥塞控制是网络层面的控制。

        参考面试回答:

        这两个机制都是 TCP 协议实现可靠传输的因素、主要区别在于:

        • 流量控制:这是一个端到端的控制机制、目的是防止发送方发送的数据过快、导致接收方处理不过来。这通过滑动窗口机制实现、接收方在ACK报文中告诉发送方自己的接收窗口大小、这样就告诉了发送方可接收的最大数据量。

        • 拥塞控制:这是一个网络层面的控制机制、目的是防止过多的数据包同时在网络中传输、导致网络拥塞。TCP 有拥塞控制的机制、通过慢启动、拥塞避免、超时重传、快速重传和快速恢复等算法调整发送速率来避免网络拥塞。当网络出现拥塞时、TCP 会降低发送速率、以减少网络负载、保证数据的可靠传输。

        所以流量控制关注的是保护接收方不被淹没

        而拥塞控制关注的是保护网络传输不被过载。

        22. 滑动窗口怎么设计的 解决什么问题

        分析

        需要分别说明发送窗口和接收窗口的优势

        参考面试回答:

        发送方和接收方在内核各自都有一个缓冲区、发送缓冲区和接收缓冲区上都各有一个窗口

        发送方的窗口表示可发送的最大数据量、接收方的窗口表示可接收的最大数据量。

        发送方有了发送窗口后:那么发送方可以不用等待已发送数据的确认报文、就可以继续发送下一批数据、提高了发送的速率。总的来说滑动窗口机制允许发送方在等待确认的同时继续发送数据、增加了传输效率。

        接收方有了接收窗口后:可以实现流量控制、把接收方的接收窗口告诉给发送方、让发送方按自己的接收情况来发送数据、避免发送方发送的数据过快、导致接收方处理不过来。

        23. TCP拆包粘包原因是什么 怎么解决

        23.1 什么是TCP粘包

        参考面试回答:

        粘包原因:

        我的理解是 TCP 是一个面向字节流协议,并不关心数据包的边界。

        • TCP 粘包现象是指在使用 TCP 协议进行数据传输时 、由于 TCP 协议是面向流的、没有明确的消息边界、导致发送方发送的多个独立的数据包、在接收方看来可能被合并成一个大的数据包

        • 如果发送的数据包太小、TCP 还有一个 Nagle算法、会把多个小数据包堆积成一个 TCP 段发送、这时候就会感觉数据包是粘合一起发送的

        拆包原因

        如果发送的数据包超过 MSS 大小或者接收窗口大小后、会被拆分多个 TCP 段发送、这时候站在应用层的角度、就是一个完整的数据包被拆开了

        • 当TCP数据大于MSS(最大分段大小)时、会拆分成多个数据包发送

        • 当TCP数据大于发送缓冲区剩余空间时、会拆分发送

        • 当接收方接收能力有限、TCP也会将数据拆分发送

        23.2 如何解决TCP粘包

        参考面试回答:

        实际TCP拆包并不是TCP的问题、只是因为TCP的特性、才出现的现象、所以要解决拆包和粘包、主要是在应用层进行处理的、比方说有这些解决方案:

        1. 固定消息长度的方式:每个数据报文都需要一个固定的长度。当接收方累计读取到固定长度的报文后、就认为已经获得一个完整的消息。当发送方的数据小于固定长度时、则需要空位补齐。但是缺点也非常明显,无法很好设定固定长度的值、如果长度太大会造成字节浪费、长度太小又会影响消息传输,所以在一般情况下消息定长法不会被采用。

        2. 特殊分隔符: 在每个消息的末尾添加一个特殊的分隔符、用于标识消息的结束。接收方读取数据、根据特殊分隔符进行消息拆分、直到遇到分隔符为止。比方说HTTP请求报文就是通过空白行的方式、分隔了请求头和请求体。这种方案需要注意的是分隔符的选择一定要避免和消息体中字符相同、以免冲突。

        3. 消息长度+消息内容的方式消息头中存放消息的总长度、例如使用4字节的int值记录消息的长度、消息体存放实际的二进制的字节数据。接收方在解析数据时、首先读取消息头的长度字段Len、然后紧接着读取长度为Len的字节数据、该数据即判定为一个完整的数据报文。这个方案项目开发中最常用的一种方法、因为使用方式非常灵活、且不会存在消息定长法和特定分隔符法的明显缺陷。当然在消息头中不仅只限于存放消息的长度、而且可以自定义其他必要的扩展字段、例如消息版本、算法类型等。

        24. TCP的keepalive了解吗? 说一说它和HTTP的keepalive的区别?

        分析

        实现的层面不同:

        • HTTP的Keep-Alive,是由应用层(用户态)实现的、称为HTTP长连接

        • TCP的Keepalive,是由TCP层(内核态)实现的、称为TCP保活机制

        参考面试回答:

        • HTTP的Keep-Alive是叫HTTP长连接、该功能是由应用程序实现的、可以使得用同一个TCP连接来发送和接收多个HTTP请求和应答、减少了HTTP短连接带来的多次TCP连接建立和释放的开销

        • TCP的Keepalive是叫TCP保活机制、该功能是由内核实现的、当客户端和服务端双方长达一定时间没有进行数据交互时、内核为了确保该连接是否还有效、就会发送探测报文、来检测对方是否还在线、然后来决定是否要关闭该连接

        25. 一个服务端进程最多可以建立多少条 TCP 连接?

        分析

        TCP 是通过四元组唯一确认一条 TCP 连接的。

        一台服务器最大支持的TCP连接数是多少

        参考面试回答:

        一条 TCP 连接是通过源IP地址、目的IP地址、源端口、目的端口这四个信息唯一确定的。也就是通过四元组确定的

        服务端进程通常是会固定监听一个端口、等待客户端的连接请求、现在假设服务端的 IP 地址和端口号都是不变、那么一个服务端进程最大能支持的理论连接数等于客户端的 IP 数量 * 客户端的端口数量。也就是 2 的 48 次方个连接数量。

        这个只是一个理论值、实际上服务端还会受系统资源的限制、比如文件描述符的最大数量、CPU和内存资源

        26. 如果已经建立了连接、但是服务端突然出现断电了会发生什么

        分析要分情况讨论:

        • 客户端会发送数据的情况

        • 客户端不会发送数据的情况

          • 客户端没有开启 TCP 保活机制的情况

          • 客户端有开启 TCP 保活机制的情况

        参考面试回答:

        首先说一下TCP的保活机制

        • TCP的Keepalive是叫TCP保活机制、该功能是由内核实现的、当客户端和服务端双方长达一定时间没有进行数据交互时、内核为了确保该连接是否还有效、就会发送探测报文、来检测对方是否还在线、然后来决定是否要关闭该连接

        服务端突然断电后:

        • 如果客户端接下来会发送数据、那么由于服务端断电了、客户端已发送的数据就没办法得到确认、于是就会发生超时重传、重传次数达到上限之后、客户端就会断开 TCP 连接了。

        • 如果客户端接下来不会再发送数据、那么要看客户端有没有开启 TCP 保活机制:

          • 如果没有开启的话、客户端的 TCP 状态将一直保持不变

          • 如果有开启了 TCP 保活机制、双方超过一段时间没有进行数据交互的话、内核就会触发 TCP 保活机制、定时发送探测报文、因为服务端断电了、客户端发送的探测报文、都不会得到响应、那么探测次数达到上限、还未收到响应的话、客户端就会断开 TCP 连接了

        27. 如果已经建立了连接、但是服务端的进程崩溃会发生什么

        分析

        TCP 的连接信息是由内核维护的、所以当服务端的进程崩溃后、内核需要回收该进程的所有 TCP 连接资源,于是内核会发送第一次挥手 FIN 报文,后续的挥手过程也都是在内核完成,并不需要进程的参与,所以即使服务端的进程退出了,还是能与客户端完成 TCP 四次挥手的过程。

        参考面试回答:

        服务端进程崩溃的话、内核会负责回收服务端进程的资源、会发送 FIN 报文、和客户端进行四次挥手断开连接。

        28. UDP使用connect和不使用connect有什么区别

        UDP使用connect和不使用connect的区别如下:

        1. 系统调用效率:

        使用connect的UDP套接字可以直接使用write/send函数发送数据、UDP套接字会在内核中记录目标地址,后续发送数据时无需重复指定地址

        2. 错误处理报告:

        • 使用connect的UDP套接字可以接收ICMP错误(如主机不可达等)一旦对UDP套接字调用了connect、当往指定的对等方发送数据时、如果发生传输错误、操作系统会将错误报告给套接字。例如如果目标主机不可达、你可能会收到ECONNREFUSED错误。这在没有调用connect的情况下是不可能的、因为UDP通常不会为无连接的操作提供错误反馈

        • 不使用connect的UDP套接字无法接收这些错误信息

        3. 数据筛选:

        • 使用connect后、套接字只接收来自指定地址的数据包。这意味着该套接字不会接收到其他任何地址的数据、从而相当于为该套接字设置了一个过滤器。

        • 不使用connect时、可以接收任何来源的数据包

        4. 系统调用简化:

        • 使用connect后可以使用send()、write()等发送数据。系统会自动使用connect时指定的地址作为数据的目的地,这使得代码更简洁,因为你不需要每次发送数据时都提供对等方的地址。

        • 未使用connect只能使用sendto()指定目标地址

        5. 多次connect特性:

        • UDP可以多次调用connect、而TCP只能调用一次

        • UDP可以通过重新connect更改目标地址

        • 可以通过connect(AF_UNSPEC)断开连接