让小白也能理解TCP协议(完结)
一引言这次我们将会把TCP所有的内容讲完包括拥塞控制延迟应答捎带应答面向字节流并且进行总结包括一些常见的关于TCP协议的一些面试问题例如粘包问题TCP连接异常的几种情况。二拥塞控制虽然TCP有了滑动窗口这个大杀器能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大量的数据仍然可能引发问题。因为网络上有很多的计算机可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态下贸然发送大量的数据是很有可能引起雪上加霜的。我们来举个例子如果10000个数据包 --------- 丢失了12个数据包 ---------重传如果10000个数据包 --------- 丢失了5000个数据包 -----------不会进行重传。因为正常的丢包是非常一小部分但是如果出现了大量的丢包那其实是网络出现的堵塞虽然我这里有点夸张那是便于理解。实际上%1~%1.5认为是正常丢包%3就已经是网络堵塞%5就是网络严重堵塞。那拥塞控制是怎么做的呢前提是网络已经堵塞了这里我们引出两个新的概念慢启动机制先发少量的数据探探路摸清当前的⽹络拥堵状态再决定按照多⼤的速度传输数据拥塞窗口一个由发送端维护的状态变量用于限制一次可以发送到网络中的、尚未确认的数据量上限。通俗理解拥塞窗口就像一个“水龙头”根据网络的拥堵程度动态调节水的流量。网络不拥堵时窗口会尝试增大多发数据网络出现拥堵如丢包时窗口就会立即减小少发数据从而避免让网络“堵死”。发送开始的时候定义拥塞窗口大小为1每次收到⼀个ACK应答拥塞窗⼝加1每次发送数据包的时候将拥塞窗口和接收端主机反馈的窗口大小做比较取较小的值作为实际发送的窗口因为我们首先要保证网络不拥堵的情况下再尽量满足接收端的最大接收能力所以取拥塞窗口和接收端窗口大小的最小值是这样来的。注意实际发送数据出去的还是靠滑动窗口而拥塞窗口和窗口大小取最小值只是决定滑动窗口的大小。像上面这样的拥堵窗口增长速度是指数级别的慢启动只是指初使时慢但是增长速度非常快。通过这样的慢启动从一开始发送少量数据确认网络是否拥堵到后面又能迅速的回到一个客观的发送速度找到了可靠性和效率的平衡点。但是指数增长肯定是不能一直维持的指数增长一直有一个爆炸式增长的名号所以有了以下的一些改变很好的权衡了后期无法控制的指数增长的数据。我们来分析一下这张图此处我们引入一个叫做慢启动的阈值。当拥塞窗口超过这个阈值的时候不再按照指数方式增长而是按照线性方式增长。直到再次发生了大量丢包情况说明又网络堵塞了所以会再次进行慢启动。但是慢启动的阈值变成了上次网络堵塞时窗口大小的一半这样是为了能够指数快速增长到一个几乎能保证不会网络拥堵的一个大小。接着再次线性增加直到再次遇到网络堵塞又会进行再一次的慢启动以此循环。滑动窗口是衡量接收端接收数据能力的指标。拥塞窗口是衡量网络堵塞的指标也就是网络接收能力的指标。大家想一想为什么要这样不断自启动循环呢因为网络是大家共享的网络是不断变化的所以接收能力是不断变化的不断地自启动循环可以及时得知网络的接收能力。拥塞控制的过程就像是热恋中的感觉刚开始的时候大家都很拘谨慢慢的互相了解但是过后熟悉之后就会有极大的发展。拥塞控制归根结底是TCP协议想尽可能快的把数据传输给对方但是又要避免给⽹络造成太大压力的折中方案。三延迟应答延迟应答其实就是表面的意思----------收到数据后等待一会儿在进行ACK应答或者说收到了第一个数据包不进行ACK应答收到第二个数据包再进行ACK应答因为确定序号的含义是收到了该序号之前的数据所以如图回复了2001可以不用回复前面的1001。延迟应答是怎么个回事我们了解了现在我们想一想为什么要这么做如果接收数据的主机立刻返回ACK应答这时候返回的窗口大小能比较小。假设接收端缓冲区为1M⼀次收到了500K的数据如果立刻应答返回的窗口就是500K但实际上可能处理端处理的速度很快10ms之内就把500K数据从缓冲区消费掉了在这种情况下接收端处理还远没有达到自己的极限即使窗口再放⼤⼀些也能处理过来如果接收端稍微等⼀会再应答比如等待200ms再应答那么这个时候返回的窗口大小就是1M⼀定要记得窗口越大网络吞吐量就越大传输效率就越高我们的⽬标是在保证网络不拥塞的情况下尽量提高传输效率那么所有的包都能够延迟应答吗那当然不是有以下两条限制数量限制每隔N个包就应答⼀次时间限制超过最大延迟时间就应答⼀次具体的数量和超时时间依操作系统不同也有差异⼀般N取2超时时间取200ms捎带应答在前面的博客中讲过这里我就补充一点在三次握手中前两次握手不能携带任何数据最后一次ACK可以携带数据这也是一个捎带应答。四面对字节流带来的粘包问题TCP 对应用程序提交的数据只看作一串连续的、无结构的字节序列它不维护数据块之间的边界如何划分这条流完全由应用层协议决定首先要明确粘包问题中的包是指的应用层的数据包。在TCP的协议头中没有如同UDP⼀样的报文长度这样的字段但是有⼀个序号这样的字段。应用程序看到了这么⼀连串的字节数据就不知道从哪个部分开始到哪个部分是⼀个完整的 应用层数据包。那么如何避免粘包问题呢归根结底就是⼀句话明确两个包之间的边界大概就是以下三种方案固定发送数据的长度不足部分填补。举个例子如果固定长度为6但是你发送了长度为5的数据那么就需要填补一个约定好的符号比如说空格也行。在数据前面添加一个的整数这个整数记录的就是数据的长度。数据包和数据包之间有特殊符号分隔比如说一些不常用的符号记得千万不能和正文符号一样。思考对于UDP协议来说是否也存在粘包问题呢这是udp缓冲区的图片因为udp缓冲区底层的实现就是将边界保留的所以udp是不会有粘包问题的。五TCP连接异常1.进程终止进程终止会释放⽂件描述符底层仍然可以发送FIN完成四次挥手和正常关闭没有什么区别。2.电脑重启其实这种情况和进程终止是一样的为什么这么说呢平常我们关机电脑的时候如果电脑上还有程序没有关闭会提示我们还有程序正在进行是否要继续关机所以我们关机前电脑会先把程序终止了再去关闭电脑所以和进程终止是一样的。3.机器掉电/网线断开这个就比较有意思我们以发送端突然断网为例其实都是一样的。如果突然发送端断网那么发送端的操作系统会检查到网卡出现异常变化就会释放掉所有的连接但是接收端不知道发送端断网了还没有进行四次挥手接收端就会无法正常关闭连接。所以我们引出一个新的概念叫做保活定时器也就是说如果说一定时间内接收端没有收到发送端的数据包便会向发送端进行询问是否还在如果对方不在也会把连接断开这种定期的询问也可以叫做心跳机制。另外应用层的某些协议也有⼀些这样的检测机制例如HTTP长连接中也会定期检测对方的状态例如QQ在QQ断线之后也会定期尝试重新连接。六总结为什么TCP这么复杂因为要保证可靠性同时又尽可能的提高性能。可靠性校验和序列号(按序到达)确认应答超时重发连接管理流量控制拥塞控制提高性能滑动窗⼝快速重传延迟应答捎带应答