良许Linux教程网 干货合集 拒绝八股!通俗易懂带你理解TCP协议,轻松拿捏面试官!

拒绝八股!通俗易懂带你理解TCP协议,轻松拿捏面试官!

引言

我坚持认为真正的学习应该是深入理解,而不是简单地死记硬背。只有当你真正理解了某个概念,它才能成为你的一部分,这样在实际应用和表达时才能更加自如。我常常告诉朋友,每次面试时,我并不是简单地背诵知识,而是能够自如地对这些概念进行吟诵,因为我对它们有深入的理解和掌握。但要承认,网络和计算机组成的细节并不像编程语言那样容易理解,因此很多人只能通过死记硬背来学习。

其实,除了那些专业设计者和深入研究的工程师,有多少人能够确保自己对这些知识的理解是完全正确的呢?在面试时,只要能够清晰地表达自己对知识的理解,而不是机械地重复,就能够与其他普通的应聘者产生明显的差异。

话不多说,接下来我会分享我对TCP协议的一些个人见解,虽然可能存在一些错误,但我非常欢迎大家提出指正。

不简单的TCP协议

TCP内部的实现是很复杂的,我会从流量控制、可靠交付、拥塞控制、TCP报文、三次握手四次挥手、重传时间等内容和大家稍微聊聊TCP的具体实现,以及为什么要这么做。

TCP的地位:是整个网络协议族中最重要的一个,TCP在网络协议族中是很特别的,因为整个网络,我们姑且分为课本上的五层吧,从下到上即物理层、数据链路层、网络层、传输层、应用层五层,只有传输层的TCP能做到可靠交付,这也是为什么很多应用都是用TCP的原因,所以在面试中TCP绝对是重点中的重点。

TCP:传输控制协议(英语:Transmission Control Protocol,缩写:TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。用户数据报协议(UDP)是同一层内另一个重要的传输协议。

在因特网协议族(Internet protocol suite)中,TCP层是位于IP层之上,应用层之下的中间层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换。

应用层向TCP层发送用于网间传输的、用8位字节表示的数据流,然后TCP把数据流分割成适当长度的报文段(通常受该计算机连接的网络的数据链路层的最大传输单元(MTU)的限制)。之后TCP把结果包传给IP层,由它来透过网络将包传送给接收端实体的TCP层。TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认信息(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失并进行重传。TCP用一个校验和函数来检验数据是否有错误,在发送和接收时都要计算校验和。(维基百科)

TCP报文

image-20240414224314617
image-20240414224314617

1.源端口,目的端口:每个两字节

2.序号:两字节,TCP的每个字节都按照序号编序(这也是面向字节流的一个标志),建立连接时序号是随机的 而后的序号都在此基础上递增。比如我第一次握手seq=10086 第三次握手seq=10087 之后又传了一百字节的数据,那么再发数据,seq=10087+100+1=10188。

3.确认号:两字节,希望对方下一个报文段的序号 这也是为什么握手的时候ack=seq+1 确认号代表前面序号的字节都已经成功按序收到(捎带确认机制),当客户端发送过来的序号seq=10086时,报文长度为200,那么服务端的ack应该是12087,表示对之前所有报文的确认。

4.数据偏移:四位,实际上表示的是首部长度,最大十进制数转换过来对应的是15,代表头部最多大小为60字节,所以TCP头部的长度应该是20-60间以4字节为单位变化的,不可能出现30字节长度的TCP头部

5.保留:六位,今后使用

然后就是6个控制位,每个1为,数据偏移+保留+6控制位=2字节

6.紧急URG:表明该报文优先级很高,当URG=1时,报文会被放在发送方此次发送报文的最前端进行优先发送

7.确认ACK:ACK=1 确认号有效 建立连接之后 每次ACK=1

8.推送PUSH:PUSH=1 立即创建一个报文段发送出去(用得少)

9.复位RST:RST=1 表示TCP连接出现严重错误,需要重新连接

10.同步SYN:SYN=1 表明这是一个连接请求报文

11.窗口:2字节,滑动窗口的大小 我们后面细说

12.检验和:2字节,这个用途和UDP的一样,加上12字段伪首部之后进行校验

13.紧急指针:2字节,只有当URG=1时才有意义,指出本报文段中紧急数据的字节数

14.选项:长度可变,最大40字节(窗口扩大选项、时间戳选项等等)

TCP的特点

  • 连接点对点
  • 全双工 A可以给B发 B也可以给A发
  • 面向连接 只有连接建立完成 才可以发数据
  • 拥塞控制 主要是通过以下机制来完成的(慢开始 拥塞避免 快恢复和快重传)
  • 可靠交付
  • 面向字节流

这些下面都会一一介绍

TCP和UDP的区别

TCP是一个复杂的协议 而UDP比较简单

1.连接 TCP是一个面向连接的协议 而UDP是一个无连接协议 即TCP需要连接建立之后才能传输数据 而UDP在未建立连接时就能够传输数据

2.交付 TCP是一个可靠交付的协议 而UDP只能尽最大努力交付 可靠交付指的是数据按序到达 不丢失 不重复

3.数量 TCP是一个点对点的全双工协议 而UDP支持一对一 一对多 多对多

4.首部长度 TCP的首部长度更长 20-60字节 而UDP的首部固定为8个字节

5.面向 TCP面向的是字节流 即上层应用层交付下来的数据报 在TCP的眼中是一串字节流 而UDP是面向数据报的

6.是否具有拥塞控制 TCP是有拥塞控制的 在网络拥塞时源点主机的发送速率会降低 而UDP不具有拥塞控制 一些追求传输效率的应用通常使用UDP协议

所谓面向字节流 指的是TCP发送端发送几次数据和接收端接受几次数据时没有必然联系的

比如发送端调用一次write写了一百个字节 但是接受端可以分10次接受 也可以写10次100字节 接收端一次接收完

原因:

TCP面向连接 一个Socket收到的数据由同一台数据发出 并且有序到达 所以每次读取多少数据都可以 因为我知道前面的数据都是对的 某些时候为了节省资源 程序可能会选择”攒一会”再读

而UDP协议不知道前面的数据是否是错的 所以每次发送就对应着一次接收 如果错了就再发

应用区别:

UDP:追求高速度的应用一般用的是UDP(KCP),比如视频、音频、实时游戏、广播,而且通常这些应用对于数据的可靠性要求不是很高,视频丢点数据(丢几帧 后面再补上),你也看不出来,但是速度慢就不能接受了,玩英雄联盟慢一秒别人就把你头打爆了

TCP:可靠传输,HTTP用的就是TCP(后面用为了追求性能用UDP去了,我们之后详说),任何确保数据可靠的应用都应该使用TCP

典中典之三次握手 四次挥手

相信很多同学都听说过三次握手 四次挥手的问题,也大概知道过程是什么样的,但是自己讲很难讲清楚,我们现在来捋一捋这到底是是个什么过程,以及为什么要三次握手四次挥手

注:下文的客户端和服务器是站在应用层角度的,可以理解成发送方和接收方

三次握手

image-20240414224319536
image-20240414224319536

第一次握手:客户端向服务器发送TCP请求连接报文,SYN置1,seq=x (都知道SYN和seq是啥吧)

第二次握手:服务器收到报文,发送TCP确认报文,SYN=ACK=1,seq=y ack=x+1

第三次握手:客户端收到报文,发送ACK=1,seq=x+1,ack=y+1

怎么记忆这个过程呢?

第一次和第二次握手 都属于正儿八经连接建立的过程 我们之前说过SYN置一的情况是请求连接报文才有的 所以前两次握手是有SYN的

第三次握手是对前两次握手的确认过程 即确认 客户端感知到服务器的存在 服务器也知道客户端的存在 所以不需要SYN

因为第一次握手发送的报文是没有实体数据的,所以第三次握手seq=第一次握手seq+1

注:第三次握手可以带数据 前面两次不行

为什么需要三次握手

这个一般是和三次握手一起问的

我个人理解有两个原因

1.TCP是面向连接的 如果只进行两次握手 服务器其实是不知道连接是否建立成功的

2.如果只进行两次握手,那么会出现这么一种情况,因为网络延迟,客户端向服务器发了一次连接请求报文,但是服务器暂时没收到,然后客户端又发了一次,这次服务器收到了,两次握手成功了,这时第一次发送的请求来了,服务器也会做出响应,就会同时有两个TCP连接,浪费资源。但是如果有三次握手的话,服务器进行确认的时候,客户端会忽略掉这次确认(想一想为什么)

四次挥手

image-20240414224323440
image-20240414224323440

第一次:客户端主动发送断开请求,FIN=1(这是一个请求断开的报文),seq=x

第二次:服务器收到之后 发送 ACK=1 seq=v ack=x+1 然后处于半关闭状态Close-wait

第三次:服务器发送FIN=1 ACK=1 seq=w ack=x+1

第四次:客户端收到后 发送ACK=1 seq=u+1 ack=w+1

一些解释

1.第三次为什么seq=w 是因为我们假设第二次到第三次的期间 服务器还在发数据

2,为什么第三次也有ACK字段

个人理解:TCP报文规定了只要建立连接之后所有报文都有ACK字段,所以其实第一次也有ACK字段的,只是大部分教材没有画出来

(八股文的很多细节是无法深究的 不是TCP的设计者谁知道真相是什么呢 我觉得只要有自己的理解 就已经超越很多死背书的人了)

3.为什么服务器连着给客户端发两次 一次不行吗?

第一次是对客户端发来的确认

第二次是请求和客户端断开

而且在这两次发送报文之间 服务器是可以发送数据的 客户端不行

为什么需要四次挥手

服务器决定进行第二次挥手和第三次挥手,某些时候把两次挥手合并,因为中间等不等待其实是由服务端应用程序决定的(handler),如果没有数据要发,同时又开启了tcp的延迟确认,那么就会合并挥手,这也是抓包的时候为什么能抓到三次挥手的包。

为什么需要等待2MSL

1.释放的端口可能会重连刚断开的服务器端口,而第二次挥手之后服务器还在给该端口发数据,如果不等的话,在这个通道里的老TCP报文可能和新的报文冲突,造成数据紊乱,2MSL(两倍生存时间,原因我推测可能是计算客户端发送给服务器,服务器处理之后再返回过来)是足够让客户端全部收到第二次挥手后的数据的。具体的时间不同版本其实也不一样,有兴趣的同学可以深入了解

2.保证确认断开,如果最后一次确认丢失了,服务器会重新进行第三次挥手,这样客户端还能够对第三次挥手的FIN报文进行处理

TCP怎么保证可靠交付

这个问题是一个很广泛的问题 基本把所有TCP的设计思想都包含了

什么是可靠交付?

我认为最起码要做到这几点:数据不丢 数据不重 数据按序 数据不错

1.数据不丢:TCP使用了确认-重传机制,对每一个收到的正确报文进行确认(ack),如果发现有数据没有被确认,就重传,其中重传又有超时重传和快重传

2.数据不重/不错:接收方会对数据进行检验。重复的数据丢弃,错误的数据丢弃,并不进行确认。

3.数据按序:流量控制,不至于一下发很多报文,而且会对数据包进行重新排序

TCP确认-重传机制

相信大家已经懂什么是确认了吧,简单来说,接收端发送ack=10001的报文,就说明从连接开始的序列号(假设是100)到ack-1(10000)的报文都是可靠交付的。

重传也很简单,我们先来聊聊超时重传

什么时候重传呢?

其实TCP在传递数据的时候,会设置一个超时计时器,当超时计时器触发了,发送端还没有收到来自接收端的确认报文,那么就认为这个消息没成功传递,原因可能有很多,网络阻塞,丢包,数据出错等等

问题来了:超时计时器怎么设置呢?每一个数据的超时时间都不一样吗?怎么能做到既不浪费算力,又能高效确认超时呢?

首先超时时间肯定不能选的很短,不然会很容易重发,浪费很多资源。

也不能很长,因为发送端只有收到确认之后才回删除数据,这样会浪费发送端的资源。

所以重传时间RTO我们一般设为比平常报文的平均往返时间稍微长一点点

具体公式如下:

RTTS(加群啊平均往返时间)=(1-a) x 旧的RTTS+ a x RTTS RFC文档推荐 a=0.125

RTTD(RTT偏差加权值) =(1-b) x 旧的RTTD + b x (RTTS-RTTD) RFC文档推荐b=0.25

RTO=RTTS+4 * RTTD

而每次触发超时重传 RTO=2 X RTO

TCP的流量控制

什么是流量控制?为什么要流量控制?

简单来说流量控制就是接收方通过参数控制发送方的速度,记得我们上文讲到的TCP报文的窗口字段吗,这个就是调控的因素,当接收方的缓存变少,就把窗口变小,发送方就会把数据发的慢一点,窗口的大小就是发送速度的大小

而我们提到的这种方案,就是TCP的滑动窗口,它的思想与算法中的滑动窗口类似,维护一个变长的窗口值,落在窗口里的就是可以发送的,在窗口后面的就是发完了的,窗口前面的就是还没发的,窗口是以字节为单位的,这也能体现TCP的面向字节流思想。

注意:发送端和接收端的窗口大小不可能做到完全同步,因为网络具有时延,当窗口为0的时候,发送端不再发送数据,待接收端缓冲区有空间了之后,会重新打开滑动窗口,让发送端继续发送数据。

TCP的拥塞控制

什么是拥塞?

拥塞:对网络中的某种资源的需求超过了资源可提供的部分

TCP采用四种算法避免拥塞

我们把A设为发送方 B设为接收方

发送方维护一个cwnd(拥塞窗口)的动态变量,其值取决于网络的拥塞程度,还有一个慢启动门限ssthresh状态变量

原则:没有拥塞,把cwnd变大,拥塞了,把cwnd缩小 思想很简单,实现很复杂

现在是不是有点蒙,之前不是提到了发送窗口大小吗,怎么又来了个拥塞窗口,A到底用什么速度发数据

经查证:发送窗口大小等于min(cwnd,B的接收窗口)

1.慢开始:cwnd小于ssthresh时,采用这种算法,cwnd大小按照指数扩容,每次 x 2

2.拥塞避免:cwnd>=ssthresh时,采用这种算法,cwnd按照常数扩容,每次+1

判断拥塞:当网络中有一段数据重传,则认为网络拥塞,ss变为拥塞时的一半,cwnd变为1

大家是不是觉得很不科学,因为丢包是很正常的,再牛逼的网络也会丢包,重传一次就认为拥塞,cwnd可能永远都等于1

因此TCP还有一种重传机制

3.快重传:不需要等到超时才进行重传,当B给A连着发了三次ack一致的确认报文,A就认为数据丢失了,重发ack之后的数据

4.快恢复:当快重传被触发时,同时执行快恢复算法,ss=拥塞时的一半,cwnd=ss,执行拥塞避免算法

TCP到底用的是超时重传还是快重传

相信如果你听懂了上面的内容,你脑子里应该有这个疑问

TCP到底是两种都用,还是拥塞的时候用快重传,平常使用超时重传呢?

我们计网老师说的是后者,但是TCP是如何判断网络是否拥塞的呢,已经建立连接的TCP能动态修改重传策略吗?

从业务的角度来说我觉得答案更可能是前者,毕竟这么玩能提高速度。

结语

今天的内容到这里就结束了,如果对您有所帮助,欢迎点赞、评论、收藏,您的支持就是对我最大的鼓励。

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部