网络原理(4)——TCP协议的特性

简介: 网络原理(4)——TCP协议的特性

       以下是TCP协议传输数据过程中的一些特性


一、滑动窗口


       这里的滑动窗口和 oj题算法里的滑动窗口性质是一样的,不过这里的滑动窗口是为了提高网络传输信息的速度。

       如图,它是类似窗口这种,传输数据的时候是按窗口这种为单位进行传输的,这样传输的速度就会比一个一个数据的传要快。因为是花费一个传输数据的等待时间,但是能传输的数据是一个窗口这样单位的数据量。

       TCP这里反复强调数据传输是可靠的,那像上面这种以窗口为单位的方式传输数据,要是中间有丢包的情况,它又会怎么处理呢?

1、ack丢了

       如果是ack丢了,其实是没关系的,因为这里有确认序号,如果你接受到3001~4000,因为接受到后,确认序号就会变成4001,如果前面接受到1~3000的数据,然后接受方没有返回ack,但你此时的确认序号就是4001,那有关系吗,接受方已经知道了,而确认序号的4001就是表示4001前的数据它都收到了,下一个要接受的数据是4001。如图:

       

      如上图,虽然中间两个ack传输失败了,但这里的确认序号已经变成了4001,也就是说,4001之前的数据都收到了,接下来要收到的数据是4001了,也就是说,中间两次的ack发不发送给主机A都可以,因为我已经知道了4001之前的数据都收到了就像你在大街想加一个小姐姐的微信,结果人家小姐姐跟你说,人家都已经有娃了,那里面包含的信息不就是人家小姐姐已经谈过恋爱还结婚了。

2、数据丢了

       如图:

       如果1~1000的数据没有发送到主机B上,也就意味着主机B不会发送ack报文给主机A了,发送方在反复多次想主机A索要ack后还是收不到,发送方就会认为1~1000的数据丢包了,就会重传1~1000的数据给主机B。

       主机B因为拿不到1001之前的数据,就会反复想发送端索要;接下来就是重传3次后,主机B终于收到1~1000的数据了,主机B就会返回ack给发送方,然后主机B的确认序号就会变成4001,而不是1001;这是因为不管是发送方还是接收方,都会有接受缓冲区,前面1001~4000的数据主机B是收到了,但不会更新确认序号,而是把最新的确认序号放在主机B里的接收缓冲区中当拿到1~1000的数据后,就会返回ack给发送端,然后更新确认序号,这个新的确认序号就是在接收缓冲区中拿的。

       注意:这里的反复索要,次数也是有限的,如果反复多次索要合理的数据,还是不成功,想要的数据应该就是丢了,这里规定的次数也就相当于 “超时时间” 的判定了。

       上述的重传的过程,整体效率还是很高的,已经接收到的数据就不不必重传了,这里重传是针对没有接受到的数据(丢包了的)这里整体的效率没有损失,这种重传就称为快速重传

       这里的滑动窗口、快速重传、确认应答、超时重传是同时存在的,互不干扰如果滑动窗口的窗口比较小,也就是要传输的数据比较少,就会出现:滑不动的情况,这时为了保证可靠性,就会退化成确认应答,一个一个的数据进行传输,使用超时重传来保证其可靠性,此时判断是否丢包就是看是否到达时间还没返回ack如果数据很庞大,就会使用快速重传来保证其可靠性,此时判断是否丢包就是看是否有多个ack,索要同一个数据。


二、流量控制(流控)


       我们通过滑动窗口可以知道,这里的窗口越大,传输速度就会越快,但是窗口能无限大吗?答案显而易见:不能;以下是原因的解释。

       我们知道,发送方发送数据的速度是由滑动窗口的大小决定的,而接收方接受数据的速度是由它自身处理数据的能力决定的,如果发送方发送的数据太多、太快,接收方处理不过来这些数据了,第一时间这些处理不过来的数据会放在接收缓冲区里面,但如果缓冲区里面也满了,这时候,就会出现丢包。所以我们要控制窗口的大小,举个简单的例子,如图:

       我们想要让蓄水池不装满水的话,进水口的速度就要和出水口的速度持平,就算快一点点也没事,因为蓄水池有容量空间,可以储存在蓄水池里;但如果进水口速度过快,出水口就那么大点位置,不久后蓄水池就会装满水,这时候出水口排不出去这么多水,进水口那就会溢出浪费了。

       我们所说的流量控制也和上面的例子类似,要控制窗口大小,尽量让发送方的发送数据速度和接受速度持平,而为了让发送、接受速度尽可能持平,就要让接收方去影响发送方的速度,这就是流量控制。流量控制是通过下面这个字段反馈给发送方的:

       这个字段在普通报文段没有意义,在ack报文中才有意义也就是接收方接受到数据后,通过ack反馈给发送方,告诉发送方下次发送数据的速度(窗口大小)该如何调整而接收方通过自己的接受缓冲区剩余空间的大小,作为ack中窗口大小的数值,发送方接受到ack后,就会根据这个数值,来调整自己窗口的大小。大概流程如图:

       第一次传输1~1000的数据给主机B,主机B收到后根据自己接收缓冲区剩余大小,在ack中设置窗口大小报文,把ack返回给主机A,主机A就知道主机B的接收缓冲区还剩多少,从而调整主机A传输数据的窗口大小,把1001~4000数据批量的传输给主机B,此时主机B的接受缓冲区就满了,然后主机A这边等一定的时间,来接受主机B发送过来的窗口更新的通知,超过这个时间阈值还没等到主机B发来的通知,此时主机A就会发一个窗口探测包给主机B,索要窗口更新通知,等收到窗口更新通知后发现是2000,此时就主机A就发送4001~6000数据给主机B。

       这里16位窗口大小最大就是64k吗?其实不然,子啊TCP报头的选项中,还包含了一个参数:窗口拓展因子;而实际真实要设置的窗口大小是16位窗口大小 * 2^窗口扩展因子


三、拥塞控制


       拥塞控制和流量控制的本质都一样:限制发送方发送数据的速率。不过这里是站在有中间路径的角度看的

       我们知道,网络是非常复杂的,通过网络从A机器发送数据给B机器,中间要经过很多的路由器、交换机,其中这中间的设备情况都不一样,有些设备的负载不大,数据能正常传输,但有些设备可能负载很大,已经到它的极限了,这时候就会出现丢包的情况。如图:

       而拥塞控制就要考虑这种情况,然后调整发送方拥塞窗口的大小

       因为这里类似木桶效应,木桶能装多少水,取决于短板;上面的图也是类似的道理,有很多条路径传输数据的速度非常快,但有其中一个路径的设备高负载了,到极限了,只要数据经过这,就会阻塞,也就必须要调整发送方的窗口大小

       因为传输数据要经过中间路径,所以啥时候回堵塞,我们不能知道,是随机的这里为了调整窗口的大小,就只能 实验 了

       而窗口的调整也是动态变化的;因为我们无法控制啥时候传输数据的时候会堵塞,所以,只要丢包了,我们就认为数据在中间某个节点上阻塞了,从而缩小窗口大小,如果没丢包,就逐渐增大窗口大小;我们称为:动态变化。以下是动态变化的规则。

拥塞窗口动态变化的规则

       总的规则:流量控制和拥塞控制谁产生的窗口小,就是谁说的算。以下是拥塞控制产生的窗口大小规则:

       1、慢启动刚开始传输数据的时候,拥塞窗口会设置的非常小,传输的速率也就很小(如果一下子就把窗口设置太大,就可能导致设备的负载过高,从而在传输的过程中阻塞、丢包)。

       2、因为刚启动时的窗口非常小,此时为了提高传输效率,就会增大窗口,而这个窗口是随指数式增大的。

       3、随着窗口增大到一定程度,就不能继续让它指数式的增大了,因为指数式的增长的太快,窗口一下子增大的太大,就会导致网络拥堵、丢包这种情况;这里会设置一个 阈值 到达阈值后,窗口就会成线性式增大

       4、窗口是随着线性式的增大,也不能让它无限的增大如果增大到一定程度,出现了丢包的情况,就要让窗口大小重新回到一个比较小的值,然后回到慢启动这个过程并且这里会根据刚才丢包的窗口大小,重新设置限制指数增长的 阈值

       大概流程如图:

      如果出现丢包了,就要进入上面说的第四阶段,但这里有两个版本的慢启动一是旧版的,窗口大小回到非常小的值,经过指数增长再进入线性增长二是新版的,窗口不会回到非常小的值,也不会回到指数增长阶段,直接是线性增长阶段,指数增长阶段只会出现在刚开始的时候

       整个过程的窗口大小都是动态变化的,这个过程也非常像谈恋爱的过程


四、延时应答


       基于滑动窗口,来提高传输效率的延时应答就是发送方批量的发送数据给接收方,接收方收到这些数据,并不会一个一个ack的发送给发送方,而是等一段时间,等接收方处理一段时间刚发过来的数据后,才返回一个ack给发送方。为什么呢?

      原因就是接收方接收到数据,不是就直接处理这个数据的,而是先放到接收缓冲区中,然后再处理数据,如果等一段时间,接收缓冲区的数据就会变少,因为留有一定的时间把它们给处理了那返回ack中会带有窗口大小的报文,这个报文就是根据接收缓冲区的剩余空间来设置窗口大小的因为留有一段时间处理缓冲区的数据,所以接收缓冲区的剩余空间就变大了,窗口大小也会变大,传输数据的速度也会变快

       上面讨论过滑动窗口如果ack丢了的情况,这种情况没关系,因为有确认序号,所以丢了也没事;所以延时应答这里,就设置每处理几个数据,就返回一个ack回去,从而起到延时的效果,当然,这里等处理几个数据的时间也不是无限等的,而是会设置一个时间阈值,如果超过这个时间阈值,不管接收方还剩多少个数据没处理,都会返回ack

       所以延时应答的核心通过在允许的范围内(延时返回ack),增大滑动窗口的大小(提高数据的传输效率)。


五、捎带应答


       基于延时应答,用来提高数据传输效率的在数据传输中,如果发送方发送数据过来,我接收到了,然后因为延时应答,我可以等一段时间再返回ack回去,但因为有等待这一时间,就可以把ack连同响应,一起返回给发送方。大概流程如图:

       

      这里ack延时的这段时间结束后,服务器刚好把请求计算完,准备返回响应给客户端,这时候就可以把ack和response一起返回回去。当然,如果返回ack和发送请求的时机一样,也可以合并,就成了如图:

       因为延时应答,所以只要延时后ack返回的时机和发送数据的时机相同,触发了捎带应答,就可以把它们合并到一起进行发送。所以,基于延时应答+捎带应答,是可以把四次挥手变成3次的:fin fin+ack ack

       捎带应答啥时候触发,具体还得是看你代码具体咋写,下次发送过来的数据快不快,接受方处理数据的速度快不快


六、面向字节流


       这里核心要解决的问题:粘包问题

      因为TCP是传输数据的时候是以字节为单位进行传输的,那如果多次传输数据过去,这些数据就会在接收缓冲区中,因为全都是字节,在接收缓冲区中就无法辨哪到哪是一个完整的应用层数据包,这就是 粘包问题。如图:

其中解决粘包问题有两种方法

       (1)通过特殊符号作为分隔符例如上图说的用空白作为分隔符,如果遇到这个分隔符了,就说明一个包结束了。

       (2)指定包的长度会使用一个特殊的空间,表示一个数据包的长度,如图:

               

       对于 粘包问题,TCP协议是不会解决的要程序员自己编写代码解决这个问题,可以用上面这两种方法解决。

       而UDP协议就不会存在这种问题因为UDP协议面向的是数据报发送数据的单位是数据报,就能分隔开不同的数据内容;而UDP的接受缓冲区不是像上面这样的,而是类似数据结构中的链表,把每个的数据报给连接起来,如图:


七、异常情况


       异常的严重程度也是有高低之分的,一种是丢包了的异常,如果是丢包了,就直接触发TCP里的超时重传特性就好了;但也还有更严重的情况:网络故障,这该如何解决呢?

以下有不同网络故障的情况,其中也会介绍如何解决:

(1)进程崩溃了

       进程无论是正常关闭,还是异常崩溃,都会进行资源回收,也就是关闭文件的效果(系统内完成的),所以,如果是进程崩溃了,进程崩溃的一方肯定会执行到类似socket.close()的效果,也就是说一定能把 fin 给发送出去;而且 TCP连接 的生命周期比进程的生命周期长,这样也就能完成四次挥手的操作,把连接给断开了(双方把保存对端的信息给删除掉)。

(2)其中一方关机了(正常关机)

       当有个主机关机,就会强制杀死系统中所有的进程,进程关闭,也就一定会给对端发送 fin,对方接收到,也就会进行四次挥手的流程,但这里四次挥手不一定能挥完

       1、如果四次挥手挥的快,在系统关机前,把最后一个ack发送过去,就能完成四次挥手

       2、如果系统提前关机了,那也没事,至少把fin发送出去了,对端接收到 fin ,就会把ack和fin也返回回来,但是因为已经关机了,最后一个ack肯定不能给对端发送过去这时候对端很久都没有收到ack,就会触发超时重传,当重传几次后,还没有收到,对端就会单方面把连接给断开了,删除连接信息

(3)其中一方断电了

直接断电了,肯定就是来不及发送fin了,这里分为两种情况

1、接收方断电了

       发送方就会突然发现,ack没了,发送方就会尝试重传,重传几次后,发现还是没有ack返回回来,就会触发 “复位” 连接进行重连,要是重连不成功,发送方就会知道对方挂了,然后进行单方面的断开连接。复位连接需要用到RST复位报文段,如图:第四个

       复位连接:清除原来TCP的各种临时数据,重新开始连接,类似过去既往不咎的效果(此时的RST不会有ack的返回)。

剩下两个报文段:URG、PSH的简单介绍

       URG:与TCP带外数据有关系,TCP中有些特殊的数据包,携带一些特殊的功能。

      PSH:催促对方快点发消息给我。

2、发送方断电了

       接收方本身就是在阻塞等待对方发消息过来,如果迟迟没有信息回来,接收方就要试探一下,对方是不是挂了,这里就会使用 “心跳包” 来咨询对方情况心跳包也不带应用层的数据的,是一个特殊的数据包,带有周期性的发送给对端,这个周期、频率程序员是可以自己设置的当发送心跳包后, 没有返回 “心跳” ,就视为对方挂了;如果对端挂了,也就会进行单方面的断开连接。

(4)断网了(网线断开)

       结合接收方和发送方都断电的情况

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
4天前
|
机器学习/深度学习 网络协议 网络性能优化
[计算机网络]深度学习传输层TCP协议
[计算机网络]深度学习传输层TCP协议
11 1
|
4天前
|
网络协议 网络架构 数据格式
网络原理,网络通信以及网络协议
网络原理,网络通信以及网络协议
8 1
|
1天前
|
网络协议
逆向学习网络篇:心跳包与TCP服务器
逆向学习网络篇:心跳包与TCP服务器
2 0
|
4天前
用UDP协议实现发送接收的网络聊天室
用UDP协议实现发送接收的网络聊天室
8 0
|
4天前
|
机器学习/深度学习 安全 数据安全/隐私保护
【计算机网络】深度学习HTTPS协议
【计算机网络】深度学习HTTPS协议
6 0
|
4天前
|
机器学习/深度学习 前端开发 JavaScript
【计算机网络】深度学习使用应用层的HTTP协议
【计算机网络】深度学习使用应用层的HTTP协议
5 0
|
1月前
|
消息中间件 Java Linux
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
|
18天前
|
网络协议 算法 Linux
【嵌入式软件工程师面经】Linux网络编程Socket
【嵌入式软件工程师面经】Linux网络编程Socket
37 1
|
3天前
|
安全 物联网 Linux
学习Linux对网络安全的重要性
**学习Linux对网络安全至关重要:** 1. 开源操作系统广泛应用于服务器、网络设备,掌握Linux是安全专家必备技能。 2. Linux内置安全特性,如最小权限和防火墙,加上丰富的安全工具,提供强大保障。 3. 可定制性允许灵活配置,满足安全需求,开源社区提供持续更新和教育资源。 4. 学习Linux能提升攻防能力,用于系统加固和渗透测试,适应跨平台安全场景。 5. 随着云计算和物联网发展,Linux在网络安全中的角色日益关键。
27 3
|
21天前
|
Linux 数据安全/隐私保护 Windows
linux 搭建cloudreve win映射网络驱动器WebDav
linux 搭建cloudreve win映射网络驱动器WebDav
26 1

热门文章

最新文章