欢迎浏览高耳机的博客
希望我们彼此都有更好的收获
感谢三连支持!
TCP(上):成熟可靠的传输层协议-CSDN博客
🥝在上篇博客中,我们针对TCP的特性,报文结构,连接过程以及相对于其他协议的区别进行了探讨,提供了初步的理解和概览。本篇中,我们将重点展开包括TCP的三次握手和四次挥手过程、滑动窗口、拥塞控制、捎带应答、延迟应答以及粘包等问题,这些都是TCP协议的关键组成部分。
目录
连接管理
三次握手
四次挥手
滑动窗口
流量控制
拥塞控制
延迟应答&捎带应答
粘包问题
异常处理
TCP小结
连接管理
🥥三次握手,四次挥手 是TCP中一个形象且重要的建立连接的机制 也是最常考的机制。
所谓的 建立连接 ,就是通信双方各自保存对方的信息。
三次握手
SYN是"Synchronize"的缩写,在网络中意为“同步”。在三次握手过程中,SYN标志位用来同步连接双方的序列号,确保数据传输的顺序性和可靠性。通过交换SYN标志位的数据包,TCP连接的两端可以确认彼此都准备好进行数据传输,并且可以正确地识别和排序收到的数据包。
ACK是"Acknowledgment"的缩写,在网络中意为“确认”。ACK标志位用于确认收到的数据包。当一个TCP段被成功接收并且没有错误时,接收方会发送一个带有ACK标志位的数据包给发送方,以告知发送方该数据包已经被成功接收。
🍇明明是"三次握手",可为什么有四个连接步骤??
将第二第三次连接步骤合并,便是三次握手的完整形态:
🍈如上图所示。而之所以将其合并,主要目的是提高建立连接的效率和减少延迟,服务器端可以在确认客户端的SYN请求的同时,发送自己的SYN请求,这样双方就可以在一次数据包交换中完成序列号的同步和确认,提高了建立连接的效率。同时也有节省带宽的考虑。这种设计使得TCP能够在保证可靠性的同时,提高网络通信的性能。这里运用了捎带应答的机制,下面会介绍到。
四次挥手
FIN是"Finish"的缩写,FIN标志位用于终止一个已经建立的连接。FIN标志位的发送和接收标志着连接的结束阶段。
第一次挥手(客户端):
客户端决定关闭连接,并向服务器发送一个FIN标志位的数据包。这个FIN包不包含任何数据,它的作用是告诉服务器:“我这边没有数据要发送了,你可以关闭连接了。”第二次挥手(服务器):
服务器接收到客户端的FIN包后,会发送一个ACK标志位的数据包作为响应,确认已经收到客户端的FIN请求。这个ACK包告诉客户端:“我收到了你的关闭请求。”第三次挥手(服务器):
如果服务器还有未发送完的数据,它会先发送完这些数据。一旦所有数据都发送完毕,服务器也会发送一个FIN标志位的数据包给客户端,告诉客户端:“我这边也没有数据要发送了,可以关闭连接了。”第四次挥手(客户端):
客户端接收到服务器的FIN包后,会发送一个ACK标志位的数据包作为响应,确认已经收到服务器的FIN请求。这个ACK包告诉服务器:“我收到了你的关闭请求,现在可以关闭连接了。”
🍉TCP四次挥手的过程中,不进行合并的原因与三次握手的情况有所不同,主要基于以下几个方面:
连接终止的单向性:
- 三次握手是建立双向通信的过程,双方都需要准备好发送和接收数据,因此需要同步序列号。
- 四次挥手是终止连接的过程,通常是由一方(通常是客户端)发起,另一方(通常是服务器)响应。连接的终止可以是单向的,意味着一方可能还有数据要发送,而另一方可能已经没有数据要发送了。
确保数据传输完成:
- 在TCP连接中,当一方想要关闭连接时,它需要确保所有已经发送的数据都被对方接收。这通常涉及到等待数据传输的确认。
- 四次挥手中的FIN标志位用来告知对方“我这边没有数据要发送了”,但这并不意味着所有的数据都已经被对方接收。因此,需要等待对方确认收到FIN,并且等待所有未确认的数据包被确认接收。
避免混淆状态:
- 如果在挥手过程中合并FIN和ACK,可能会导致状态混淆。例如,如果一个FIN+ACK包被发送,接收方可能会不清楚这是一个新的连接请求还是一个连接终止请求。
确保可靠性:
- 在TCP中,确保数据的可靠传输是非常重要的。在四次挥手中,每个FIN都需要一个对应的ACK来确认,这样可以确保双方都知道连接即将关闭,并且所有数据都已经被接收和处理。
综上所述,四次挥手不进行合并是为了确保数据的可靠传输,避免状态混淆,以及确保资源的正确释放。这个过程虽然看起来效率较低,但它确保了TCP连接的可靠性和稳定性。
🍊TCP三次握手四次挥手的完整过程:
🍋三次握手四次挥手建立连接的意义:
1.投石问路,抛砖引玉,确认当前通信路径的通畅2.协商参数,通信双方共同确认一些通信中的必备参数数值
!!!注意!!!
TCP可靠性的保证不是三次握手四次挥手,而是确认应答和超时重传~
滑动窗口
🍍在上篇以及连接管理中,都涉及到了确认应答策略,对每一个发送的数据段,都要回复一个ACK确认应答。收到ACK后再发送下一个数据段。这样做有一个比较大的缺点,就是性能较差。尤其是数据往返的时间较长的时候。
既然一发一收的方式性能较低,那么一次发送多条数据,就可以大大的提高性能(其实是将多个段的等待时间重叠在了一起)。
窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。上图的窗口大小就是4000个字节(四个段)。
发送前四个段的时候,不需要等待任何ACK,直接发送;收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据,依此类推;操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答;只有确认应答过的数据,才能从缓冲区删掉;窗口越大,则网络的吞吐率就越高。
🍎如果在滑动窗口中出现丢包:若ACK丢失,则可以通过后续的ACK进行确认,问题不大;若数据包丢失,则ACK会一直返回丢包前的对应序列号,发送端就会重新发送这一部分数据,这种机制被称为"高速重发控制"。
流量控制
🍑接收端处理数据的速度是有限的。如果发送端发得太快,导致接收端的缓冲区被塞满,这个时候如果发送端继续发送,数据无处存放,就会造成丢包,继而引起丢包重传等一系列连锁反应。
因此TCP支持根据接收端的处理能力来决定发送端的发送速度,这个机制就叫做流量控制(Flow Control)。
接收端将自己可以接收的缓冲区大小放入TCP首部中的“窗口大小”字段,通过ACK端通知发送端。窗口大小字段越大,说明网络的吞吐量越高。接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端。发送端接收到这个窗口之后,就会减慢自己的发送速度。如果接收端缓冲区满了,就会将窗口置为0;这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。
接收端如何把窗口大小告诉发送端呢?TCP首部中有一个16位窗口字段,存放了窗口大小信息。
🍓那么,16位数字最大表示65535,那么TCP窗口最大就是65535字节么?实际上,TCP首部40字节选项中还包含了一个窗口扩大因子M,实际窗口大小是窗口字段的值左移M位。在位运算中,左移一位代表着乘以2,那么左移M位则是乘以2的M次方。
如果说滑动窗口是TCP在踩油门_那么流量控制就是TCP在踩刹车
并且,流量控制并不是TCP所独有的,数据链路层中的某些协议,也有流量控制机制
拥塞控制
🫐虽然TCP有了滑动窗口这个利器,能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大量的数据,仍然可能引发问题。因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态下,贸然发送大量的数据,是很有可能雪上加霜的。
所以TCP引入慢启动机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据。
此处引入一个概念称为拥塞窗口。发送开始的时候,定义拥塞窗口大小为1;每次收到一个ACK应答,拥塞窗口加1;每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小做比较,取较小的值作为实际发送的窗口。
像上面这样的拥塞窗口增长速度,是指数级别的。“慢启动”只是指初始时慢,但是增长速度非常快。为了不增长的那么快,因此不能使拥塞窗口单纯的加倍。此处引入一个叫做慢启动的阈值。当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长。当TCP开始启动的时候,慢启动阈值等于窗口最大值;在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回1。
🥝少量的丢包,仅仅是触发超时重传;大量的丢包,就会认为网络拥塞。当TCP通信开始后,网络吞吐量会逐渐上升;随着网络发生拥堵,吞吐量会立刻下降。这是TCP的动态控制机制。
拥塞控制,归根结底是TCP协议想尽可能快地把数据传输给对方,但是又要避免给网络造成太大压力的折中方案。
面多加水_水多加面!
延迟应答&捎带应答
🍇如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能会比较小。假设接收端缓冲区为1M,一次收到了500K的数据;如果立刻应答,返回的窗口就是500K。但实际上可能处理端处理的速度很快,10ms之内就把500K数据从缓冲区消费掉了。
在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过来。如果接收端稍微等一会再应答,比如等待200ms再应答,那么这个时候返回的窗口大小就是1M。窗口越大,网络吞吐量就越大,传输效率就越高。在保证网络不拥塞的情况下尽量提高传输效率。这便是延迟应答。
🥥 很多情况下,在延迟应答的基础上,客户端服务器在应用层也是“一发一收”的。意味着客户端给服务器说了"How are you",服务器也会给客户端回一个"Fine, thank you"。这个时候ACK就可以搭顺风车,和服务器回应的"Fine, thank you"一起回给客户端。就比如在TCP三次握手中,第二次握手的ACK与SYN同时返回。这便是捎带应答。
粘包问题
我们知道TCP是面向字节流的协议。
🍉粘包问题中的“包”是指的应用层的数据包。在TCP的协议头中,没有如同UDP一样的“报文长度”这样的字段,但是有一个序号这样的字段。站在传输层的角度,TCP是一个一个报文传输过来的,按照序号排好序放在缓冲区中。站在应用层的角度,看到的只是一串连续的字节数据。那么应用程序看到了这么一连串的字节数据,就不知道从哪个部分开始到哪个部分结束是一个完整的应用层数据包。
解决粘包问题的关键就是明确"两个包之间的边界"。对于定长的包,保证每次都按固定大小读取即可;对于变长的包,可以在包头的位置约定一个包总长度的字段,从而就知道了包的结束位置;还可以在包和包之间使用明确的分隔符,只要保证分隔符不和正文冲突即可。
🍊对于UDP协议来说,是否也存在“粘包问题”?对于UDP,如果还没有上层交付数据,UDP的报文长度仍然在。同时,UDP是一对一把数据交付给应用层,就有很明确的数据边界。站在应用层的角度,使用UDP的时候,要么收到完整的UDP报文,要么不收,不会出现如上的情况。
异常处理
进程崩溃/终止:进程终止会释放文件描述符,仍然可以发送FIN。和正常关闭没有什么区别。
机器重启:和进程终止的情况相同。
机器掉电/网线断开:接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了,就会进行reset。即使没有写入操作,TCP自己内置了一个保活定时器,会定期询问对方是否还在。如果对方不在,也会把连接释放。
🍋另外,应用层的某些协议,也有一些这样的检测机制。例如HTTP长连接中,也会定期检测对方的状态。例如QQ,在QQ断线之后,也会定期尝试重新连接。
TCP小结
TCP(上):成熟可靠的传输层协议-CSDN博客
在本系列博客中,我们深入探讨了TCP的核心特性,包括🍍确认应答、🍋超时重传、🍎连接管理、🍑滑动窗口、🫐流量控制、🍓拥塞控制、🍇延时应答、🍊捎带应答以及🍉面向字节流和🥝异常处理。这些特性共同确保了TCP在网络通信中的可靠性和效率。通过理解这些机制,我们能够更好地把握网络通信的复杂性,并在现实中有效地利用TCP。
TCP协议的深度和广度远不止于此,希望大家继续探索和学习,我期待在未来的探索中与您同行。如果您有任何疑问或想要进一步讨论的话题,请随时留言。
希望这篇博客能为你理解TCP及网络提供一些帮助
如有不足之处请多多指出
我是高耳机