- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
前言: 上篇,我们写了 TCP 的三大特性,本篇将 TCP 的其他7大特性进行讲述!
滑动窗口机制,是在可靠性的前提下,进一步的提高传输效率
一发一收的方式: TCP 协议需要对数据进行确认后,才可以发送下一个数据包,如图:
如上图,发送端每发送一个数据包,都需要得到接收端的确认应答以后,才可以发送下一个数据包,是一问一答的串行过程;即每次传输数据都需要等待一个对应的等待时间,那么传输 N 份数据,就需要等待 N 次应答时间,总的传输时间:N 份数据传输时间 + N 份应答传输时间
一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了),如图:
滑动窗口本质上是批量传输数据
总的传输时间:N 份数据传输时间重叠成了一份时间,N 份应答传输时间重叠成了一份时间
相当于把多份数据的传输时间和等待 ACK 的时间压缩成一份了,总的等待时间少了,传输效率也就高了
窗口
窗口大小: 不等待 ACK 的情况下,批量发送的最大数据量,就叫"窗口大小" (如上图,就是4000个字节,4个字段)
发送前四个字段的时候,不需要等待任何 ACK,直接发送;
收到第一个ACK后,滑动窗口向后移动,继续发送第五个字段的数据,依次类推;
操作系统内核为了维护这个滑动窗口;需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答;只有确认应答过的数据,才能从缓冲区删掉;
窗口越大, 则网络的吞吐率就越高
滑动:
窗口范围内的数据是在等待这些数据的 ACK (已经被发送出去)
如上图,当发送方收到 2001 的 ACK,意味着 1001 - 2000 的数据对方已经接收,此时立刻继续传输 5001 - 6000 的数据,则等待 ACK 的数据包的序号就是 2001,3001,4001,5001
丢包的两种情况如图:
**情况1:**确认应答 (ACK) 丢包
不需要进行额外的处理;在这种情况下,部分 ACK 丢了并不要紧,因为可以通过后续的 ACK进 行确认
理解"确认序号"的含义
从当前序号开始,前面的数据都已经正确到了
如上图,是 1001 的 ACK 丢失,2001 的 ACK 没丢,此时,发送方收到 2001 之后,就会认为 1 - 1000 这个数据也是顺利到达的,1001 丢了无所谓,2001 的 ACK 能够包含 1001 ACK 中的信息
**情况2:**数据包丢失
如下图,若是 1001-2000 丢失,然后 2001-3000,3001-4000 等后边的几个数据都顺利到达,此时主机 B 反馈的 ACK 的确认序号始终是 1001;
此时若主机 A 发现连续几个 ACK 都是1001,主机 A 就知道,1001 这个数据丢失,就会重传 1001
在重传 1001 之前,收到的确认序号都是 1001
当主机 B 收到 1001 这个数据的时候,由于 2001-7000 这些数据前边已经收到过,接下来的 ACK 就从 7001 开始
此处的重传,只是重传丢了的数据,其他数据不需要额外重传—— 快速重传(搭配滑动窗口下的超时重传)
"乱序"传输:
流量控制: 根据接收方的处理能力来反向制衡发送方的发送效率(窗口大小) (通过接收缓冲区的 “剩余空间大小” 来决定发送方的速率)
窗口大小不能无限大,传输速率太快,接收方可能处理不过来
在使用滑动窗口机制进行数据传输时,发送方根据实际情况发送数据包,接收端接收数据包;由于接收端处理数据包的能力是不同的,因此可能出现两种情况:
(第一次的窗口大小是根据链路带宽的大小来决定的) ,发送数据包,接收端接收这些数据包,并返回确认应答包,告诉发送端自己下次希望收到的数据包是多少(新的窗口大小),发送端收到确认应答包以后,将以该窗口大小进行发送数据包
如下图:
图解:
窗口探测:
拥塞控制是考虑网络传输路径上的拥堵程度
虽然TCP有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据;但是如果在刚开始阶段就发送大量的数据,仍然可能引发问题;因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵,在不清楚当前网络状态下,贸然发送大量的数据,是很有可能雪上加霜
因此,TCP引入 慢启动 机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据
线增积减**(和式增加,积式减少)**
像上面这样的拥塞窗口增长速度,是指数级别的,“慢启动” 只是指初使时慢,但是增长速度非常快,具体的增长如下图:
刚开始的时候从1指数增长,到达阈值后开始线性增长,如果出现网络阻塞,直接减小到初始值,然后再次指数增长到达新的阈值(新阈值为上次阻塞窗口大小的一半),再次线性增长直到网络阻塞,一直这样动态变换循环
为啥要动态变化??
网络的拥堵情况是瞬息万变的,我们要随时根据网络的实际情况进行动态调整 (随时适应网络的变化过程)
(数学推导)
目的是为了提高效率,在流量控制的基础上,尽量返回一个合理但又比较大的窗口
在前面我们提到,在发送端发送数据后,接收数据的主机需要返回 ACK应答,此时若立刻返回,窗口可能比较小 (因为缓冲区的数据只处理了一部分),所以 TCP 采用了延迟应答机制,举例:
不带延迟应答:
有一个超市,假设泡面库存最多存放100箱,当前已经存了80箱,空余20箱;
第二天一早,送货小哥来问:“老板,你明天需要多少面?”
老板:“你最多送20箱就行。”
带延迟应答:
第二天一早,送货小哥来问:“老板,你明天需要多少面?”
老板:“我晚上的时候打电话告诉你需要多少。”
(可能白天又卖出了10箱,第二天最多送10箱即可)
延时应答其实就是让 ACK 的发送时间晚一会儿 (不影响可靠性的前提下)
延迟的时间中就会给应用程序提供更多的消费数据的机会,此时时间到了,再发送 ACK 的时候,得到的窗口大小(接收缓冲区的剩余空间就会更大一些)
窗口越大,网络吞吐量就越大,传输效率就越高;我们的目标是在保证网络不拥塞的情况下尽量提高传输效率
问:所有的包都可以延迟应答么?
肯定不是
.
1.数量限制: 每隔 N 个包就应答一次 (N一般为2)
2.时间限制: 超过最大延迟时间就应答一次 (时间一般取200 ms,延迟应答的等待时间不能超过超时重传的时间,不然就重传了)
在延迟应答的基础上,为了进一步提高程序运行效率而引入的机制
在很多情况下,客户端和服务器的通信模式一般都是 Request - Response 模式,即 “一问一答”
如图:
注意:
三次握手中间的 SYN 和 ACK 都是由内核决定的,不涉及不同的时机
上述提到的四次挥手的过程,ACK 是内核决定的,发的 FIN (close方法) 是应用程序代码决定的
严格说,粘包问题不是 TCP 自身的机制,而是面向字节流传输所具备的共性问题
粘包,指粘的是应用层数据包,导致数据在处理的时候,容易读取半个应用层数据包
面向字节流: 指的是一次读一个字节,或者一次读两个字节,或者一次读 N 个字节都行
举例: 双方建立连接,需要在连接后一段时间内发送不同结构数据,如连接后,有好几种结构
读多少个字节才是一个完整的应用层数据报,这个是不清楚的
若一次读一个汉字,读出来就是 “好”;若一次读三个汉字,读出来就是 “好个P”
读法不一样,最终的含义差异也很大;读取应用层数据,就不应该只读半个包
归根结底就是一句话,明确两个包之间的边界
TCP 协议本身不帮你区分应用层数据包,相对而言,UDP 协议没这个问题 (UDP 协议就是按照数据包为单位进行收发的)
比如,上述回答改为 “好个P;”
用分号 ;当做两个包的分隔符,读数据,一直读到分号;才认为是一个完整的应用层数据包
应用层协议,是程序猿自己来定的,只要保证分隔符不和正文冲突即可**
比如,上述用例改为 “4你好不好3好个P”
先读取最开始的四个字节,得到包的长度3;继续读取3个汉字,于是就读取一个完整的包
HTTP 协议基于 TCP 的应用层协议,自己就会处理好粘包问题,上述两种方式都使用到了:
.
对于 GET 请求,分隔符就是空行
对于 POST 请求,Content-length 来指定包的长度
**思考:**对于UDP协议来说,是否也存在 “粘包问题” 呢?
双方建立交互的连接,并不是一直存在数据交互,有些连接会在数据交互完毕后,主动释放连接,而有些不会,那么在长时间无数据交互的时间段内,交互双方都有可能出现掉电、死机、异常重启,还是中间路由网络无故断开、NAT超时等各种意外
在这些 “异常情况” 下,TCP 对于连接会有一些特殊的处理
举例:
1.进程崩溃: 这种情况,TCP 连接会正常四次挥手 (只要是进程退出,都会自动关闭相关的文件)
2.主机关机(按照流程关机): 关机的时候会强制先杀进程,杀进程过程之中就要进行四次挥手了
3.主机断电 / 网线断开:
TCP 之所以复杂,是因为它既要保证可靠性,同时又尽可能的提高性能
可靠性:
校验和,序列号
确认应答,超时重传
连接管理,流量控制,拥塞控制
提高性能:
滑动窗口,快速重传
延迟应答,捎带应答
其他:
定时器(超时重传定时器,保活定时器,TIME_WAIT定时器等)
广播的原则 如果两个数组的后缘维度(从末尾开始算起的维度)的轴长度相符或其中一方的长度为1,则认为它们是广播兼容的。广播会在缺失维度和(或)轴长度为1的维度上进行。 在上面的对arr每一列减去列
之前在讲 MySQL 事务隔离性提到过,对于写操作给读操作的影响这种情形下发生的脏读、不可重复读、虚读问题。是通过MVCC 机制来进行解决的,那么MVCC到底是如何实现的,其内部原理是怎样的呢?我们要
我创建了一个 JavaScript 对象来保存用户在 ColorBox 中检查复选框时设置的值。 . 我对 jQuery 和“以正确的方式”编程 JavaScript 比较陌生,希望确保以下用于捕获用
我为了回答aquestion posted here on SO而玩示例,发现很难理解python的import *破坏作用域的机制。 首先是一点上下文:这个问题不涉及实际问题;我很清楚from fo
我想让我的类具有标识此类的参数 ID。例如我想要这样的东西: class Car { public static virtual string ID{get{return "car";}} }
更新:我使用的是 Java 1.6.34,没有机会升级到 Java 7。 我有一个场景,我每分钟只能调用一个方法 80 次。它实际上是由第 3 方编写的服务 API,如果您多次调用它,它会“关闭”(忽
希望这对于那些使用 Javascript 的人来说是一个简单的答案...... 我有一个日志文件,该文件正在被一个脚本监视,该脚本将注销中的新行提供给任何连接的浏览器。一些人评论说,他们希望看到的更多
我们正在开发针对 5.2 开发的 PHP 应用程序,但我们最近迁移到了 PHP 5.3。我们没有时间去解决所有迁移到 PHP 5.3 的问题。具体来说,我们有很多消息: Declaration of
简介 在实现定时调度功能的时候,我们往往会借助于第三方类库来完成,比如: quartz 、 spring schedule 等等。jdk从1.3版本开始,就提供了基于 timer 的定时调度功能。
Java中,一切都是对象,在分布式环境中经常需要将Object从这一端网络或设备传递到另一端。这就需要有一种可以在两端传输数据的协议。Java序列化机制就是为了解决这个问题而
我将编写自己的自定义控件,它与 UIButton 有很大不同。由于差异太大,我决定从头开始编写。所以我所有的子类都是 UIControl。 当我的控件在内部被触摸时,我想以目标操作的方式触发一条消息。
在我的代码中,在创建 TIdIMAP4 连接之前,我设置了一大堆 SASL 机制,希望按照规定的“最好到最差”顺序,如下所示: IMAP.SASLMechanisms.Add.SASL := mIdS
在 Kubernetes 中,假设我们有 3 个 pod,它们物理上托管在节点 X、Y 和 Z 上。当我使用“kubectl expose”将它们公开为服务时,它们都是集群中的节点(除了 X、Y 和
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 9 年前。 Improve this ques
我知道进程间通信 (ipc) 有几种方法,例如: 文件 信号 socket 消息队列 管道 命名管道 信号量 共享内存 消息传递 内存映射文件 但是我无法找到将这些机制相互比较并指出它们在不同环境中的
当我尝试连接到 teradata 时,出现了TD2 机制不支持单点登录 错误。 在 C# 中,我遇到了类似的问题,我通过添加 connectionStringBuilder.Authetication
我有一个带有 JSON API 的简单 Javascript 应用程序。目前它在客户端运行,但我想将它从客户端移动到服务器。我习惯于学习新平台,但在这种情况下,我的时间非常有限 - 所以我需要找到绝对
我想了解事件绑定(bind)/解除绑定(bind)在浏览器中是如何工作的。具体来说,如果我删除一个已经绑定(bind)了事件的元素,例如使用 jQuery:$("#anElement").remove
我不是在寻找具体答案,只是一个想法或提示。我有以下问题: Android 应用程序是 Web 服务的客户端。它有一个线程,通过 http 协议(protocol)发送事件(带有请求 ID 的 XML
我正在研究 FreeBSD TCP/IP 栈。似乎有 2 种 syn flood 机制,syncookies 和 syncache。我的问题是关于 syncookies,它是从头开始还是在 SYN 队
我是一名优秀的程序员,十分优秀!