TCP的数据流大致能够分为两类,交互数据流与成块的数据流。
交互数据流就是发送控制命令的数据流。比方relogin,telnet。ftp命令等等。成块数据流是用来发送数据的包,网络上大部分的TCP包都是这样的包。
非常明显。TCP在传输这两种类型的包时的效率是不一样的,因此为了提高TCP的传输效率,应该对这两种类型的包採用不同的算法。
总之。TCP的传输原则是尽量降低小分组传输的数量。
TCP的交互式数据流
?
经受时延的确认技术
TCP的交互式数据流通常使用“经过时延的确认”技术。通常Server在接收到从Client发送过来的数据时。并不立即发送ACK,而是等一小段时间,看看本机是否有数据要反馈给Client,假设有,就将数据包括在此ACK包中,曾经发送给Client。普通情况下这个时延为200ms。
须要注意的时这个200ms的定时器时相对于内核的时钟滴答的。也就是jeffs的。增加一个数据分组到达后,此定时器已经pass了100ms。那么再过100ms ACK才会被发送,假设在这100ms内有数据要反馈,则在100ms后ACK会和数据一起发送。
? Nagle算法分析。
Nagle算法主要用来预防小分组的产生。
在广域网上,大量TCP小分组极有可能造成网络的拥塞。
Nagle时针对每个TCP连接的。它要求一个TCP连接上最多仅仅能有一个未被确认的小分组。在改分组的确认到达之前不能发送其它小分组。
TCP会搜集这些小的分组,然后在之前小分组的确认到达后将刚才搜集的小分组合并发送出去。
有时候我们必需要关闭Nagle算法,特别是在一些对时延要求较高的交互式操作环境中,全部的小分组必须尽快发送出去。
我们能够通过编程取消Nagle算法。利用TCP_NODELAY选项来关闭Nagle算法。
TCP成块数据流
和TCP成块数据流相关的东西有非常多,比方流量控制,紧急传输数据,数据窗体大小调整等等。
?
正常数据流
TCP通常不会对每一个到达的数据分段进行确认操作,通常一个ACK报文能够确认多个成块数据段报文。通常情况下是两个成块数据报文段须要一个ACK报文确认。一般是由以下的原有造成的 :当收到一个报文后,此TCP连接被标识未一个未完毕的时延确认,当再次收到一个数据报文后,此连接有两个未确认的报文段。TCP立即发送一个ACK。当第三个数据报文到达后,第四个报文到达前,通常此TCP连接已经经过了200ms延时,因此一个ACK被发送,这种循环周而复始,从而出现了一个ACK确认两个数据报文的情况。当然,ACK的产生非常大程度上和其接收数据报文段的时间紧密相关,也就是和Client段发送数据的频率相关。和网络拥塞程度相关。和Client与Server两端的处理能力相关。总是是一个多因素决定的结果。
? TCP的滑动窗体协议
TCP使用滑动窗体协议来进行流量控制。特别须要注意的是,滑动窗体是一个抽象的概念,它是针对每个TCP连接的,并且是有方向的。一个TCP连接应该有两个滑动窗体。每个传输数据方向上有一个,而不是针对连接的每一端的。
窗体左边沿向右边滑动叫做窗体合拢,表示发送方发送了数据或者接收到了确认;窗体右边沿向右边滑动叫做窗体的张开,表示数据已经被用户空间进程接收而且释放了缓存;窗体左边沿向左移动则表明此ACK是反复ACK,应该丢弃;窗体右边沿向左移动叫做窗体收缩,一般不会有人这样做。
当左边沿和右边沿重合的时候表明窗体大小是0,此时发送方不应该在发送数据了,由于接收方的接收缓冲区已满,用户进程还没以接收。当用户进程接收完毕后,接收方应该发送一个ACK,表明此时的接收窗体已经恢复。此ACK的序号同前一个win为0的ACK同样。
相同。在实现中,发送方不必发送一个全窗体的数据。可是它当然能够这样做。
ACK总是将窗体向右边滑动,窗体的大小能够减小,接收方在发送ACK之前不必等待窗体被填满(即变为0),非常多实现是收到两个数据报文段后立马发送ACK。
?
TCP窗体大小的调整
TCP窗体的大小通常由接收端来确认,也就是在TCP建立连接的第二个SYN+ACK报文的Win字段来确认。
当然。程序能够随时改变这个窗体(缓存)的大小。默认的窗体大小是4096字节。可是对于文件传输来说这并非一个理想的数字,假设程序的主要目的是传输文件,那么最好将这个缓存设置到最大,可是这样可能会造成发送端连续发送多个数据报文段后,接收方才反馈一个ACK的情况。当然,这也没有什么不能够的,仅仅要不超时。就不算错。
? TCP的PUSH包
PUSH是TCP报头中的一个标志位。发送方在发送数据的时候能够设置这个标志位。
该标志通知接收方将接收到的数据所有提交给接收进程。这里所说的数据包含与此PUSH包一起传输的数据以及之前就为该进程传输过来的数据。
当Server端收到这些数据后。它须要立马将这些数据提交给应用层进程,而不再等待是否还有额外的数据到达。
那么应该合适设置PUSH标志呢?实际上如今的TCP协议栈基本上都能够自行处理这个问题,而不是交给应用层处理。假设待发送的数据会清空发送缓存。那么栈就会自己主动为此包设置PUSH标志。源于BSD的栈一般都会这么做。并且,BSD TCP STACK也从来不会将收到的数据推迟提交给应用程序,因此。在BSD TCP STACK中,PUSH位是被忽略的,由于根本就没实用。
?
TCP的慢启动(拥塞窗体)
TCP在局域网环境中的效率是非常高的,可是到了广域网的环境中情况就不同了,在发送方和接收方之间可能存在多个Router以及一些速率比較慢的链路,并且一些中继路由器必须缓存分组。还可能分片。所以在广域网的环境中,TCP的效率可能出现故障。
为了解决问题,如今的TCP栈都支持“慢启动”算法,即拥塞窗体控制算法。该算法通过观察到新分组进入网络的速率与还有一端返回ACK的速率同样而工作。事实上,拥塞窗体是发送方使用的一种流量控制算法。
慢启动为TCP的发送方添加了一个拥塞窗体,当连接建立时,拥塞窗体被初始化为一个报文段大小,每收到一个ACK,拥塞窗体就会添加一个报文段。发送方取拥塞窗体与通过窗体的最小值作为发送的上限。
?
TCP成块数据吞吐量
TCP窗体大小,窗体流量控制,慢启动对TCP的成块传输数据综合作用,可能对TCP的传输数据有意想不到的影响。
RTT(Round-Trip Time) :往返时间。是指一个报文段从发出去到收到此报文段的ACK所经历的时间。
通常一个报文段的RTT与传播时延和发送时延两个因素相关。
在发送的过程中有可能发生这种情况,即TCP两端的传输“管道”被填满。即整个管道上都有数据在跑,此时无论拥塞窗体和通告窗体是多少。管道上都不能在容纳很多其它的数据了。
此时每当接收方从网络上移去一个报文段,发送方就发送一个,可是管道上的ACK总是固定的,这种情况就是连接的理想稳定状态。
普通情况下带宽*时延就是一条线路的容量,因此吧RTT减小能够添加一条线路的容量,注意RTT加大的意思时传输时间减小!
当 数据由一个大的管道向一个小的管道传输时,就有可能发生拥塞。比如,当若干输入流到达一个路由器。而此路由器的输出带宽小于这些输入流的带宽总和时,就会 发生拥塞。
这样的情况普遍见于局域网与广域网的接口处。假设发送方处于局域网。并且不使用慢启动,使用局域网的带宽尽快的发送报文,那么返回的ACK之间的间隔与最慢的广域网链路一致。并且,因为路由器转发包速度慢,所以路由器就有可能主动丢失分组包。
?
TCP的紧急方式
TCP提供了一种“紧急方式”的传输数据方式,TCP的一端能够告诉还有一端有些具有某种方式的紧急数据被放在了普通的数据流中,接收方能够自行选择处理。紧急方式客厅通过设置TCP的URG标识位与紧急指针的偏移量来设置。这个紧急指针指向紧急数据的最后一个字节(也有可能是最后一个字节的下一个字节)。
如今有很多实现将紧急方式叫做“带外数据”,事实上这是不对的。
眼下紧急指针被用来禁止停止FTP的传输数据。
只是总的来说。用的不多。
对于传输数据来说。假设用紧急数据来传输大量数据,这样的方法显然是不可取的,再建立一个TCP连接不是更简单有效吗?
====================================================================
http://zhidao.baidu.com/question/98620785
为了防止网络的拥塞现象,TCP提出了一系列的拥塞控制机制。最初由V. Jacobson在1988年的论文中提出的TCP的拥塞控制由“慢启动(Slow start)”和“拥塞避免(Congestion avoidance)”组成,后来TCP Reno版本号中又针对性的加入了“高速重传(Fast retransmit)”、“高速恢复(Fast Recovery)”算法,再后来在TCP NewReno中又对“高速恢复”算法进行了改进,近些年又出现了选择性应答( selective acknowledgement,SACK)算法,还有其它方面的大大小小的改进。成为网络研究的一个热点。 TCP的拥塞控制主要原理依赖于一个拥塞窗体(cwnd)来控制,在之前我们还讨论过TCP还有一个对端通告的接收窗体(rwnd)用于流量控制。窗体值的大小就代表能够发送出去的但还没有收到ACK的最大数据报文段,显然窗体越大那么数据发送的速度也就越快,可是也有越可能使得网络出现拥塞。假设窗体值为1,那么就简化为一个停等协议,每发送一个数据。都要等到对方的确认才干发送第二个数据包。显然传输数据效率低下。TCP的拥塞控制算法就是要在这两者之间权衡,选取最好的cwnd值,从而使得网络吞吐量最大化且不产生拥塞。
由于须要考虑拥塞控制和流量控制两个方面的内容,因此TCP的真正的发送窗体=min(rwnd, cwnd)。可是rwnd是由对端确定的,网络环境对其没有影响,所以在考虑拥塞的时候我们一般不考虑rwnd的值,我们临时仅仅讨论怎样确定cwnd值的大小。关于cwnd的单位。在TCP中是以字节来做单位的,我们假设TCP每次传输都是依照MSS大小来发送数据的,因此你能够觉得cwnd依照数据包个数来做单位也能够理解,所以有时我们说cwnd添加1也就是相当于字节数添加1个MSS大小。 慢启动:最初的TCP在连接建立成功后会向网络中发送大量的数据包,这样非常easy导致网络中路由器缓存空间耗尽,从而发生拥塞。因此新建立的连接不能够一開始就大量发送数据包,而仅仅能依据网络情况逐步添加每次发送的数据量,以避免上述现象的发生。
详细来说。当新建连接时,cwnd初始化为1个最大报文段(MSS)大小,发送端開始依照拥塞窗体大小发送数据,每当有一个报文段被确认。cwnd就添加1个MSS大小。这样cwnd的值就随着网络往返时间(Round Trip Time,RTT)呈指数级增长,其实,慢启动的速度一点也不慢。仅仅是它的起点比較低一点而已。
我们能够简单计算下: 開始 ---> cwnd = 1 经过1个RTT后 ---> cwnd = 2*1 = 2 经过2个RTT后 ---> cwnd = 2*2= 4 经过3个RTT后 ---> cwnd = 4*2 = 8 假设带宽为W,那么经过RTT*log2W时间就能够占满带宽。 拥塞避免:从慢启动能够看到,cwnd能够非常快的增长上来,从而最大程度利用网络带宽资源,可是cwnd不能一直这样无限增长下去,一定须要某个限制。TCP使用了一个叫慢启动门限(ssthresh)的变量,当cwnd超过该值后。慢启动过程结束。进入拥塞避免阶段。对于大多数TCP实现来说。ssthresh的值是65536(同样以字节计算)。拥塞避免的主要思想是加法增大。也就是cwnd的值不再指数级往上升。開始加法添加。此时当窗体中全部的报文段都被确认时,cwnd的大小加1,cwnd的值就随着RTT開始线性添加,这样就能够避免增长过快导致网络拥塞,慢慢的添加调整到网络的最佳值。
上面讨论的两个机制都是没有检測到拥塞的情况下的行为,那么当发现拥塞了cwnd又该怎样去调整呢? 首先来看TCP是怎样确定网络进入了拥塞状态的,TCP觉得网络拥塞的主要依据是它重传了一个报文段。
上面提到过,TCP对每个报文段都有一个定时器,称为重传定时器(RTO),当RTO超时且还没有得到数据确认,那么TCP就会对该报文段进行重传,当发生超时时,那么出现拥塞的可能性就非常大。某个报文段可能在网络中某处丢失,而且兴许的报文段也没有了消息。在这样的情况下,TCP反应比較“强烈”: 1.把ssthresh减少为cwnd值的一半 2.把cwnd又一次设置为1 3.又一次进入慢启动过程。
从总体上来讲。TCP拥塞控制窗体变化的原则是AIMD原则,即加法增大、乘法减小。能够看出TCP的该原则能够较好地保证流之间的公平性,由于一旦出现丢包。那么马上减半退避,能够给其它新建的流留有足够的空间,从而保证整个的公平性。 其实TCP还有一种情况会进行重传:那就是收到3个同样的ACK。TCP在收到乱序到达包时就会马上发送ACK。TCP利用3个同样的ACK来判定数据包的丢失,此时进行高速重传。高速重传做的事情有: 1.把ssthresh设置为cwnd的一半 2.把cwnd再设置为ssthresh的值(详细实现有些为ssthresh+3) 3.又一次进入拥塞避免阶段。 后来的“高速恢复”算法是在上述的“高速重传”算法后加入的,当收到3个反复ACK时,TCP最后进入的不是拥塞避免阶段。而是高速恢复阶段。
高速重传和高速恢复算法一般同一时候使用。高速恢复的思想是“数据包守恒”原则,即同一个时刻在网络中的数据包数量是恒定的。仅仅有当“老”数据包离开了网络后,才干向网络中发送一个“新”的数据包,假设发送方收到一个反复的ACK,那么依据TCP的ACK机制就表明有一个数据包离开了网络,于是cwnd加1。假设能够严格依照该原则那么网络中非常少会发生拥塞,其实拥塞控制的目的也就在修正违反该原则的地方。 详细来说高速恢复的主要步骤是: 1.当收到3个反复ACK时,把ssthresh设置为cwnd的一半,把cwnd设置为ssthresh的值加3,然后重传丢失的报文段,加3的原因是由于收到3个反复的ACK。表明有3个“老”的数据包离开了网络。 2.再收到反复的ACK时。拥塞窗体添加1。 3.当收到新的数据包的ACK时,把cwnd设置为第一步中的ssthresh的值。原因是由于该ACK确认了新的数据,说明从反复ACK时的数据都已收到。该恢复过程已经结束,能够回到恢复之前的状态了,也即再次进入拥塞避免状态。 高速重传算法首次出如今4.3BSD的Tahoe版本号,高速恢复首次出如今4.3BSD的Reno版本号。也称之为Reno版的TCP拥塞控制算法。 能够看出Reno的高速重传算法是针对一个包的重传情况的,然而在实际中,一个重传超时可能导致很多的数据包的重传,因此当多个数据包从一个数据窗体中丢失时而且触发高速重传和高速恢复算法时。问题就产生了。因此NewReno出现了,它在Reno高速恢复的基础上稍加了改动,能够恢复一个窗体内多个包丢失的情况。详细来讲就是:Reno在收到一个新的数据的ACK时就退出了高速恢复状态了。而NewReno须要收到该窗体内全部数据包的确认后才会退出高速恢复状态,从而更一步提高吞吐量。 SACK就是改变TCP的确认机制。最初的TCP仅仅确认当前已连续收到的数据。SACK则把乱序等信息会全部告诉对方,
1、滑动窗体
滑动窗体协议是传输层进行流控的一种措施,接收方通过通告发
送方自己的窗体大小,从而控制发送方的发送速度。从而达到防止发送方发送速度过快而导致自己被淹没的目的。
TCP的滑动窗体攻克了端到端的流量控制问题,同意接受方对传输进行限制。直到它拥有足够的缓冲空间来容纳很多其它的数据。
2、拥塞窗体
拥塞窗体也看做是发送端用来进行流量控制的窗体。
可是。实际上,TCP还必须应付互联网中的拥塞现象。
拥塞是指一个或者多个交换点的数据报超载而导致时延剧烈添加的现象。
为了控制拥塞,TCP使用了第二个窗体限制。即拥塞窗体限制。
对于拥塞窗体大小的限制採用慢開始和乘法减小两种技术。
乘法减小的拥塞避免策略:一旦发现报文段丢失,就把拥塞窗体的大小减半(直到减至最小的窗体,窗体中应至少包括一个报文段)。
慢開始恢复:拥塞窗体随着一个确认的到达,拥塞窗体的大小每次添加一个报文段。
3、小结
慢启动为发送方的TCP添加了还有一个窗体:拥塞窗体(congestion window),记为cwnd。
当与还有一个网络的主机建立TCP连接时,拥塞窗体被初始化为1个报文段(即还有一端通告的报文段大小)。每收到一个ACK,拥塞窗体就添加一个
报文段(cwnd以字节为单位,可是慢启动以报文段大小为单位进行添加)。
发送方取拥塞窗体与通告/滑动窗体中的最小值作为发送上限。拥塞窗体是发送方使用的流量控制。而通告/滑动窗体则是接收方使用的流量控制。
发送方開始时发送一个报文段。然后等待ACK。
当收到该ACK时,拥塞窗体从1添加为2,即能够发送两个报文段。
当收到这两个报文段的ACK时间,拥塞被添加到形式4。这是添加一个指数关系。