欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > 网络练级宝典-> TCP协议

网络练级宝典-> TCP协议

2025/2/25 3:10:58 来源:https://blog.csdn.net/a1275174052/article/details/144321628  浏览:    关键词:网络练级宝典-> TCP协议

目录

TCP协议

可靠性

TCP协议格式

序号与确认序号

窗口

六个标志位

超时重传机制 

 连接管理机制

三次握手 

四次挥手

流量控制

滑动窗口

拥塞控制

延迟应答

捎带应答

面向字节流

粘包问题

TCP异常情况 

TCP总结:

TCP的应用层协议


TCP协议

TCP:Transmission Control Protocol(传输控制协议).

TCP协议是今天互联网使用最广泛的传输层协议。

TCP能被广泛使用是因为其可靠的数据传输。很多应用层协议都是基于TCP协议的。比如:HTTP,HTTPS,FTP,SSH等,甚至MySQL底层也是TCP协议。

可靠性

网络中为什么不可靠

 

我们知道我们在内存和外设之间的“线”叫IO总线。内存和CPU的叫系统总线。因为这些设备都在一台机器上,所以之间的距离是很短的,所以数据传输很难出现错误。

但是进行网络通信时,我们要经过很长的距离,这段距离中,数据是很可能丢失的。所以为了保证信息的完整和可靠性。TCP就是为了解决上述问题的

UDP协议

TCP保证了可靠性,UDP是一种不可靠的传输协议,那存在的意义是什么?

 TCP因为要保证可靠性,就会引入更多的工作,时间空间。所以使用起来比UDP也要困难。

UDP因为不可靠,所以不需要考虑传输的问题,所以UDP就像是单向的发送,并且并不保证对方是否收到,就像以前的寄信。

而TCP就像打电话,要保证双方是能接通的,才能开始通信。

UDP和TCP之间并没有谁好谁坏,在不同的场景都有使用。

如果只是只管数据传输并允许少量丢包,那么就可以选择UDP,像视频直播这类的其实使用的是UDP协议。

如果要保证数据完整,比如银行转账,支付宝支付等等这些就得使用TCP协议了。

TCP协议格式

 字段含义(后面会有详细讲解):

  • 源/目的端口号:表示数据是从哪个进程来,到发送到对端主机上的哪个进程。
  • 32位序号/32位确认序号:分别代表TCP报文当中每个字节数据的编号以及对对方的确认,是TCP保证可靠性的重要字段。
  • 4位TCP报头长度:表示该TCP报头的长度,以4字节为单位。
  • 6位保留字段:TCP报头中暂时未使用的6个比特位。
  • 16位窗口大小:保证TCP可靠性机制和效率提升机制的重要字段。
  • 16位检验和:由发送端填充,采用CRC校验。接收端校验不通过,则认为接收到的数据有问题。(检验和包含TCP首部+TCP数据部分)
  • 16位紧急指针:标识紧急数据在报文中的偏移量,需要配合标志字段当中的URG字段统一使用。
  • 选项字段:TCP报头当中允许携带额外的选项字段,最多40字节。

TCP报头的6个标志位。

  • URG:紧急指针是否有效。
  • ACK:确认序号是否有效。
  • PSH:提示接收端应用程序立刻将TCP接收缓冲区当中的数据读走。
  • RST:表示要求对方重新建立连接。我们把携带RST标识的报文称为复位报文段。
  • SYN:表示请求与对方建立连接。我们把携带SYN标识的报文称为同步报文段。
  • FIN:通知对方,本端要关闭了。我们把携带FIN标识的报文称为结束报文段。

TCP报头和载荷分离过程

通常来说TCP报头是20字节,但是有选项报头增加,可能会突破20字节,所以4位首部长度,4个比特位,2的4次方就是16,我们首部长度的单位是4字节,所以总共能表示4 * 15 = 60字节。所以我们选项是可能有40字节的。

所以当首部长度为 0101 == 5 时 报头长度 为 5*4 == 20字节,此时就判断为一个没选项的报头

然后只要分离把报头的20字节解开,剩下的就是载荷(数据)了。

序号与确认序号

注意:实线代表收到信息,虚线代表没收到。

 我们在主机A发送信息,如果主机B收到,且要返回信息,此时信息没发回来,A是不知道的

可以看到我们最后回复的人的信息总是不可靠的,因为要收到信息的人是不确定收没收到的。

就像发给女朋友信息,对方心情不好时有可能不回你的信息,此时你不知道是信息没发出去,还是对方心情不好,所以不可靠。

上图我们把数据和能否收到信息的判定数据绑到一起给对方发过去,此时如果对方一直没有回复,那我们就会一直发,直到对方回应收到。

32位序号

 我们在网络中只是一条信息一条信息的发,然后一条一条的确认,那这个效率真的太慢了。所以我们可以一次发一堆,对方也可以收一堆,然后对方会判定自己究竟收到多少数据,然后回应时发给发送方,发送方就知道有哪些数据对方没收到,然后就继续发送这些数据,直到收方收到。

32位序号,就是我们一堆数据发送的序号,每块数据都有自己的序号,然后对方就可以判断那些收到了。

我们TCP有发送缓冲区,和接收缓冲区,其实这个缓冲区就是字符数组(序列化发送数据)。然后我们每一个字数据都是有对应的下标的,我们根据发送数据时这块载荷的第一个字节的序列号,作为32位序号。

所以此时以1000字节发送上图数据,就会发出序列号为1,1001,2001的三个数据。接收端收到信息后,会根据序列号进行排序,让数据保持顺序(可靠)。

32位确认序号

序号发出的是头部,那确认序号发出的就是尾部。 收到第一个数据就是1001的确认序号。剩下两个没收到,然后就会把这个1001给到发送端,此时就可以根据确认序号知道对方收到哪些信息。

注意:响应报头不止可以发出响应,他也是可以携带数据的,如果你之前发送的数据完整,且可执行就会发给应用层,应用层处理完会一起把数据和回应发给发送端。

报文丢失怎么办

我们想的太理想了,如果我们报文丢失,比如就收到1和3两块数据,那此时回应报文应该填多少呢?记住了此时要填小的,因为我们2丢了,那我们只能确认第一块收到了,然后发送端会发送2,3 一起过来,但是3 我们已经收到了,此时就会有去重机制,把重复报文排出。(这个过程是重复的直到接收端收到全部数据。或者多次没收到说明有异常,就会断开连接)

 

为什么要确认序号,和序号两种 

记住TCP我们双方都是可以发送数据和接收数据的。所以两边都有发送缓冲区和接收缓冲区。所以两种序号都要有,也说明TCP是全双工的(双方都能接收和发送)。

所以发送序号是表明自己发送的数据,确认序号是 表明收到对方发送的多少数据。

总结

  • 32位序列号,是用来给发出端标记自己发送的数据的。接收端了解自己收到的那些数据
  • 32位确认序号,是用来给发出端确认自己的数据对方是否完整收到,接收端确认收到的那些数据。

窗口

TCP的接收缓冲区和发送缓冲区

 

 我们知道TCP是传输层协议,然后我们在应用层写的代码。

write和send等接口,其实都是把数据先写到发送缓冲区,然后TCP在发送缓冲区中,取出数据包装好,才会发送给网络层。

read和recv等接口,其实都是把数据从接收缓冲区,经过TCP解开报头,确认是哪个应用的数据然后向应用层发送。

就像我们read和write并不是直接从磁盘中读取数据,而是从缓冲区中获取。

 所以我们应用层只要把数据通过write或send 写到发送缓冲区即可,怎么发送就交给TCP,接收缓冲区也是同理。

 为什么需要缓冲区

数据重传:

我们数据是需要保留的,防止对方没收到,可以重新发送,所以就维护一个缓冲区保存这些数据,然后对应用层也没影响。只有对方确认这块数据收到了,那才会删除这块数据。 

接收速度:

接收端处理速度是有限的,所以需要一个缓冲区保存还没处理的数据。不能说没处理的数据我就不要了。因为数据传输过来不容易啊,走了这么一段路,来即是客。数据重排也是在缓冲区里进行的。应用层只用接收完整的信息,数据可靠性就交给TCP

生产者消费者模型:

我们发现两边都有缓冲区,那不就是生产者消费者模型吗?一边发出(生产)数据,一边接收(消费)数据。如果生产的太快了,就会等等消费者,消费快了也是同理,供需相应才能长久。

所以其实TCP就是两个生产者消费者模型,因为两边都可以生产也可以消费。大产业

窗口大小

 既然有缓冲区,那缓冲区不可能说是无限大的。要考虑你的体量。所以就有了窗口的说法,这个窗口制约了供需关系,如果发送端发的太快了,接收端是没办法吃得下的,所以就需要让发送端停止或者减速。避免新来的数据我们收不到。

所以现在就能解释16位窗口大小是什么了,这个窗口填的是接收缓冲区中剩余空间的大小,当前主机能接收的数据大小。然后发送端就可以根据我们的需求进行自己发送速率的更改。

窗口越大,发送端可以发的越快。窗口越小,发送端发的越慢。如果窗口值没了,那就停止发送,因为发了我也要不了。

六个标志位

为什么要标志位

因为我们不只是传输数据,我们开始时要建立连接(三次握手),然后可能重连,然后关闭时(4次挥手)。

6个标志位代表不同的发送类型,有些类型是不用携带数据的,所以就在TCP中解决,不会向上传。

其实就是6个比特位,位图的知识,一个比特位表示一个标志

SYN 

 SYN是请求连接的报文,只有三次握手建立连接时,需要设定

ACK

 报文中ACK设置为1,表明我们收到了对方的信息,一般除了三次握手第一次不设置,后面只要有通信就都要设置,表达我们收到了你上次的信息(表达需要TCP检查我收到的数据有没有问题,确认序号)。

FIN

FIN只有4次挥手断开连接时设置,表示请求断开连接。

URG 

 URG标志位需要和16位紧急指针配合使用。

在recv函数中可以设置选项MSG_OOB,说明我们有紧急的信息需要处理,紧急指针就是定位这个紧急信息在缓冲区的位置,然后优先读取这个数据。

PSH

这个标志位代表要求接收端迅速读取你的数据。

在我们缓冲区中是有水位线的,怎么理解?

如果我们有数据就拿给上层那多次的的数据读取是很消耗资源的,所以在接受缓冲区有个水位线,只有达标才往上传。所以如果没有达标我们的数据其实也是卡着的。

所以设置PSH可以让没达标的数据也直接传上去,也是一种保障机制,当你缓冲区数据小于水位线时,这个标志位被设置,对方不发数据了,也会给你个PSH让你把剩余的数据读取上去。保障机制 

所以recv时,我们指定了接收多大的数据,数据可能是不足这么大的,这就是原因,PSH。 

超时重传机制 

我们一个数据如果一直得不到回应,那就会判定对方没收到,此时就会重新发送。

但是这个时间怎么判断呢?如果固定的时间,那就不同的情况就没办法处理

所以我们的时间是动态的,而不是固定的。

丢包的两种情况

 

上图我们看见,第一种我们包对方没接到,第二种对方接到了,我们没收到。

只要出现丢包不管是哪种情况,我们发送方都不知道,默认为这个包对方没收到,于是就会重新发这个数据。但这个数据对方已经收到了怎么办,当然没关系,我们的接收方根据序号会进行去重机制。

所以缓冲区的作用又体现了,我们的数据不是立马删除而是在缓冲区里保留,只有对方确认收到并且我已经确认,那这个数据才会删除。

超时重传的时间

我们上面说,超时的时间是动态的,那它是怎么做的呢? 

即第一次为500ms,每次翻倍向上,如果多次没接收到,那我就不管了。

 连接管理机制

我们的服务器是可能联系多个客户端的,那这时不可能只用一个缓冲区来接收,所以我们的连接其实就是在不同的缓冲区进行数据的通信。

TCP在握手的时候建立连接,这时也会有缓冲区的组成。

操作系统对连接的管理

对于管理我们就要想到6个字“先描述,在组织”,即我们连接就是一个结构体,然后我们用数据结构来管理这个结构体。

连接的管理就是对这个数据结构的增删查改,所以效率就是管理这个结构体的时间成本,已经空间成本。

三次握手 

我们知道不管怎么通信最后一个信息都是没有保证的。

 第一次握手:SYN设置,表示客户端要建立连接

第二次握手:SYN + ACK表示服务器也要建立连接,而且这是给客户端的回应(ACK)。

第三次握手:ACK表达这是客户端给服务器的回应。

为什么是三次?

第一次代表客户端发起连接请求,第二次,服务器收到,并且发出我也可以连接,第三次,客户端知道服务器收到了,然后发送我也收到了的请求,此时服务器收到,就代表我们已经连接了。

所以三次双方其实都申请了连接,并且也都给对方了回应,所以三次足以。

因为最后一次是客户端发出的,所以只有这条信息是不可靠的,所以只有客户端会觉得异常,就不会影响到服务器。

这样风险都是客户端的。

三次握手状态

1.最开始我们都是关闭的CLOSED

2.客户端主动给服务器发送了信息SYN,客户端收到SYN。

3.服务器回应客户端SYN+ACK,客户端接受然后建立联系。

4.客户端回应服务器ACK,表示我建立联系,服务器接受然后建立联系。

一个表白的过程:我先表白,你收到后愿意的话,就会发出信息,然后我接受,我这时默认你是我女朋友,然后发出回应,然后你收到后,也建立好联系,我是你男朋友,中间只要有一句玩笑话(没收到),那这个联系就是玩笑了。 

套接字和三次握手的联系

我们的三次握手产生于Connect(),在这个期间我们进行了三次握手。

然后完成了三次握手,服务器Accept(),从Accept中获取连接的信息,然后进行连接的管理后,双方就可以send receive了。

四次挥手

为什么在断开连接需要4次?

断开连接,客户端发起,此时客户端并不知道你服务器是否把全部数据处理完,我只是想给这个指令FIN,然后我服务器收到后,依然回一个ACK给客户端,此时连接并不能断开,万一在断开前客户端的信息我没有接受完全,如果这时候断开,数据就是不完整的,所以我只是回应你,但是我们还不能断开

只有我服务器数据全部处理完成发送给应用层时,这时我才会发一个FIN给你,代表我可以把链接关闭,此时要收到客户端的回复,双方就可以把连接给关掉了。

连接的时候我们并不没数据传输,只是双方的连接建立,所以ACK和SYN可以一起发送,而在断开连接时,我们服务器可能有数据没处理完,但是我们也要给客户端回应一下说我们收到这个请求了。等服务器全部处理完成时,然后发送FIN,客户端回应ACK,此时双方连接断开。

四次挥手状态变化 

 

可以看到我们服务器是先CLOSE_WAIT的,而客户端也是FIN_WAIT,双方都在等待,直到服务器发送FIN,此时服务器处于等待对方ACK的状态,直到服务器也收到客户端的ACK后二者才会断开连接。

TIME_WAIT

主动发起连接关闭的一方在四次挥手后要处于TIME_WAIT状态,等待两个MSL(Maximum Segment Lifetime,报文最大生存时间),因为我们的ACK不能保证服务器是否收到,所以这期间要保证报文送出,如果时间到了我们客户端就会自动关闭,然后服务器在长时间收不到回应也会关闭连接

流量控制

TCP支持根据接收端的接收数据的能力来决定发送端发送数据的速度,这个机制叫做流量控制(Flow Control)。

 流量控制上面就有介绍,就是窗口大小的控制,根据接收端的剩余窗口大小,发送端要进行速度控制。

那在接收端没有窗口后,服务端怎么知道?两种情况

1.等待告知,接收端处理好后,会发送我可以了的通知给发送端,此时发送端是不确定能不能收到的,所以就有超时重传的功劳

2.主动询问,客户端在收到窗口没有后,每隔一段时间就问一下服务器你可以没。

为什么要两种,也是一种可靠性的体现,双方都在确认,如果一方出了问题,另一方也能知道。

16位窗口大小,TCP窗口最大只有65535吗?

 我们之前说报头中还保留了40字节的选项字段,这个字段中就有一个窗口扩大因子M,选用后,窗口大小就是窗口字段的值左移M位得到的。

滑动窗口

连续发送多个数据

 

双方在发送时可以一次性发送多个数据包,但并不是有多少发多少,因为要考虑接收端的接受能力。那怎么考虑呢?

滑动窗口 

 我们的缓冲区其实要分为三个部分的:

我们滑动窗口就是在缓冲区移动的一个更小的窗口。

这个窗口描述的就是此时发送缓冲区最大能发送的数据。在没有下一个ACK返回时。

 滑动窗口考虑的不只是对方的接收能力,还有我们网络的拥塞情况。

但这里我们主要考虑接收能力,下面在说拥塞

其实上面图已经很好说明了,我们的蓝色部分就是当前对方能接收的数据大小,灰色部分就是我们知道对方已经知道了,黄色部分是暂时发不出去的,蓝色部分的内容可以直接发送,直到滑动窗口大小变为0.等待下次对方给出的对方能接收的大小。

就比如说这样,就是我们蓝色区域并不是一起向后的,也有可能是左边走,右边不动,直到对方更新窗口大小。

如何实现滑动窗口 

 滑动窗口就是一种算法思想,如果大家经常做算法题应该很好理解,这里简单解释一下:

我们分为进窗口,和出窗口。

我们进窗口就是end的移动,出窗口就是start的移动,可以这样想,start是在把之前的数据丢弃所以是出窗口,end则是没有发送的数据可以发送了,此时加入窗口

当我们收到窗口大小为win,确认序号为x。确认序号是数据尾,那确认序号的下一个位置就是start,根据win,可以更新end,end = start + win。

丢包问题

情况1:只有ACK丢包 

 这种情况就是ACK丢失,这种情况没影响。因为我们的确认序号一定是发出的当前接收到的连续的最后一个数据的序号。

所以只要6001,收到了就会默认其他的数据都收到了。如果5001 - 6000的应答没收到,那这时窗口里还是有5001,直到收到更大的应答信号,才会向后更新。

情况2:数据包丢了

这是数据包丢失了,接收方,是从前往后确认的,如果中间断了,那确认序号就是前一个数据的序号,此时全部数据都要求发送端重发(有去重机制所以没事)。下次只要收到1001的序号,那就会和之前的数据进行组合。 

但我们思考,如果一直都是1001,没收到,那还需要一直发后面的数据吗?

当然不用,当出现三次同一个确认序号,那就会重复只发这一个序号的内容,减小传输损耗。

快重传和超时重传

 超时重传:

我们会传确认序号后面窗口的所有内容给接收方,这是一般的超时重传

快重传:

如果连续3次都是同一个确认序号,此时就会触发快重传,即只发送一个确认序号数据块。这样就提上了效率,因为传一个和传一堆的差距还是有的。

拥塞控制

什么是拥塞控制

两个主机在进行TCP通信的过程中,出现少量丢包是正常的,此时调用超时重传和快重传即可,但是如果一次出现了大量的数据丢包。那就不正常了

所以这就是为什么发送端要考虑的不仅是对方的接收能力大小,也要考虑网络是否拥塞。

  •  流量控制:根据接收端的接收能力大小,更改发送端的发送速率。避免数据丢失。
  • 滑动窗口:发送端不用等待ACK就直接能发送的区域大小,提高效率。
  • 拥塞窗口:考虑的是双方通信网络的问题如果数据超过了拥塞窗口的大小就会引起网络拥塞。

因为网络不是我们一个人用,而是无数的人用,此时如果全部人一次发超多数据,网络的接收能力没了就出现雪崩了,此时网络启动自我保护机制,要求所有在使用该网络的主机,进行拥塞控制 

 怎么解决网络拥塞

拥塞控制: 

慢启动策略:因为我们首次发送无法确认网络是否拥塞,所以我们从少开始发,第一次发一个,如果没有问题就慢慢提升发送个数。

 除了缓冲区和滑动窗口以外,每个TCP还维护了一个拥塞窗口。这个和滑动窗口的区别就是,滑动窗口和缓冲区的对象是发送端和接收端,而拥塞窗口的对象是发送端和网络。

拥塞窗口的增速模式:

最开始进行的指数增长,后面就会有一个值来改变成以加法增大,直到一个值遇到了网络拥塞,此时就让这个值减小一定倍数,下一次的限定值就会减小。 

延迟应答

我们的应答并不是立即返回给发送端,因为对于服务器来说处理数据是很快的,所以可以留一点时间给服务器,让他吃掉一些数据,那发过去的窗口就可以大一点,发送端就可以一次多发一些信息。(增加网络吞吐量)

不是所有数据包都可以延迟应答。

数量限制:每有N个包就应答一次。

时间限制:超过最大延迟时间应答一次(这个时间 << 超时重传时间)

N一般取2,超时时间一般取200ms。

捎带应答

有了上面的基础,其实很好理解了,就是我们在准备应答时,接收端也处理好了一些信息,此时这ACK和这些处理好的信息可以一起过去。提升效率。

面向字节流

当创建一个TCP的socket时,就会在内核中创建一个接收和一个发送缓冲区。

对于TCP来说,它不关系发送缓冲区里是什么数据,我们只是用write()把数据写下去,TCP把它当成一个个字节的数据。然后怎么拆分怎么发送给对方是TCP协议里完成的,然后在接收端只要管read()数据,具体的细节都是TCP协议实现,我们只用负责write和read即可。这就是面向字节流,不关心数据内容

UDP是面向数据报,它的数据大小每个报文都是有规定的,所以就是一个数据一个数据的发送,当数据量大时,只是把这个数据分几次发出。而TCP是一大堆数据,可能这个数据发出的一半和别人的整体连带一起,或者拆开发出都是可以的。

粘包问题

粘包指的是应用层的数据包。

TCP是可以多个数据包一起发送的,所以其中就有多个数据粘在一起发送。

传输层来说,这些数据是有序号排好序放到缓冲区的。

应用层来说,这就是一堆的连续的字节数据。

那怎么找到,这些数据分类分开呢?

如何解决粘包问题

这里就是应用层要考虑的问题的了,我们可以使用Json等格式带出我们当前内容的长度,也可以在包与包直接打上分隔符。

TCP只是保证数据完整发送过来,不包括怎么帮你分开数据。所以应用层要规划好数据传输解包规则。

UDP有粘包吗? 

没有。因为UDP是面向数据报的所以它的发送就是遵循一个数据一个数据的发送,所以不会有粘包问题,每个包都是一个数据,如果要分包处理,需要在应用层上再加逻辑。

TCP异常情况 

进程终止

客户端访问服务器时,客户端进程崩溃,此时已经建立的连接怎么办?

这个进程的文件描述符会自动关闭,会自动调用close函数,关闭文件描述符,此时底层就会自动执行4次挥手,所以没关系。

机器重启 

客户端正常访问服务器时,如果将客户端主机重启,此时建立好的连接怎么样?

 操作系统会杀掉所有进程,此时就和上面一样。

机器掉电/网线断了

这时候就不会close了,因为我们的进程还在保持,但是无法发送信息了,当服务器长期没收到信息也会关闭这个链接所以没事。

服务器会定期检查客户端的存在情况,如果多次的发送都没有收到ACK,就会关闭

客户端也会定期的给服务器发送存活情况,如果服务器长时间没收到也会关闭。

TCP总结:

可靠性:

  • 检验和。
  • 序列号。
  • 确认应答。
  • 超时重传。
  • 连接管理。
  • 流量控制。
  • 拥塞控制。

性能保证:

  • 滑动窗口。
  • 快速重传。
  • 延迟应答。
  • 捎带应答。

TCP定时器

TCP中还设置了各种定时器。 

  • 重传定时器:为了控制丢失的报文段或丢弃的报文段,也就是对报文段确认的等待时间。
  • 坚持定时器:专门为对方零窗口通知而设立的,也就是向对方发送窗口探测的时间间隔
  • 保活定时器:为了检查空闲连接的存在状态,也就是向对方发送探查报文的时间间隔。
  • TIME_WAIT定时器:双方在四次挥手后,主动断开连接的一方需要等待的时长。

TCP的应用层协议

基于TCP的应用层协议:

  • HTTP(超文本传输协议)。
  • HTTPS(安全数据传输协议)。
  • SSH(安全外壳协议)。
  • Telnet(远程终端协议)。
  • FTP(文件传输协议)。
  • SMTP(电子邮件传输协议)。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词