分类: LINUX
2007-07-31 15:23:05
TCP的数据流大致可以分为两类,交互数据流与成块的数据流。交互数据流就是发送控制命令的数据流,比如relogin,telnet,ftp命令等等;成块数据流是用来发送数据的包,网络上大部分的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通常不会对每个到达的数据分段进行确认操作,通常一个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连接不是更简单有效吗?