JavaEE 网络原理——TCP的工作机制(末篇 其余TCP特点)


其余相关文章: JavaEE 网络原理——TCP的工作机制(中篇 三次握手和四次挥手)

本篇文章衔接的是前面两篇文章的内容,在这里继续解释 TCP 的内部原理。

一、滑动窗口

我们知道,可靠性和传输效率是相互矛盾的
对此,我们就需要在保证可靠性的基础上,来尽可能的提高传输效率。(尽量降低效率的折损)
在这里插入图片描述
如图所示:对于基本的确认应答的情况来讲,每次发送一个数据,都需要进行等待,再到 ack 到达后才会发送下一个。

因此,为了解决上述的问题,滑动窗口就此诞生。如图:
在这里插入图片描述
滑动窗口的本质是:不等待的批量发送一组数据,然后使用一份时间来等待着数组和多个 ACK。
窗口大小: 将不需要等待,就直接可以发送的数据的最大的量,称之为 “窗口大小”。

  • 问题1
    当批量发送窗口大小这些数据后,发送方就要等待 ack 了。
    呢么,问题来了,什么时候继续向下发送?等待什么时候结束?

不是说,等待到所有的 ack 到达,才能继续向下发送,而是到一个 ack ,就会继续向下发送下一条。(此处的等待的 ack 始终是 4 条)
这里简单举出一个例子:
假设本人当前是一个客服的工作人员,需要对用户的问题进行逐一的回答。(本人只能一次和 4 个用户进行沟通)
简单分析:
此处,这里的 4 就是窗口大小。
是将这里的 4 个客户都解决完毕后,才继续后面的 4 个客户吗? 很显然这里不是这样的!!!
而是在这 4 个客户中,只要有一个客户的问题解决后,就可以开启和下一个客户交流了。(始终是同时在和 4 个客户聊天)

具体情况如图:
在这里插入图片描述
如上图所示,本来等待 ack 是 1001 - 5000.
接下来,收到了 2001 这个 ack,此时说明 2001 之前的数据 (1001 - 2000) 已经被确认了。
此时就可以立即发送 5001 - 6000 的数据,此时意味着等待 ack 的范围就是 2001 - 6000。

  • 问题 2
    在滑动窗口的情况下,此时,出现丢包的情况该怎么办?

情况1:返回的 ack 丢失了。
在这里插入图片描述
其实,对于这样的情况,不需要去做任何的处理,问题不大。
关键需要理解的要点在于 确认序号 的含义。
这里的 “确认序号” 所表示的是该序号向前的所有数据都已经确认送达了。
也就是说,这里的 1001 返回的 ack 虽然丢失,但是后面的 2001 处返回的 ack 实际上涵盖了前面从 1 - 1001 处的 ack 信息。

情况2:数据丢失
在这里插入图片描述
如上图所示,1001 - 2000 在传输时出现丢包情况。在接下来的 2001 - 3000 到达主机 B 之后,由 B 给 A 返回的 ACK 确认序号仍然是 1001. (此时和你发送过来的这个数据号是什么,关系已经不大了。)

这里的意思就是在索要 1001 开头的数据。

在这里,对此处的丢包重传方式起了一个新的名字,叫做 “快速重传”(重传操作只是重传了丢失的数据)。
这里的重传机制可以视为是超时重传机制在滑动窗口下的变形。
注:如果此处的传输数据比较密集,就按照滑动窗口的方式来传输。
如果此处的传输数据比较稀疏,就不在按照滑动窗口的方式了,此时就还按照之前的超时重传的形式来处理丢包。

快速重传中的问题

  1. 有关补发数据是否会出现数据顺序混乱的情况。
    解释: 这个问题并不复杂,TCP 中有一个接收缓冲区,在缓冲区中会对数据按照序号进行重新排队。
  2. 以上面图片中的情况为例,假设此时在 2001 - 7001 之间有丢失了一组,此时的情况该是怎样?
    解释: 这里其实也不难理解,当 主机 B 接受到 1001 - 2000 后,就会再次按顺序向后索取缺少的其他范围的数据。

二、流量控制

流量控制作用: 这是一种干预发送窗口大小的机制。
在上面的一个要点中,我向大家讲解了 滑动窗口。对于滑动窗口,窗口越大,传输的效率就会越高。(也就是说,在一份时间中,等待的 ACK 就越多)。
但是,窗口的大小也不能无限大!!!

可能出现的问题如下:

  1. 完全不等待 ack ,在可靠性能否保障画上问号。
  2. 窗口太大,也会消耗大量的系统资源。
  3. 发送的速度太快,接受方处理不过来,发了也没什么用。。。

其实,综上所述,接受方的处理能力,就是一个很重要的约束依据。发送方的速度,不能超出接受方的处理能力!

流量控制,要做的工作就是这个。根据接受方的处理能力,协调发送方的发送速率。

所以,如何衡量接收方的处理能力 就是这里的关键所在

解释: 如何衡量接受方的处理能力

这里有一个最简单直接的方法,直接观察接收方的缓冲区剩余大小。
在这里插入图片描述
如上图所示,每次 A 给 B 发送个数据。
B 就需要计算下一个水池中的剩余空间,然后将这个值通过 ack 报文返回给 A
A 此时就根据这个值来决定接下来发送的速率是多少。(窗口大小是多少)

在这里插入图片描述
通过上图所示,窗口大小就指的是 16 位窗口大小。
但是,这里是否就意味着,窗口大小最大是 64kb?答案显然不是这样的!TCP 为了让窗口更大,在选项部分,引入了窗口拓展因子

解释: 窗口拓展因子如何作用。

假设: 此时,窗口大小已经是 64kb。在拓展因子中写入 2.
这里的意思就是让 64kb << 2 ——> 256 kb

所以,通过上面的分析我们可以知道,接受缓冲区一直是在动态变化的,所以,对应的每次返回的 ack 携带的窗口大小也在变化。对此,发送方也是在不断的动态调整的!!!

特殊情况: 当窗口的大小为 0 时。
此时,发送方就会暂停发送,在暂停发送的等待过程中,会给 B 定期发送窗口探测报文。这个报文不会携带任何的具体业务数据,只是为了触发 ack 查询窗口大小。

三、拥堵控制

这里的拥堵控制,与前面向大家解释的流量控制共同决定发送方的窗口大小的多少。

这里描述的 “发送发窗口大小” 是通过 接收方 的 ack 报文中的 窗口大小字段。是从 接收方 告诉给 发送方 的!

流量控制: 考虑的是接收方的处理能力。
拥塞控制: 考虑的是传输过程中,中间节点的处理能力。

在这里插入图片描述
形如上图所示,对于两台主机 A,B 两者之间传递数据,是需要在中间经过多台设备的

对于接受方的处理能力,是比较好量化衡量的。但是 中间节点 ,不好衡量。

因此,为了解决这个问题,设计 TCP 的大佬们想出了一个非常天才的方法。
既然不好直接量化,呢么就可以通过 “实验” 的方式来测试出一个合适的值。

解释: 如何通过测试获取合适的值。

在这里插入图片描述
如上图所示,这里的 “拥塞窗口” 表述的是,不断尝试要以多大的窗口进行发送。
初始阶段: 由于初始窗口比较小(从1开始),此时每一轮的不丢包,都会使窗口大小扩大一倍(指数增长)。
中间阶段: 当增长率到达阈值时,此时的指数增长就会变成线性增长。(注意,这里增长的前提是不丢包)
调整阶段: 当传输过程中一但产生丢包,呢么说明此时,发送的速率已经接近网络的极限。此时,一下子就会把窗口大小缩成很小的值。(并且重复前面的指数增长和线性增长的过程)
通过上面的分析,可以的出一个结论
拥塞窗口不是一个固定的数值,而是一直动态变化的。随着时间的推移,逐渐达到一个平衡的过程。

上面的动态处理,即可以把问题解决,同时也可以随着网络情况的变化而变化

四、延时应答

延时应答,在这里也是一个为了提高效率的机制
这个机制,也就是对滑动窗口进行补充。

滑动窗口的关键,就是让窗口的大小大一点,传输的速度就快一点。
因此,需要做的就是,在接收方能够处理的了的前提下,尽可能将窗口的大小放大。

延时应答,就是在接受数据后,不是立即返回 ACK 而是稍等一会在返回。
在这个等待的时间中,接受方的应用程序,就可以将缓冲区的数据进行一波消费,此时剩余的空间不就变大了一点。。。
如图:
在这里插入图片描述
这里表现出的延时应答方式,就是在滑动窗口下。ACK 不在对每一条数据进行返回,比如隔一个返回一次。

五、捎带应答

捎带应答也是提高效率的一种方式。
捎带应答的引入,是在延时应答的基础之上的。

我们已经知道,服务器客户端程序,最典型的模型就是 “一问一答”。
如图:
在这里插入图片描述
从上图可以看出,这两个信息根本就是不同时机产生,不同时机发送。

捎带应答,就是在这两条信息中进行动作的。
在之前的延时应答机制中,因为要等待缓冲区中的数据消费。所以,就会导致在等待 ACK 的过程中,B 就要向 A 发送业务数据了。呢么此时,就可以让这个业务数据 捎带着 ACK 一并发送过去 即可。
如图:
在这里插入图片描述

注意:
这里要说的是,“合并发送” 这个事情是成立的,并不是说有了 “捎带应答” 就一定会合并
而 “延时应答” 则是提高了合并的概率。

六、面向字节流

当数据面向字节流时,此时,在这里就引入了一个比较麻烦的事—— 粘包问题

在这里插入图片描述
在前面的说明中,我们已经知道了一个名词 “接收缓冲区”
在上图中的多次交互中,其实所有的交互信息都会被存入到 接受缓冲区 中,这里的多个数据都会被放到一起。
呢么,问题来了
此时应用程序在 read 读取的时候,读取到哪里才算是一个完整的应用层数据报呢?

在这里插入图片描述
如上图所示,我们大家都知道,糯米团子的粘度是很大的。想要单独拿出来一个,很容易就会出现粘连的情况。

TCP 是字节流的。
一次读取一个字节,读取 N 个字节,都是可以的。
在这样的情况下,一次读取的是半个数据报,或整个数据报,或多个数据报的情况都是存在的!

解释: 描述 “粘包” 具体情况。

这里是引用
以上图的情况为例,应用程序调用 read
如果 read 的是 7 个字节,此时正好读出的是 aaaaaaa ,这就是一个完整的数据报。
如果 read 的是 8 个字节,此时读到的就是 aaaaaaab ,读到的就是 “一个半” 数据。
如果 read 的是 6 个字节,此时读到的是 aaaaaa ,读到的就是 半个数据报。

综上所述,粘包问题的影响还是比较大的。
但是,还有一个更令人悲伤的故事。。。
在 TCP 层次,没有在 socket api 中告诉我们应该读取几个字节。具体要怎么读,完全是程序员自己负责。

我们肯定是想读到的是一个完整的一个数据报。
解决问题的方法其实也非常简单,只需要在应用层中约定好应用层协议即可。
尤其是要明确应用层数据报之间的边界就好。

约定的条件有下面的两条
1.约定好分隔符。
2.约定好每个包的长度。

七、异常情况

对于异常情况,直接说明就是在传输过程中出现了不可抗力

对于不可抗力大概有下面几点情况:

1 进程崩溃
2.主机关机(按照正常流程关机)
3.主机断电
4.网线断开

这里我们将上述情况分为下面两部分:

  • 情况 1 和 2
    针对这两种情况,直接对应的是进程没了
    (1)在 进程崩溃 情况下,对应的 PCB 也就没了,对应的文件描述符表也就释放了。相当于 socket.close()。 此时内核会继续完成四次挥手。其实仍然是一个正常断开的流程。
    (2)对于 主机关机 主机关机要先杀进程,然后才会正常关机。(在杀死进程的过程中,也和上面一样会触发四次挥手)。

  • 情况 3 和 4
    针对这两个情况显然是来不及挥手了
    (3)对于 主机掉电 在这里可以分为两类情况。
    假设 接受方 掉电
    发送方 仍然在继续发送数据,发送完需要等待 ack ,但是 ack 始终等不到。。。
    超时重传在怎么重传,也收不到 ack。。。
    在重传几次后,仍然没有应答,此时尝试尝试重置 TCP 的连接。但是很显然这个重置也会失败。
    最终只能放弃连接(单方面放弃)。
    如果是 发送方 掉电
    接受方 发现,没数据了。此时接受方还不知道发送方是一个什么情况。(此时,接收方能做的事就只有一件,等待!)
    在等待一段时间后,接收方 会周期性的向 发送方 发送一个消息。来确认对方是否还在正常工作。

接受方周期性的发送信息,在这里有一个更加形象的名字 心跳包
1.心跳,是一个周期性的。
2.如果心跳无了,呢么就说明挂了
在这里也就是通过心跳包来确认通信双方是否处在一个正常的工作状态中。。

八、总结

到此,本人使用了长达三篇文章对 TCP 中最重要的十个特性进行了描述。但是这里的学习仍然任重道远。还需要反复的思考琢磨来达到更清晰的理解。

码子不易,您小小的点赞是对我最大的鼓励!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值