计算机网络面试题汇总

374 阅读27分钟

“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

计算机网络面试题汇总

OSI 与 TCP/IP篇

img

1.OSI模型或者TCP/IP模型各层的功能,都有哪些协议?

物理层

实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异(比特流)

数据链路层

建立逻辑连接、进行硬件地址寻址、差错校验等功能。将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现就直接丢弃。(帧)

网络层

选择合适的网间路由和交换结点, 确保数据及时传送。协议有:ICMP IGMP IP(IPV4 IPV6) ARP RARP(分组或者包)

传输层

负责向两台主机进程之间的通信提供通用的数据传输服务。协议有:TCP UDP,

会话层

建立、管理、终止会话。对应主机进程,指本地主机与远程主机正在进行的会话

表现层

数据的表示、安全、压缩。格式有,JPEG、ASCll、DECOIC、加密格式等

应用层

通过应用进程间的交互来完成特定网络应用,协议有:HTTP FTP TFTP(文件传输) SMTP(电子邮件传输) SNMP(网络管理) DNS(域名系统) TELNET(远程登录) HTTPS DHCP(动态主机配置)

至于tcp/ip模型你选择OSI模型对应的层去说即可

五层体系结构

七层模型传输数据过程:

img

2.为什么要分层,分层有什么意义?

  1. 各层之间是独立的。某一层可以使用其下一层提供的服务而不需要知道服务是如何实现的。

  2. 灵活性好。当某一层发生变化时,只要其接口关系不变,则这层以上或以下的各层均不受影响。

  3. 结构上可分割开。各层可以采用最合适的技术来实现 。

  4. 易于实现和维护。

  5. 能促进标准化工作。

    与分层体系结构的思想相似的日常生活有邮政系统,物流系统。

TCP篇

1.什么是TCP

TCP是面向连接的,可靠的,基于字节流的传输层通信协议

面向连接的:

⼀定是 「⼀对⼀」 才能连接,不能像 UDP 协议可以⼀个主机同时向多个主机发送消息,也就是⼀对多是无法做到的;

可靠的:

无论的网络链路中出现了怎样的链路变化,TCP 都可以保证⼀个报⽂⼀定能够到达接收端;

基于字节流:

消息是 「没有边界」 的,所以无论我们消息有多大都可以进行传输。并且消息是 「有序的」,当 「前⼀个」 消息没有收到的时候,即使它先收到了后面的字节,那么也不能扔给应用层去处理,同时对 「重复」 的报⽂会自动丢弃。

2.TCP的头格式是什么?

TCP头部格式

image-20210911140927364

序列号(seq)

在建立连接时由计算机⽣成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送⼀次数据,就 「累加」⼀次该「数据字节数」的大小。

用来解决网络包乱序问题

确认应答号(ack)

指下⼀次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数 据都已经被正常接收。

用来解决不丢包的问题。

控制位:

  • ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建⽴连接时的 SYN 包之外该位必 须设置为 1 。

  • RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。

  • SYN:该位为 1 时,表示希望建⽴连接,并在其「序列号」的字段进⾏序列号初始值的设定。

  • FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双⽅的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。

3.为什么需要TCP协议,他工作在哪一层?

网络层是不可靠的,他不能保证网络包的按序交付,也不能保证网络包中的数据完整性!所以就有传输层的TCP协议负责网络数据包的可靠性!

4.tcp是面向连接的,那么怎么确认是唯一的连接?

  • 源地址
  • 目的地址
    • 通过ip协议发送报文给对方主机
  • 源端口
  • 目的端口
    • 告诉TCP协议应该把报文发送给哪一个进程

5.传输层有TCP与UDP协议,他们之间区别是什么,分别应用的场景是哪儿?

TCP与UDP区别:

  1. 连接
    • TCP是面向连接的,传输数据首先要建立连接
    • UDP不需要建立连接,即刻传输数据
  2. 服务对象
    • TCP协议是一对一传输
    • UDP是可以一对一,一对多,多对多的交互通信
  3. 可靠性
    • TCP是可靠交付数据的,数据无差错,不丢失,不重复,按需到达
    • UDP尽最大努力交付,不能保证可靠性
  4. 首部开销
    • TCP首部长度较长一般是20字节
    • UDP首部只有8个字节,固定不变,开销小
  5. 传输方式
    • TCP 是流式传输,没有边界
    • UDP是一个一个包传输的,有边界

TCP与UDP应用的场景:

TCP是面向连接的,具有可靠性的,经常用于

  • FTP文件传输
  • HTTP/HTTPS
  • SMTP邮件传输

UDP是不需要连接,尽最大努力交付的,经常用于:

  • 视频,音频等多媒体通信
  • 游戏网络通信
  • 广播通信

6.简述TCP三次握手的过程

TCP 通过三次握手来建立连接

image.png

三次握手过程如下:

  • 一开始,客户端和服务端都处于CLOSED
  • 先是服务端主动监听某个端口,处于LISTEN ,开始接受第一个报文。
  • 客户端随机初始化序列号(client_isn),同时控制位中的SYN置为1。第一个报文发送到服务端,表示向服务端发起连接请求,之后客户端处于SYN-SENT状态
  • 服务端收到报文后,服务端随机初始化序列号(server_isn),确认应答号(ack)填入client_isn+1 接着SYNACK 都置为1,发送报文到客户端,该报文不包含应用层数据,之后服务端处于SYN-RCVD状态
  • 客户端收到报文后,还要向服务端回应一个应答报文,ACK置为1 ,确认应答号(ack)填入server_isn+1 ,序列号可以填入应用层的数据,之后客户端处于ESTABLISHEN状态
  • 服务器收到客户端的应答报文后,也进入ESTABLISHED状态

温馨提示

第三次握手是可以携带数据的,前面两次的序列号都随机生成,不可以携带数据

那么为什么前两次不能携带数据?

减少服务器被攻击的风险。如果前两次能够携带数据,一旦有人想要攻击服务器,就可以在第一次握手中的SYN的报文放入大量数据,服务器势必会消耗更多的时间和内存空间去处理这些数据,就增加了服务器被攻击的风险。

第三次握手,客户端已经处于ESTABLISHED状态,可以接受服务器的报文,相对安全

参考博客:

blog.csdn.net/King___Ding…

7.为什么是三次握手,不是两次,或者四次呢?

比较常回答的是“因为三次握手才能保证双方具有接收和发送的能⼒。”

但是这回答比较片面,,没有说到点子上,面试官不愿听

接下来以三个方面分析三次握手的原因:

  • 三次握手才可以阻止重复历史连接的初始化(主要原因)
  • 三次握手才可以同步双方的初始序列号
  • 三次握手才可以避免资源浪费

原因一:避免历史连接

img

三次握手的首要原因是为防止旧的重复连接引起连接初始化混乱的问题。

三次握手避免历史连接过程如下:

  • 一个旧的SYN报文会比新的SYN报文先到服务端
  • 服务端返回一个SYN+ACK报文到客户端
  • 客户端通过自身的上下文,判断此连接是否是一个历史连接?
    • 如果是,客户端返回一个RET报文,终止此次连接,等待正确的SYN+ACK报文
    • 如果不是,则建立连接

如果是两次握手连接,没有足够的上下文去判断当前连接是否是历史连接,三次握手则可以在客户端(发送方)准备发送第三次 报⽂时,客户端因有足够的上下文来判断当前连接是否是历史连接:

原因二:同步双方初始序列号

序列号是可靠传输的一个关键因素,他的作用如下:

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

img

四次握手也可以同步初始序列号,但是第二步与第三步可以优化成一步

两次握手只能保证一方那个的初始序列号被对方成功接受,不能保证双方的初始序列号都能确认接受

原因三:避免资源浪费

img

如果是两次握手连接,在网络情况复杂的情况下,客户端的SYN 阻塞了,重复发送多次SYN报文,那么服务器在收到请求后就会建立多个冗余无效的连接,造成不必要的资源浪费

小结:

TCP建立连接,通过三次握手的原因:

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

不使用两次握手的原因:

无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;

不使用四次握手的原因:

三次握手是理论上最少次数可靠连接建立,不需要使用更多的通信次数,节约资源。

8.刚才你提到前两次握手时随机生成的序列号,为什么要随机生成,为什么客户端和服务端初始序列号是不同的?

如果⼀个已经失效的连接被重新建立起来了,但是该旧连接的历史报文可能还残留在网络中(还没有到达接收方),如果序列号相同,那么就无法分辨 出该报文是不是历史报文,如果历史报文被新的连接接收了,则会产生数据错乱。

所以,每次建立连接前重新初始化⼀个序列号主要是为了通信双方能够根据序号将不属于本连接的报文段丢弃。

另一方面时为了安全性,防止黑客伪造相同序列号TCP报文被对方接收

参考图:

这里写图片描述

9.简述TCP的四次挥手的过程

img

  • 客户端打算关闭连接,此时会发送一个FIN置为1的TCP报文,即FIN报文,之后客户端进入FIN_WAIT_1状态
  • 服务端收到此报文后,会回复一个ACK确认应答报文,之后服务端进入CLOSED_WAIT状态
  • 客户端收到服务端发过来的ACK应答报文,之后进入FIN_WAITE_2状态
  • 等待服务端处理完数据后,也向客户端发送FIN报文之后服务端进入LAST_ACK状态
  • 客户端收到服务端的FIN报文后,向服务端发送ACK确认应答报文,之后进入TIME_WAIT状态
  • 服务端收到客户端发送的ACK确认应答报文后,就直接进入CLOSE状态,至此服务端已经完成连接关闭
  • 客户端经过2MSL一段时间后,自动进入CLOSED状态,客户端完成连接关闭

10.为什么挥手需要四次,不能三次吗?

  • 关闭连接时,客户端会发送一个FIN报文,仅仅表示客户端不再发送数据,但是可以接受数据
  • 服务端接受后,返回一个ACK确认应答报文,可能服务端还有数据需要处理和发送,等待服务端不再发送数据时候,才发送FIN报文,表示同意关闭连接

有上面过程可知,服务端需要将未处理完的数据,进行处理以及发送。所以服务端的FINACK报文分开发送,而不是一起发送.

11.为什么需要TIME_WAIT状态

根据第三版《UNIX网络编程 卷1》2.7节,TIME_WAIT状态的主要目的有两个:

  • 优雅的关闭TCP连接,也就是尽量保证被动关闭的一端收到它自己发出去的FIN报文的ACK确认报文;

img

  • 处理延迟的重复报文,这主要是为了避免前后两个使用相同四元组的连接中的前一个连接的报文干扰后一个连接。

img

12.为什么TIME_WAITE需要2MSL,不是更多呢?

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

MSLTTL的区别:

  • MSL 的单位是时间
  • TTL是经过路由跳数

所以MSL应该要大于等于TTL消耗为0的时间,以确保报文已被自然消亡

当客户端发送最后一个ACK报文,服务端处于LAST_ACK状态,在收到最后一个ACK之前它会一直重传FIN报文直至超时。那么客户端发送到服务端的ACK报文有两种结局

  • ACK在网络中丢失,我觉得情况不需要考虑,因为tcp是可靠连接,因为除非多次重传失败,否则AB两端的状态不会发生变化直至最后一个ACK不再丢失。
  • ACK报文被B接收到。我们假设A发送了ACK报文后过了一段时间t之后B才收到该ACK,则有 0 < t <= MSL。因为A并不知道它发送出去的ACK要多久对方才能收到,所以A至少要维持MSL时长的TIME_WAIT状态才能保证它的ACK从网络中消失。同时处于LAST_ACK状态的B因为收到了ACK,所以它直接就进入了CLOSED状态,而不会向网络发送任何报文。所以晃眼一看,A只需要等待1个MSL就够了,但仔细想一下其实1个MSL是不行的,因为在B收到ACK前的一刹那,B可能因为没收到ACK而重传了一个FIN报文,这个FIN报文要从网络中消失最多还需要一个MSL时长,所以A还需要多等一个MSL。

综上所诉TIME_WAIT等待2倍的MSL比较合理解释:

第一个MSL是为了等自己发出去的最后一个ACK报文从网络消失,第二个MSL是对了等服务端收到ACK之前的一刹那可能重传的FIN报文从网络消失

虽然说维持TIME_WAIT状态一段时间有2个目的,但这段时间具体应该多长主要是为了达成上述第二个目的而设计的。

为什么不是 4 或者 8 MSL 的时长呢?

你可以想象一个丢包率达到百分之一的糟糕网络,连续两次丢包的概率只有万分之一,这个概率实在是太小了,忽略它比解决它更具性价比。

13.TCP如何保证可靠传输

是通过序列号、确认应答、重发控制、连接管理以及窗⼝控制等机制实现可靠性传输的。

14.简单讲讲TCP常见的重传机制

TCP 实现可靠传输的方式之一,是通过序列号与确认应答。

超时重传

重传机制的其中一个方式,就是在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据,也就是我们常说的超时重传

发生超时重传的情况如下:

  • 数据包丢失
  • 确认应答ACK丢失

超时时间设置为多少

RTT

RTT 就是数据从网络一端传送到另一端所需的时间,也就是包的往返时间。

超时重传时间是以 RTO (Retransmission Timeout 超时重传时间)表示。

超时时间较长与较短

  • 当超时时间 RTO 较大时,重发就慢,丢了老半天才重发,没有效率,性能差;
  • 当超时时间 RTO 较小时,会导致可能并没有丢就重发,于是重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。

根据上述的两种情况,我们可以得知,超时重传时间 RTO 的值应该略大于报文往返 RTT 的值

实际上「报文往返 RTT 的值」是经常变化的,因为我们的网络也是时常变化的。也就因为「报文往返 RTT 的值」 是经常波动变化的,所以「超时重传时间 RTO 的值」应该是一个动态变化的值

因为是以时间为驱动,会存在一个问题:seq=3 的确认报文没有接收到,客户端会死等 3 的确认报文,直到超时,那么是再次发送报文 seq=3,还是把 seq=3,4,5都发送呢? 其实这两种方式都不好,因为你会去等 timeout 时间,timeout 是变化的,就可能会逐渐变长。

快速重传

快速重传,他与超时重传不同,他不是以时间为驱动,而是以数据(确认应答)驱动重传。

快速重传机制

发送方发出1,2,3,4,5份数据

  • 第一份 seq1 先送到了,ack 返回2
  • seq2 没有送到接受方
  • seq3,seq4,seq5 都送到了,但是ack都返回2
  • 发送端接受到了三个ack=2,知道seq2对方没有收到,就重传seq2
  • 最后,接收方收到seq2 ,又因为seq3,seq4,seq5收到了,返回ack=6

其实快速重传有一个问题:

当我收到了三个返回 ack 的报文,就面临一个问题,我是传一个还是后面的全部重传。对于发送方来说,**不知道这三个 ack 对应的哪些报文啊?**可能在上图中我知道是 seq=3,4,5,但是如果我需要传 20 个报文呢,可能接收方收到的 seq=1,seq=6,seq=10,seq=20 这些报文,接收方还是会收到三次的第 2 次数据对应的ACK 报文。发送方收到之后,就发送一个 seq=3 的报文,接下来两边就不会再发送数据了,陷入了等待。

因为对于发送方来说,seq=3 报文,我发送了 啊。对于接收方来说,我确实没有接收到 3 但是 20 个包已经发送完了啊,接收方对于第三次数据怎么去发送三个 ACK 包呢!

SACK

为了解决这个问题,出现SACK机制(选择性确认)

选择性确认

当发送方接受到有三次同样的ACK报文,它可以根据SACK缓冲区的记录,告诉你我收到了哪些,发现 200-299 丢失,那么他就可以选择 seq2 进行重发

D-SACK

D-SACK是对SACK进行一个扩展,主要使用SACK告诉发送方有哪些数据被重复接受了!

  • 发送方能够知道是发出去的包丢了还是接收方发送的ACK丢了
    • SACK若告知这个包已被发送过,那么说明是接收方发送的ACK丢了
  • 发送方可以知道发出去的数据包是否被网络延迟了
    • 发送方在延迟之后会重发数据包,之前的数据包若一段时间后到达了接收方,接收方返回的应答报文中能看到该数据包已经被接收了,是个重复的报文,说明被网络延迟了,而重发的数据包已被收到

为什么要知道这些数据?理由:知道这些东西可以很好得帮助TCP了解网络情况,从而可以更好的做网络上的流控

15.为什么引入滑动窗口?

我们都知道,TCP必需要解决的可靠传输以及包乱序(reordering)的问题,所以,TCP必需要知道网络实际的数据处理带宽或是数据处理速度,这样才不会引起网络拥塞,导致丢包。

引入滑动窗口来做网络的流控,尽可能地保证网络流畅,不会发生拥塞。

滑动窗口的工作流程

img

16.既然说到了窗口,那么大小是怎么决定的?

在TCP头部格式中有一个窗口大小的字段

这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。

所以窗口大小由接收方决定。

17.流量控制,当接收方的窗口为0时,他有一个潜在的死锁现象,你知道吗?简单描述下!

可能存在的死锁现象

  • 当接收方的窗口为0时候,会发送一个报文告知发送方此时的窗口为0
  • 当接收方处理完数据后,此时窗口不为0 ,会发送一个窗口非0的报文
  • 如果这个报文在网络中丢失,那么就会出现死锁的情况
  • 接收方在等待发送方发送数据
  • 发送方在等待接收方告知他的窗口不为0

窗口关闭潜在的危险

18.死锁现象怎么解决

当发送方接受到了一个窗口为0 的通知后,会启动持续计时器

持续计时器超时时候,发送方会发送一个窗口探测的报文,接收方收到这个报文后,会返回当前的窗口大小。

窗口探测

窗口探测

  • 如果接收窗口仍然为 0,那么发送方就会重新启动持续计时器;
  • 如果接收窗口不是 0,那么死锁的局面就可以被打破了。

19.你了解糊涂窗口综合症吗?

症状是:

当接收方窗口内的数据,来不及处理,缓存在窗口中,那么会导致发送方的窗口越来越小,甚至发生发送方发送的报文数据只有几个字节的情况。

糊涂在哪:如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节,这就是糊涂窗口综合症

糊涂窗口综合症

从图上可以看出解决方法有两种:

  • 接收方不告知小窗口(窗口大小,很小达几个字节)
    • 当窗口长度小到一定程度,就发送窗口为0的报文
  • 发送方不发送数据少的报文
    • 等待接收方告知的报文窗口大小达到一定的字节就发送数据,不然就一直囤积数据

20.有了流量控制,为什么TCP可靠性还需要拥塞控制?

流量控制是对发送方和接收方两者的发送数据量的一个束缚,但是大量的计算机都是在一个网络环境里面,如果对发送到环境内的报文不进行约束,那么网络就会出现堵塞,极易导致发送的数据包出现丢失,时延等问题,如果TCP再发生重传,网络负担就会更重,于是会导致更多的丢包,更长的时延,恶性循环下去。

于是,就有了拥塞控制,控制的目的就是避免「发送方」的数据填满整个网络。

为了限制发送方发送到网络中的数据量,定义了一个叫做「拥塞窗口」的概念。

如果报文发生了丢失,时延,那么就代表网络处于拥塞的状态。

21.拥塞窗口与发送窗口有什么关系?

发送方发送的报文,不能仅仅考虑到对面接收方的窗口大小,还要考虑到网络环境的情况。

为了报文减少丢失,时延,更可靠的到达接收方,发送窗口的值取得是拥塞窗口和接收窗口中的最小值

22.拥塞控制的原理是什么?

拥塞控制主要是四个算法:

  • 慢启动
  • 拥塞避免
  • 快重传
  • 快恢复

慢启动

慢启动的规则是当发送方收到一个ACK,拥塞窗口以指数级别的上升。

可能你觉得这是慢启动吗?

是的,慢启动是一点点的去提高发送包的数量,刚开始很慢的启动,先开始发送的数量是1,逐渐慢慢提高到慢启动门限

慢启动算法

慢启动的发送包的数量不是一直提高,有一个叫慢启动门限的状态变量

  • cwnd < ssthresh 时,使用慢启动算法。
  • cwnd >= ssthresh 时,就会使用「拥塞避免算法」。

拥塞避免

当拥塞窗口超过慢启动门限时候,就会启动拥塞避免

拥塞避免的规则是每当收到一个ACK时候,拥塞窗口+1,直到出现丢包或者超时的情况,避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。

拥塞避免

快重传和快恢复

当发生拥塞情况,TCP就会启动快重传和快恢复机制。

当发生了超时重传情况

  • ssthresh (慢启动门限)设为 cwnd/2
  • cwnd (拥塞窗口)重置为 1

然后重新开始慢启动。

拥塞发送 —— 超时重传

这种方式比较激烈,丢了包就一切重来,十分不利于网络的稳定传递

现在的TCP控制算法就是当收到三个重复确认ACK时,TCP开启快速重传 Fast Retransmit 算法,而不用等到RTO超时再进行重传

  • cwnd 大小缩小为当前的一半
  • ssthresh 设置为缩小后的 cwnd 大小
  • 然后进入快速恢复算法 Fast Recovery

当发送方收到3个重复的ACK的情况

  • cwnd = cwnd/2 ,也就是设置为原来的一半;
  • ssthresh = cwnd;
  • 进入快恢复算法
    • 拥塞窗口 cwnd = ssthresh + 3 ( 3 的意思是确认有 3 个数据包被收到了);
    • 重传丢失的数据包;
    • 如果再收到重复的 ACK,那么 cwnd 增加 1;
    • 如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,
    • 该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态;

快速重传和快速恢复

补充知识

介绍UDP

UDP 不提供复杂的控制机制,利⽤ IP 提供⾯向**「⽆连接」**的通信服务。

UDP 的头部格式如下:

image-20210911144021592

发送方的滑动窗口是怎么样的过程

img

  • #1 是已发送并收到 ACK确认的数据:1~31 字节
  • #2 是已发送但未收到 ACK确认的数据:32~45 字节
  • #3 是未发送但总大小在接收方处理范围内(接收方还有空间):46~51字节
  • #4 是未发送但总大小超过接收方处理范围(接收方没有空间):52字节以后

当发送方把数据全部发送出去,那么可用窗口大小为0,如果没有收到ack确认报文,则无法发送数据

可用窗口耗尽

当收到了部分数据的ack报文时候,如果发送窗口大小没有发生变化,那么滑动窗口往右移动一定字节。接下来会有一部分数据变成可发送的窗口,如果发送变化,那么移动就会更多点。

32 ~ 36 字节已确认

接收方的滑动窗口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qqtk6Trs-1663416322881)(cdn.jsdelivr.net/gh/baici1/i…)]

  • #1 + #2 是已成功接收并确认的数据(等待应用进程读取);
  • #3 是未收到数据但可以接收的数据;
  • #4 未收到数据并不可以接收的数据;

如果发送方发送了,大量的数据,那么接受窗口为0,等待处理完成后,会逐渐恢复接受窗口的大小。

流量控制过程

TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。

下面举个栗子,为了简单起见,假设以下场景:

  • 客户端是接收方,服务端是发送方
  • 假设接收窗口和发送窗口相同,都为 200
  • 假设两个设备在整个传输过程中都保持相同的窗口大小,不受外界影响

流量控制

根据上图的流量控制,说明下每个过程:

  1. 客户端向服务端发送请求数据报文。这里要说明下,本次例子是把服务端作为发送方,所以没有画出服务端的接收窗口。
  2. 服务端收到请求报文后,发送确认报文和 80 字节的数据,于是可用窗口 Usable 减少为 120 字节,同时 SND.NXT 指针也向右偏移 80 字节后,指向 321,这意味着下次发送数据的时候,序列号是 321。
  3. 客户端收到 80 字节数据后,于是接收窗口往右移动 80 字节,RCV.NXT 也就指向 321,这意味着客户端期望的下一个报文的序列号是 321,接着发送确认报文给服务端。
  4. 服务端再次发送了 120 字节数据,于是可用窗口耗尽为 0,服务端无法再继续发送数据。
  5. 客户端收到 120 字节的数据后,于是接收窗口往右移动 120 字节,RCV.NXT 也就指向 441,接着发送确认报文给服务端。
  6. 服务端收到对 80 字节数据的确认报文后,SND.UNA 指针往右偏移后指向 321,于是可用窗口 Usable 增大到 80。
  7. 服务端收到对 120 字节数据的确认报文后,SND.UNA 指针往右偏移后指向 441,于是可用窗口 Usable 增大到 200。
  8. 服务端可以继续发送了,于是发送了 160 字节的数据后,SND.NXT 指向 601,于是可用窗口 Usable 减少到 40。
  9. 客户端收到 160 字节后,接收窗口往右移动了 160 字节,RCV.NXT 也就是指向了 601,接着发送确认报文给服务端。
  10. 服务端收到对 160 字节数据的确认报文后,发送窗口往右移动了 160 字节,于是 SND.UNA 指针偏移了 160 后指向 601,可用窗口 Usable 也就增大至了 200。

参考博客:

snailclimb.gitee.io/javaguide/#…

七层模型:

www.jianshu.com/p/9b9438dff…

TCP连接三次握手四次挥手

blog.csdn.net/csdnnews/ar…

TCP连接–初始化序列号

blog.csdn.net/qq_35733751…

TCP连接四次挥手

cloud.tencent.com/developer/a…

TCP重传机制、滑动窗口、流量控制、拥塞控制。

www.cnblogs.com/xiaolincodi…

TCP拥塞控制

cloud.tencent.com/developer/a…

整体参考文章:

coolshell.cn/articles/11…

coolshell.cn/articles/11…