面试必备!TCP协议经典十五连问!

      最后更新:2022-08-02 08:16:12 手机定位技术交流文章

      TCP协议是大厂面试必问的知识点。整理了15道非常经典的TCP面试题,希望大家都找到理想的offer呀

      1.谈论TCP三手过程

      在CLOSED状态中启动客户端和服务器,然后服务端开始监视一个端口并进入LISTEN状态

      • 第一个握手(SYN=1, seq=x),然后客户端进入SYN_SEND状态

      • 第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1),然后服务器结束,进入SYN_RCVD状态。

      • 第三次握手(ACK=1,ACKnum=y+1),发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手,即可以开始数据传输。

      为什么TCP应该握手三次,而不是两次,而不是四次?

      TCP握手为什么是三次呢?为了方便理解,我们以谈恋爱为例子:两个人能走到一起,最重要的事情就是相爱,就是我爱你,并且我知道,你也爱我,接下来我们以此来模拟三次握手的过程:

      你为什么不两次握手?

      如果女孩只是两次握手,她可能不知道,她说我也爱你,男孩是否接受,关系不会愉快地发展。

      你为什么不能四次握手?

      因为握手不能是四次呢?因为三次已经够了,三次已经能让双方都知道:你爱我,我也爱你。而四次就多余了。

      3.在TCP中握手四次

      1. 第一个波(FIN=1, seq=u),发送后,客户端进入FIN_WAIT_1状态

      2. 第二波(ACK=1, ack=u+1, seq =v),发送后,服务器终止CLOSE_WAIT状态,客户端收到确认包,并输入 FIN_WAIT_2状态

      3. 在第三波(FIN=1, ACK1, seq=w, ack=u+1)之后,服务器最终处于LAST_ACK状态,等待客户端的最后ACK。

      4. 第四波(ACK=1,seq=u+1,ack=w+1),客户端从服务器端收到关闭请求,发送一个确认包,并输入 TIME_WAIT状态,等待一个固定的时间(两个最大寿命周期,2MSL,2 Maximum Segment Lifetime)之后,没有服务器端的ACK接收,考虑到服务器端正常关闭了连接,于是他自己关闭了联系,进入封闭状态。服务器端收到此确认包后,关闭连接,进入封闭状态。

      4. 为什么 TCP 应该 四 次 波动?

      举个例子吧!

      小明和小红打电话聊天,当电话即将结束时,小红说:“我没话要说,”小明回答说:“我知道。但小明可能还有话要说,小红不能要求小梅按自己的步伐完成电话,也许小明又说了些什么,最后小明说,“我已经干完了,”小红回答:“我知道。这样通话才算结束。

      5. TIME-WAIT 状态为什么需要等待 2MSL

      2MSL,2 Maximum Segment Lifetime,即两个最大段生命周期

      • 一个MSL确保,当手关和关四次时,最后的ACK消息到达链路的另一端

      • 一个MSL确保相反端不会收到ACK,以便重新传输的 FIN消息可以到达

      6.TCP和UDP之间的区别

      1. TCP是面向连接的(例如先建立连接的呼叫);UDP是不连接的,即在发送数据之前不需要连接。

      2. TCP需要安全性,提供可靠的服务,并通过TCP连接传输数据,没有损失、重复和安全可靠性。UDP尽力提供,而不是保证可靠的提供。

      3. TCP是点到点连接,UDP是一到一,一到多人,多到多人

      4. TCP传输效率相对较低,UDP传输效率较高,适用于高速传输和高速通信或广播通信。

      5. TCP适用于网页、邮件等;UDP适用于视频、语音广播等。

      6. TCP为节点流动,UDP为消息

      7. TCP报文首部有哪些字段,说说其作用

      • 16位元端口编号:源端口编号,主机所在的地方;目标端口编号,上层协议或应用程序发送的

      • 32位序号:一次TCP通信(从TCP连接建立到断开)过程中某一个传输方向上的字节流的每个字节的编号。

      • 32位元确认数:用于响应向另一方发送的TCP消息,其值是接收的TCP消息字段的序列值加上1。

      • 四个头 ministry:表示tcp头部有多少32位字符(4字节)。 因为最大4位字符可以识别15位字符,TCP头部长60字节。

      • 6位元标记: URG (如果紧急标记有效), ACk (如果确认号有效), PSH (缓冲区尚未填满), RST (如果另一方要求重新建立连接), SYN (建立连接消息标记), FIN (如果另一方被告知关闭连接)

      • 16位元窗口大小:是TCP流量控制的手段。 我在这里所说的窗口指的是接收通知窗口,它告诉对方的TCP接收缓冲区它能包含多少字节数据,这样对方可以控制发送数据的速度。

      • 16位校验和:由发送端填充,接收端对TCP报文段执行CRC算法以检验TCP报文段在传输过程中是否损坏。注意,这个校验不仅包括TCP头部,也包括数据部分。这也是TCP可靠传输的一个重要保障。

      • 16位紧急指针:正偏差。它添加序列字段的值,以表示最近的紧急数据的下一个序列的序列。因此,确切地说,这个字段是紧急指针与当前序列的移动,它可以称为紧急调动。TCP的紧急指示器是发送紧急数据从发送器到接收器的方式。

      8.TCP如何确保可靠性

      • 首先,TCP连接是基于三个握手,切断是基于四个握手。 确保连接和切断的可靠性。

      • 第二,TCP的可靠性也反映在状态上;TCP记录发送的数据,接受的数据和不接受的数据,并确保数据包到达顺序,并确保数据传输不误导。

      • TCP的可靠性也反映在可控上,它具有信息校正、ACK响应、超时再传输(发送器)、序列数据再传输(接收器)、重复数据丢弃、流量控制(滑窗)和拥挤控制等机制。

      9. TCP 重发机制

      超时重传

      为了实现可靠的传输,TCP实现了重传输机制。 最基本的重传输机制是超时重传输,即在发送数据消息时,设置一个计时器,每当对方没有收到ACK确认来响应消息时,就会重传输消息。

      这个间隔时间,一般设置为多少呢?我们先来看下什么叫RTT(Round-Trip Time,往返时间)。

      再传输时间out(英语:Retransmission timeout)是再传输时间out(英语:Retransmission timeout),又称RTO。

      RTO设置多久呢?

      • 如果RTO较小,那么数据很可能不会丢失,并且会重新出现,导致网络阻塞和更多的加班事件。

      • 如果RTO是更大的,效果直到花被感谢或没有复发才好。

      一般情况下,RTO略大于RTT,效果是最好的。一些小伙伴会问,超时时间有没有计算公式呢?有的!有个标准方法算RTO的公式,也叫Jacobson / Karels 算法。我们一起来看下计算RTO的公式

      1.首先计算SRTT(计算平滑RTT)

      2. 再计算RTTVAR (round-trip time variation)

      3. 最终的RTO

      其中,α = 0.125,β = 0.25, μ = 1,∂ = 4这些参数是获得大量结果的最佳参数。

      然而, 超时 传输 有 下列 缺点 :

      • 当消息段落丢失时,它会在重新传输子组之前等待一段时间的超时,从而增加最后到最后的延迟。

      • 当一个报文段丢失时,在其等待超时的过程中,可能会出现这种情况:其后的报文段已经被接收端接收但却迟迟得不到确认,发送端会认为也丢失了,从而引起不必要的重传,既浪费资源也浪费时间。

      而且,TCP有一个策略,即将时间间隔随时间增加两倍。 过时传输需要长时间等待,因此,也可以使用快速的再传输机制。

      快速重传

      快速重传机制不是时间驱动的,而是数据驱动的,它基于接收器的反馈来触发重传。

      让我们一起看一下以下的快速转播过程:

      快速重传流程

      发送器发送了1,2,3,4,5,6数据:

      • 第一个Seq=1首先发送,然后Ack返回2;

      • 第二个Seq=2也被发送,假设也是正常,所以ACK返回3;

      • 由于其他原因,例如网络,第三个Seq=3没有被发送;

      • 第四个Seq=4也是交付的,但由于Seq3没有交付,所以ACK返回3;

      • 接下来的Seq=4,5也被发送,但ACK返回3,因为Seq=3没有收到。

      • 发送器收到三份重复冗余ACK=3的确认(实际上是四份,但第一个是正常ACK,最后三个是重复冗余)以知道发送过程中消息的哪个部分丢失,然后再发送该部分消息在计时器过期之前。

      • 最后,它接收到Seq3,而Seq=4,5,6接收到所有,所以ACK返回7。

      但快速重传还可能会有个问题:ACK只向发送端告知最大的有序报文段,到底是哪个报文丢失了呢?并不确定!那到底该重传多少个包呢?

      是重传 Seq3 呢?还是重传 Seq3、Seq4、Seq5、Seq6 呢?因为发送端并不清楚这三个连续的 ACK3 是谁传回来的。

      选择性确认再传输(SACK)

      为了解决快速重传的问题:应该重传多少个包? TCP提供了SACK方法(带选择确认的重传,Selective Acknowledgment)。

      SACK机制就是,在快速重传的基础上,接收端返回最近收到的报文段的序列号范围,这样发送端就知道接收端哪些数据包没收到,酱紫就很清楚该重传哪些数据包啦。SACK标记是加在TCP头部选项字段里面的。

      SACK机制

      如上图所示,发射器接收了三个相同的ACK=30确认消息,触发了快速重入机制,并仅通过SACK信息检测30~39数据的这个部分丢失了,所以当它被检索时,您只能选择30~39TCP消息部分重新启动。

      D-SACK

      D-SACK,即Duplicate SACK(重复SACK),我们根据SACK做了一些扩展,,主要是通知发件人,你重复接受哪些包?DSACK的目的是帮助发送者作出决定,是否发生数据包序列 、 数据包丢失 、 数据包重复或伪造再传输?让TCP更好地控制网络流量.来看个图吧:

      D-SACK总程序

      10. TCP 聊天的滑动窗口

      TCP 发送一个数据,需要收到确认应答,才会发送下一个数据。这样有个缺点,就是效率会比较低。

      这就好像我们面对面聊天,你说完一句,我应答后,你才会说下一句。那么,如果我在忙其他事情,没有能够及时回复你。你说完一句后,要等到我忙完回复你,你才说下句,这显然很不现实。

      为了解决这个问题,TCP引入了一个窗口,即由操作系统打开的缓存空间。 窗口大小值表示继续发送数据而不等待确认响应的最大值。

      在TCP上有一个叫做win的字段,这个字段是16位元窗口的大小,它告诉TCP的另一端接收缓冲区有多少字节的数据它也可以包含,这样另一端可以控制发送数据的速度,从而实现流量控制的目标。

      通俗点讲,就是接受方每次收到数据包,在发送确认报文的时候,同时告诉发送方,自己的缓存区还有多少空余空间,缓冲区的空余空间,我们就称之为接受窗口大小。这就是win。

      TCP滑动窗口分为两个类型:发送窗口和接收窗口。 发射器上的滑动窗口包含四个主要组件,如下:

      • ACK确认发送和接收

      • ACK确认发送但未收到

      • 未发送但可以发送

      • 未发送也不可以发送

      • 一个虚拟矩形框是一个发送的窗口.

      • SND.WND:显示发送的窗口的大小,图上显示的网格数为14。

      • SND.UNA:一个绝对指针,指向已发送但未确认的第一个字符串的序列数目。

      • SND.NXT:下一个发送的地方,它指的是第一个没有发送但可以发送的字符串的序列数。

      接收方的滑动窗口包含三大部分,如下:

      • 已成功接收并确认

      • 接收但不接收数据

      • 未收到数据 未收到数据

      • 一个虚拟矩形框是一个接收窗口.

      • REV.WND:显示接收器窗口的大小。空行框顶部的网格为九。

      • REV.NXT:下一个接收位置,指第一个未接收但可以接收的节点的序列数。

      11.聊天TCP的流量控制

      TCP三次握手,发送端和接收端进入STABLISHED状态,因此它们可以愉快地传输数据。

      但发送器不能疯狂地向接收器发送数据,因为如果接收机不接收,接收器只能在缓存中存储未处理的数据。如果缓存区都满了,如果发送者仍然发送疯狂的数据,接收器只能丢失所接收的包,这是浪费网络资源。

      TCP提供了一种机制,允许发送器根据接收器实际接收能力,即流量控制,控制发送的数据量。

      TCP控制通过滑动窗口的流动. 让我们看一下简短的流动控制:

      首先,双方握手三次,将各自的窗口大小初始化为400字节。

      TCP的流量控制

      1. 如果当前的发送者向接收者发送了200字节,那么发送者的SND.NXT向右移动200字节,这意味着当前可用的窗口被减少200字节。

      2. 接收后,将它放在缓冲队列中,REV.WND=400-200=200字节,因此 win=200字节返回发送者。 接收者将缩小滑动窗口的200字节拖到ACK消息的第一部分

      3. 发送者又发送了200字节,200字节到达,继续放到缓冲队列。不过这时候,由于重负荷,收件人不能处理这么多字符,只能处理100字节,剩下的100位字节继续被放置在缓冲队列中。这时候,REV.WND=400-200-100=100字节,赢 = 100 返回发送者。

      4. 发送者继续工作, 将发送100字节来, 此时接受窗口的胜利为0.

      5. 发送者停止发送,打开一个预定的任务,并要求接收者每次,直到胜利超过0,继续发送。

      12.TCP交通堵塞控制

      交通堵塞控制在网络上工作,防止太多的包进入网络,避免网络过载.其目标是最大化网络瓶颈链的带宽.它与流量控制有什么区别?流量控制在接收器上工作,根据接收器的实际接收能力, 控制了传输速度.防止分组丢失的。

      我们可以把网络链与水管进行比较,如果我们想最大限度地利用网络传输数据,就必须尽快使水管达到最佳状态。

      发送方维护一个拥塞窗口cwnd(congestion window)的变量,估计该链(水管)在一段时间内能携带和运输的数据(水)的数量。它的大小代表了网络的密度,并且是动态变化的,但是为了达到最大的传输效率,我们应该如何知道这个管道有多高效?

      一个相对简单的方法是增加输送的水量,直到水管即将爆炸(相应的损失发生在网络上)。TCP的描述如下:

      只要网络中没有出现拥塞,拥塞窗口的值就可以再增大一些,以便把更多的数据包发送出去,但只要网络出现拥塞,拥塞窗口的值就应该减小一些,以减少注入到网络中的数据包数。

      事实上,有很多常见的交通堵塞控制算法

      • 慢启动

      • 拥塞避免

      • 拥塞发生

      • 快速恢复

      慢启动算法

      慢启动算法,表面意思就是,别急慢慢来。它表明,在TCP建立连接完成后,首先不要大量发送数据,相反,首先检查网络的拥挤程度。增加拥挤的窗口的大小,从小到大,如果没有出现丢包,每收到一个ACK,增加1到拥挤窗口的cwnd大小(单元是MSS)。发送窗口每轮都翻倍,呈指数增长,如果出现丢包,拥塞窗口就减半,进入拥塞避免阶段。

      • TCP连接完成,初始化cwnd = 1,表明一个MSS单元大小的数据可以传输。

      • 每次收到ACK, cwnd都会增加一个;

      • 每当有RTT时,cwnd的增长率就增加两倍;指数的增长率就增加

      为了防止cwnd增长过大引起网络拥塞,还需设置一个慢启动阀值ssthresh(slow start threshold)状态变量。当cwnd到达该阀值后,就好像水管被关小了水龙头一样,减少拥塞状态。即当cwnd >ssthresh时,进入了拥塞避免算法。

      拥塞避免算法

      一般来说,慢启动阀值是65535字节,cwnd到达慢启动阀值后

      • 每次收到ACK,cwnd = cwnd + 1/cwnd

      • 当每个RTT通过时,cwnd = cwnd + 1

      显然这是一个线性上升算法,以避免造成网络拥塞的问题。

      拥塞发生

      当一个包丢失时发生网络拥塞时,有两个情况:

      • RTO超时重传

      • 快速重传

      如果RTO超时传输发生,则将使用拥堵发生算法

      • 慢启动阀值 sshthresh = cwnd /2

      • 用1取代cwnd

      • 进入新的慢启动过程

      这真的是辛辛苦苦几十年,一朝回到解放前。其实还有更好的处理方式,就是快速重传。发送方收到3个连续重复的ACK时,就会快速地重传,不必等待RTO超时再重传。

      image.png

      慢启动阀值ssthresh 和 cwnd 变化如下:

      • 拥挤窗口大小 cwnd = cwnd/2

      • 慢启动阀值 ssthresh = cwnd

      • 进入快速恢复算法

      快速恢复

      快速再传输和快速恢复算法通常同时使用。 快速恢复算法得出结论,还收到了三个重复的ACK,表明网络并不坏,因此不需要像RTO超时一样强。

      如上文所述,Cwnd和sshthresh在进入快速恢复之前已更新:

      然后,真正的快速算法如下:

      • cwnd = sshthresh  + 3

      • 重复数个ACK(即数个丢失的包)

      • 如果你得到重复的ACK,那么cwnd = cwnd +1

      • 如果ACK接收新数据,cwnd = sshthresh。 当ACK接收新数据时,它表示恢复过程已经结束,可以再次输入避免拥挤的算法。

      13.半联队列与SYN洪灾攻击的关系

      在TCP进入三个握手之前,服务端从封闭状态转换为 LISTEN状态,同时内部创建两个队列:一个半联队列(SYN队列)和一个全联队列(ACCEPT队列)。

      什么是半联队列(SYN队列)?什么是全联队列(ACCEPT队列)?记住TCP的三个握手图:

      三次握手

      • 当TCP三次握手时,客户端将SYN发送到服务器,服务器接收ACK和SYN,状态从 LISTEN变为SYN_RCVD,此时连接被推入SYN队列,即半连接队列。

      • 当客户端答复ACK时,服务端收到三个握手完成。 此时,连接等待特定应用程序被删除,然后在它被删除之前,它被推入ACCEPT队列,即整个连接队列。

      SYN Flood是一种典型的DoS (Denial of Service,拒绝服务) 攻击,它在短时间内,伪造一个不存在的IP地址,将大量SYN消息发送到服务器。当服务器响应SYN+ACK消息时,ACK 将 不 收到 任何 答复 ;因此, 在 服务器 上 创建 了 大量 的 半 连接 队列,这不会处理正常的TCP请求。

      主要有syn cookie和SYN Proxy防火墙解决方案。

      • syn cookie:收到SYN包后,服务器按照某种方法,用数据包的源地址、端口等参数计算一个cookie值,作为您的SYNACK包的序列数,答复SYN+ACK后,服务器不会立即分配资源处理,收到发件人的ACK包后,根据数据包的源地址和端口重新计算数据包中的认证序列数目,如果正确,设置连接,否则丢弃该包。

      • SYN代理防火墙:服务器防火墙作为代理,响应每个接收的SYN消息,并保持半连接。 发送者返回ACK包后,重建SYN包并发送到服务器,创建一个真正的TCP连接。

      14. Nagle算法和延迟确认

      Nagle算法

      如果发件人向收件人发送一个很小的包裹,例如单封信,那么亲爱的朋友,你认为问题是什么?

      在TCP/IP协议中,无论发送多少数据,总是在数据前面添加协议头,同时,对方接收到数据,你还需要发送一份ACK来确认.为了尽量利用网络宽度,TCP总是想发送尽可能多的数据。纳格尔算法设计为尽可能多的数据发送,避免用许多小数据块重载网络.

      Nagle算法的基本定义是:任意时刻,最多只能有一个未被确认的小段。所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。

      Nagle算法的实现规则:

      • 如果数据包长度达到MSS,则允许发送;

      • 如果包含 FIN, 则允许发送;

      • 如果设置TCP_NODELAY选项,发送是允许的;

      • 如果没有设置TCP_CORK选项,则允许发送,如果所有发送的小数据包(长度小于MSS)被确认;

      • 上述条件不满足,但如果有加班(一般是200ms),则会立即发送。

      延迟确认

      如果收件人刚收到发送人的数据包,它会在很短的时间里收到第二个数据包。 所以请问收件人是否是一个好的单一反应,还是一个好的单一反应?

      收到数据包后,如目前尚无向相反方向发送数据,它可以在一段时间后得到确认(Linux默认为40ms)。如果只有数据可以传递到相反的端,由于ACK传输数据,没有必要单独派出ACK。如果时间不允许发送数据,也发送ACK,避免对端以为丢包。

      然而,有些场景不能延迟确认,例如找到一个随机序列包、接收一个比帧大的消息、需要调整窗口大小等。

      一般来说,纳格尔算法和延迟确认不能同时使用,纳格尔算法意味着输出的延迟,延迟确认意味着接收的延迟,因此大豆会造成更大的延迟,导致性能问题。

      15.TCP的粘附和解压缩

      TCP是面向流,无限制的数据串。TCP数据库不能理解高级业务数据的具体含义,它将根据TCP缓冲区的实际情况分发数据包,所以在业务上认为,一个完整的包可以通过TCP将它分成多个包发送,也可以将多个小包装入一个大的数据包,这就是所谓的TCP粘贴和拆卸问题。

      TCP的粘包和拆包

      为什么粘贴和拆卸会发生?

      • 如果发送的数据小于由TCP发送的缓冲器的大小,TCP将数据数次写入缓冲器并一次发送。

      • 如果接收数据端的应用程序层没有及时读取接收缓冲区的数据,则会发生粘附;

      • 如果发送的数据大于TCP发送缓冲区剩余缓冲空间的大小,则会发生压缩;

      • 待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。即TCP报文长度-TCP头部长度>MSS。

      解决方案:

      • 发送器将每个包封入一个固定的长度

      • 通过在数据末端添加特殊字符分开

      • 将数据分为两部分,一部分是头部,一部分是内容体;其中头部结构大小固定,且有一个字段声明内容体的大小。

      • 如果这篇文章对你有帮助,别忘了给我三个链接,赞扬,翻译,评论,

        下次再见吧! 如何得到答案: 同意 评估 关闭

        学习更多知识和技能,关注和私人消息(03)

      本文由 在线网速测试 整理编辑,转载请注明出处,原文链接:https://www.wangsu123.cn/news/31244.html

          热门文章

          文章分类