本文内容以下:
1)TCP协议概念
2)TCP头部结构和字段介绍
3)TCP流量控制
滑动窗口
4)TCP拥塞控制
慢启动、拥塞避免、快重传、快恢复html
有关TCP的三次握手四次挥手单独写了一篇博客:【TCP协议】---TCP三次握手和四次挥手程序员
有关TCP粘包和黏包,也单独写一篇博客,下一篇博客就写有关粘包黏包问题。算法
TCP(Transmission Control Protocol 传输控制协议)是一种面向链接(链接导向)的、可靠的、 基于IP的传输层协议。缓存
首先来看看OSI的七层模型网络
咱们须要知道TCP工做在网络OSI的七层模型中的第四层——传输层,IP在第三层——网络层,ARP 在第二层——数据链路层;同时,咱们须要简单的知道,数据从并发
应用层发下来,会在每一层都会加上头部信息,进行 封装,而后再发送到数据接收端。这个基本的流程你须要知道,就是每一个数据都会通过数据的封装和解封 装的过程。 socket
在OSI七层模型中,每一层的做用和对应的协议以下:高并发
从上面图片能够看出,TCP协议是封装在IP数据包中。post
下图是TCP报文数据格式。TCP首部若是不计选项和填充字段,它一般是20个字节。性能
下面分别对其中的字段进行介绍:
源端口和目的端口
各占2个字节,这两个值加上IP首部中的源端IP地址和目的端IP地址惟一肯定一个TCP链接。有时一个IP地址和一个端口号也称为socket(插口)。
序号(seq)
占4个字节,是本报文段所发送的数据项目组第一个字节的序号。在TCP传送的数据流中,每个字节都有一个序号。例如,一报文段的序号为300,并且数据共100字节,
则下一个报文段的序号就是400;序号是32bit的无符号数,序号到达2^32-1后从0开始。
确认序号(ack)
占4字节,是指望收到对方下次发送的数据的第一个字节的序号,也就是指望收到的下一个报文段的首部中的序号;确认序号应该是上次已成功收到数据字节序号+1。
只有ACK标志为1时,确认序号才有效。
数据偏移
占4比特,表示数据开始的地方离TCP段的起始处有多远。实际上就是TCP段首部的长度。因为首部长度不固定,所以数据偏移字段是必要的。数据偏移以32位为长度单位,
也就是4个字节,所以TCP首部的最大长度是60个字节。即偏移最大为15个长度单位=1532位=154字节。
保留
6比特,供之后应用,如今置为0。
6个标志位比特
① URG:当URG=1时,注解此报文应尽快传送,而不要按原本的列队次序来传送。与“紧急指针”字段共同应用,紧急指针指出在本报文段中的紧急数据的最后一个字节的序号,
使接管方能够知道紧急数据共有多长。
② ACK:只有当ACK=1时,确认序号字段才有效;
③ PSH:当PSH=1时,接收方应该尽快将本报文段当即传送给其应用层。
④ RST:当RST=1时,表示出现链接错误,必须释放链接,而后再重建传输链接。复位比特还用来拒绝一个不法的报文段或拒绝打开一个链接;
⑤ SYN:SYN=1,ACK=0时表示请求创建一个链接,携带SYN标志的TCP报文段为同步报文段;
⑥ FIN:发端完成发送任务。
窗口
TCP经过滑动窗口的概念来进行流量控制。设想在发送端发送数据的速度很快而接收端接收速度却很慢的状况下,为了保证数据不丢失,显然须要进行流量控制, 协调好
通讯双方的工做节奏。所谓滑动窗口,能够理解成接收端所能提供的缓冲区大小。TCP利用一个滑动的窗口来告诉发送端对它所发送的数据能提供多大的缓 冲区。窗口大小为
字节数起始于确认序号字段指明的值(这个值是接收端正指望接收的字节)。窗口大小是一个16bit字段,于是窗口大小最大为65535字节。
检验和
检验和覆盖了整个TCP报文段:TCP首部和数据。这是一个强制性的字段,必定是由发端计算和存储,并由收端进行验证。
紧急指针
只有当URG标志置1时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。
在看这里,为了更好的理解,建议先了解TCP三次握手,四次挥手。博客:【TCP协议】---TCP三次握手和四次挥手
TCP流量控制主要是针对接收端的处理速度不如发送端发送速度快的问题,消除发送方使接收方缓存溢出的可能性。
TCP流量控制主要使用滑动窗口协议,滑动窗口是接受数据端使用的窗口大小,用来告诉发送端接收端的缓存大小,以此能够控制发送端发送数据的大小,从而达到流量
控制的目的。这个窗口大小就是咱们一次传输几个数据。对全部数据帧按顺序赋予编号,发送方在发送过程当中始终保持着一个发送窗口,只有落在发送窗口内的帧才容许被发送;
同时接收方也维持着一个接收窗口,只有落在接收窗口内的帧才容许接收。这样经过调整发送方窗口和接收方窗口的大小能够实现流量控制。
咱们能够经过下图来分析:
1、发送方接收到了对方发来的报文 ack = 33, win = 10,知道对方收到了 33 号前的数据,如今指望接收 [33, 43) 号数据。那咱们开始发送[33, 43) 号的数据。
2、[33, 43) 号的数据你是已经发送了,但接受方并无接受到[36,37]数据。因此接收方发送回对报文段 A 的确认:ack = 35, win = 10。
3、发送方收到了 ack = 35, win = 10,对方指望接收 [35, 45) 号数据。那么发送方在发送[35, 45) 。
这里面须要思考一个问题?
第一步发送了[33, 43),若是此次发送[35, 45),那中间重叠部分不是发送了两次,因此这里要思考: 是所有从新发送仍是只发送接收端没有收到的数据,若是所有发送那么重复
发送的数据接收端怎么处理。这个下面快速重传会讲。
4、接收方接收到了报文段 [35, 41),接收方发送:ack = 41, win = 10. (这是一个累积确认)
5、发送方收到了 ack = 41, win = 10,对方指望接收 [41, 51) 号数据。
6、.......
这样一直传输数据,直到数据发送完成。这么一来就保证数据数据的可靠性,由于若是某数据没有获取到,那么ack永远不会跳过它。
这里也要思考一个问题,若是某一数据一只没有获取到,总不能一直这样堵塞在这里吧,这里就要讲接下来有关堵塞的解决方法。
流量控制是经过接收方来控制流量的一种方式;而拥塞控制则是经过发送方来控制流量的一种方式。
TCP发送方可能由于IP网络的拥塞而被遏制,TCP拥塞控制就是为了解决这个问题(注意和TCP流量控制的区别)。
TCP拥塞控制的几种方法:慢启动,拥塞避免,快重传和快恢复。
这里先理解一个概念: 拥塞窗口
拥塞窗口:发送方维持一个叫作拥塞窗口 cwnd的状态变量。拥塞窗口的大小取决于网络的拥塞程度,而且动态变化。
发送方的让本身的发送窗口=min(cwnd,接受端接收窗口大小)。说明: 发送方取拥塞窗口与滑动窗口的最小值做为发送的上限。
发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减少一些,以减小
注入到网络中的分组数。
下面将讨论拥塞窗口cwnd的大小是怎么变化的。
TCP在链接过程的三次握手完成后,开始传数据,并非一开始向网络通道中发送大量的数据包。由于假如网络出现问题,不少这样的大包会积攒在路由器上,很容易致使网
络中路由器缓存空间耗尽,从而发生拥塞。所以如今的TCP协议规定了,新创建的链接不可以一开始就发送大尺寸的数据包,而只能从一个小尺寸的包开始发送,在发送和数据被
对方确认的过程当中去计算对方的接收速度,来逐步增长每次发送的数据量(最后到达一个稳定的值,进入高速传输阶段。相应的,慢启动过程当中,TCP通道处在低速传输阶段),
以免上述现象的发生。这个策略就是慢启动。
画个简单的图从原理上粗略描述一下
咱们思考一个慢启动引发的性能问题?
在海量用户高并发访问的大型网站后台,有一些基本的系统维护需求。好比迁移海量小文件,就是从一些机器拷贝海量小碎文件到另外一些机器,来完成一些系统维护的基本需求。
慢启动为何会对拷贝海量小文件的需求形成重大性能损失?
举个简单的例子,咱们对每一个文件都采用独立的TCP链接来传输(循环使用scp拷贝就是这个例子的实际场景,很常见的用法)。那么工做过程应该是,每传输一个文件创建一个
链接,而后链接处于慢启动阶段,传输小文件,每一个小文件几乎都处于独立链接的慢启动阶段被传输,这样传输过程所用的TCP包的总量就会增多。更细致的说一说这个事,若是在
慢启动过程当中传输一个小文件,咱们可能须要2至3个小包,而在一个已经完成慢启动的TCP通道中(TCP通道已进入在高速传输阶段),咱们传输这个文件可能只须要1个大包。
网络拷贝文件的时间基本上所有消耗都在网络传输的过程当中(发数据过去等对端ACK,ACK确认归来继续再发,这样的数据来回交互相比较本机的文件读写很是耗时间),撇开三次
握手和四次握手那些包,若是文件的数量足够大,这个总时间就会被放大到需求难以忍受的地步。
所以,在迁移海量小文件的需求下,咱们不能使用“对每一个文件都采用独立的TCP链接来传输(循环使用scp拷贝)“这样的策略,它会使每一个文件的传输都处于在一个独立TCP的慢启
动阶段。
如何避免慢启动,进而提高性能?
很简单,尽可能把大量小文件放在一个TCP链接中排队传输。起初的一两个文件处于慢启动过程传输,后续的文件传输所有处于高速通道中传输,用这样的方式来减小发包的数
目,进而下降时间消耗。一样,实际上这种传输策略带来的性能提高的功劳不只仅归于避免慢启动,事实上也避免了大量的3次握手和四次握手,这个对海量小文件传输的性能消耗
也很是致命。
先补充下: 慢启动中拥塞窗口的cwnd值,开始是1,接下开是指数型增涨的。一、二、四、八、16.....这样涨太快了吧。那么就有了堵塞避免。
cwnd不能一直这样无限增加下去,必定须要某个限制。TCP使用了一个叫慢启动门限(ssthresh)的变量,一旦cwnd>=ssthresh(大多数TCP的实现,一般大小都是65536),慢
启动过程结束,拥塞避免阶段开始;
拥塞避免:cwnd的值再也不指数级往上升,开始加法增长。此时当窗口中全部的报文段都被确认时,cwnd的大小加1,cwnd的值就随着RTT开始线性增长,这样就能够避免增加过
快致使网络拥塞,慢慢的增长调整到网络的最佳值。(它逻辑很简单就是到必定值后,cwnd不在是指数增加,而是+1增加。这样显然慢多了)。
非ECN环境下的拥塞判断,发送方RTO超时,重传了一个报文段,它的逻辑以下:
1)把ssthresh下降为cwnd值的一半。
2)把cwnd从新设置为1。
3)从新进入慢启动过程。
上面的图仍是蛮好理解的。
TCP要保证全部的数据包均可以到达,因此,必须要有重传机制。
注意: 接收端给发送端的Ack确认只会确认最后一个连续的包,好比,发送端发了1,2,3,4,5一共五份数据,接收端收到了1,2,因而回ack 3,而后收到了4(注意此时3没收到)
此时的TCP会怎么办?咱们要知道,由于正如前面所说的,SeqNum和Ack是以字节数为单位,因此ack的时候,不能跳着确认,只能确认最大的连续收到的包,否则,发送端
就觉得以前的都收到了。
3.1)超时重传机制
一种是不回ack,死等3,当发送方发现收不到3的ack超时后,会重传3。一旦接收方收到3后,会ack 回 4——意味着3和4都收到了。
可是,这种方式会有比较严重的问题,那就是由于要死等3,因此会致使4和5即使已经收到了,而发送方也彻底不知道发生了什么事,由于没有收到Ack,因此,发送方可能会
悲观地认为也丢了,因此有可能也会致使4和5的重传。
对此有两种选择:
① 一种是仅重传timeout的包。也就是第3份数据。
② 另外一种是重传timeout后全部的数据,也就是第3,4,5这三份数据。
这两种方式有好也有很差。第一种会节省带宽,可是慢,第二种会快一点,可是会浪费带宽,也可能会有无用功。但整体来讲都很差。由于都在等timeout,timeout可能会很长。
3.2)快速重传机制
因而,TCP引入了一种叫Fast Retransmit的算法,不以时间驱动,而以数据驱动重传。也就是说,若是,包没有连续到达,就ack最后那个可能被丢了的包,若是发送方连续收到
3次相同的ack,就重传。Fast Retransmit的好处是不用等timeout了再重传,而是只是三次相同的ack就重传。
好比:若是发送方发出了1,2,3,4,5份数据,第一份先到送了,因而就ack回2,结果2由于某些缘由没收到,3到达了,因而仍是ack回2,后面的4和5都到了,可是仍是ack回2
由于2仍是没有收到,因而发送端收到了三个ack=2的确认,知道了2尚未到,因而就立刻重转2。而后,接收端收到了2,此时由于3,4,5都收到了,因而ack回6。示意图以下
Fast Retransmit只解决了一个问题,就是timeout的问题,它依然面临一个艰难的选择,就是重转以前的一个仍是重装全部的问题。对于上面的示例来讲,是重传#2呢仍是重传
#2,#3,#4,#5呢?由于发送端并不清楚这连续的3个ack(2)是谁传回来的?也许发送端发了20份数据,是#6,#10,#20传来的呢。这样,发送端颇有可能要重传从#2到
#20的这堆数据(这就是某些TCP的实际的实现)。可见,这是一把双刃剑。
总结: 无论是超时重传仍是快速重传确实能保证数据的可靠性,但它没法解决的问题就是:好比发送端发了一、二、三、四、5,而接收端收到了一、三、四、5,那么这个时候它发送的
ack是2。那么发送端发送的是重传#2呢仍是重传#2,#3,#4,#5的问题。若是在发送#2,#3,#4,#5,自己资源是一种浪费,由于接受方#3,#4,#5已经缓存下来,只需
#2,因此在发一遍是无心义的。
有关TCP协议固然远远不止于此,之后随着对它认识愈来愈清楚以后,也会再次补充,或者在写一篇。