计算机网络面试题——第二篇

1. TCP拆包和粘包

  1. 现象
  • 粘包:指在TCP传输中,发送方的多个数据包在接收方被合并在一个包接收,导致多条消息数据粘在一起,接收方无法正确区分这些消息的边界。
  • 拆包:指的是发送方的一个数据包在接收方被分成了多个包接收,导致一条完整的消息被拆分成多个部分,接收方无法一次性接收到完整的数据。
  1. 原因
  • 粘包:主要原因是TCP是面向字节流的协议,他不关心数据边界,数据在发送方可能被一次发送,接收方在读取时可能会将多个消息拼接在一起。
  • 拆包:可能由于网络传输中的MTU(最大传输单元)限制或者发送缓冲区大小限制,一个大包被分成了多个小包传输。
  1. 解决方法
  • 使用定长消息:每个消息都有固定的长度,接收方按照固定长度读取数据。
  • 添加消息分隔符:在每个消息之间添加特定的分隔符,接收方可以通过分隔符来区分消息。
  • 使用消息头:在消息的头部添加一个长度字段,指示消息的长度,接收方根据这个长度来读取相应长度的数据。

2. TCP的三次握手

具体流程为

  1. 客户端首先发送一个SYN消息给服务器
  2. 服务器收到后回复一个SYN-ACK消息
  3. 最后客户端在发送一个ACK消息确认已收到

为什么需要三次握手

有两个原因:

  • 避免历史错误连接的建立,减少通信双方不必要的资源消耗。
  • 帮助通信双方同步初始化序列号。
  1. 为什么三次握手,可以避免历史错误连接的建立
  • 因为网络情况比较复杂,发送方第一次发送请求后,可能由于网络原因被阻塞住了,此时发送方可能又会再次发送请求。
  • 如果握手只有两次,那么接收方应对发送方的请求只能拒绝或者接受,但是他无法识别当前的请求是旧的请求还是新的请求。

    并且 如果网络阻塞时间较长,发送方可能多次发送请求,且接收方还可能全部接受这些连接,这就造成了资源的不必要浪费。
    如果要避免这种情况发生,两次通信是不够的。发送方需要知晓接收方到底接收了哪个链接,如果接受的是老连接,那么发送方需要告知接收方,这个连接不对,也就是RST通知,如果对,那么就返回ACK告知接收方OK。这就使得一次握手至少需要三次。
  1. 帮助通信双方同步初始化序列号
    因为网络本身的不稳定性可能会导致
  • 数据丢失
  • 数据重复传输
  • 数据乱序
    而TCP是一个可靠的传输协议,需要保证数据不丢失且有序的传输,基于上述问题,TCP引入了序列号,他使得
  • 接收方可以根据序列号去重
  • 接收方可以根据序列号排序
  • 发送方针对未接收到ACK序列号对应的数据包,可以重传。
    序列号是有序的,因此在通信的初始阶段,双方就需要同步序列号,不然数据对不上。
  • 发送方通过SYN控制消息并携带自己期望的初始序列号SEQ给接收方
  • 接收方收到SYN消息之后,通过ACK控制消息以及SEQ+1来进行确认,并带上自己的SEQ。
  • 发送方通过ACK控制消息以及接收方的SEQ+1来进行确认,并且还能够在三次握手通信的同时,直接携带数据进行传输。

3. TCP初始序列号ISN怎么取值

初始序列号ISN是以时间戳为基础生成的。

为什么序列号不能写死,比如从0开始

如果写死一个值,比如0,那么建设已经建立好连接了,client也发了很多比如已经发了20个包了,然后网络断了之后client重新连接,端口号还是之前那个,序列号又从0开始,此时服务端返回第20个包的ack,此时就会乱序。

4. TCP三次握手时,发送SYN之后就宕机了会怎么样

client发送SYN至server后宕机了,此时server发送syn+ack就一直得不到回复,此时会进行阶段性重试,多次重试后还没有收到ACK则断开连接,释放资源。

重试次数由系统参数tcp_synack_retries决定,在linux中默认重试5次,分别间隔为1s、2s、4s、8s、16s。

5. 什么是SYN Flood攻击

SYN Flood是一种拒绝服务攻击(Dos),攻击者通过发送大量的SYN包来耗尽服务器的资源,从而使得服务器无法响应用户的连接请求。

攻击是利用TCP三次握手的机制,攻击者发送大量的SYN包,但是不完成后续的握手步骤,导致服务器在等待未完成连接的状态下耗尽资源。

如何防御SYN Flood攻击

  1. SYN Cookies:根据第一次握手的客户端信息,生成cookies,随着第二次握手返回给客户端,后续客户端第三次握手时携带cookies,最终建连接,这个过程不会使用SYN队列
  2. 缩短连接超时时间
  3. TCP半连接队列扩展:增大服务器的半连接队列,使其能够容纳更多的未完成的连接。

6. 什么是TCP的四次挥手

TCP的四次挥手用于完全关闭一个以建立连接的过程,他确保双方都能完成数据传输并且安全的释放连接资源。

步骤

  1. 第一次挥手(FIN->ACK): 客户端主动关闭连接,发送FIN包,进入FIN_WAIT_1状态,服务器收到fin后,表示不再接受数据,但仍可能继续发送数据。
  2. 第二次挥手(ACK):服务器发送ACK后,确认已经收到FIN。此时服务器进入close_wait状态,客户端进入fin_wait_2状态。
  3. 第三次挥手(FIN->ACK):服务器完成所有数据传输后,发送fin包,进入last_ack状态,客户端收到fin后,准备关闭连接。
  4. 第四次挥手(ACK):客户端发送最后一个ACK包,进入time_wait状态,等待可能迟到的fin包。服务器收到ACK后,关闭连接,进入closed状态。客户端在TIME_WAIT计时结束后,正式关闭连接。

1. 为什么挥手需要四次

主要是为了 确保数据完整性。TCP是一个全双工协议,也就是说双方都要关闭,每一方都向对方发送FIN和响应ACK。

  • 客户端发起连接断开,代表客户端没数据要发送,但是服务器可能还有数据没有返回客户端。
  • 服务器发送完成,发送FIN,客户端再向服务端发送ACK。
    所以,一个FIN+ACK代表一方数据结束传输,因此,需要两对FIN+ACK,加起来就是四次通信。

2.挥手一定要四次吗
不一定,有时候可以变成三次挥手

如果client发送FIN给server的时候,server已经没数据发送给client了,那么Server就可以将ACK和他的fin一起发送给client,这样一来就变成三次挥手。

7. 为什么TCP挥手需要有TIME_WAIT状态

  1. 确保最后的ACK被成功接收
  • 在TCP四次挥手过程中,主动关闭连接的一方在发送最后一个ACK确认后进入TIME_WAIT状态。
  • 如果这个ACK丢失了,另一方(被动关闭连接的一方)没有收到确认包,会重发FIN报文。主动关闭的一方需要在TIME_WAIT状态下保持一段时间,以便能够重发ACK,确保连接能够被正确的关闭。
  1. 防止旧的重复分段干扰新连接
  • TCP连接在关闭后,可能会有一些延迟的或者已经失效的报文还在网络中传输,如果立即重新使用相同的IP地址和端口建立新的连接,可能会受到这些旧报文干扰。
  • TIME_WAIT状态可以确保在旧连接的所有报文都超时失效后,才允许新的连接使用相同的IP地址和端口,从而避免数据混乱。

为什么TIME_WAIT等待的是2MSL

MSL是TCP报文段在网络中可以存活的最大时间。Linux是30s。

为什么设置了2MSL

假设被动关闭方没有客户端的最后一个ACK,此时就会触发超时重发FIN。当客户端收到FIN后,会重发ACK给被动关闭方,这一来一回就需要2MSL时间。

相关推荐
JaguarJack1 天前
FrankenPHP 原生支持 Windows 了
后端·php·服务端
BingoGo1 天前
FrankenPHP 原生支持 Windows 了
后端·php
JaguarJack2 天前
PHP 的异步编程 该怎么选择
后端·php·服务端
BingoGo2 天前
PHP 的异步编程 该怎么选择
后端·php
JaguarJack3 天前
为什么 PHP 闭包要加 static?
后端·php·服务端
ServBay4 天前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户962377954484 天前
CTF 伪协议
php
BingoGo6 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack6 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo7 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php