跳至主要内容

【转】重读TCP协议

TCP 的数据流

       TCP的数据流大致可以分为两类,交互数据流与成块的数据流。交互数据流就是发送控制命令的数据流,比如relogintelnetftp命令等等;成块数据流是用来发送数据的包,网络上大部分的TCP包都是这种包。

       很明显,TCP在传输这两种类型的包时的效率是不一样的,因此为了提高TCP的传输效率,应该对这两种类型的包采用不同的算法。

       总之,TCP的传输原则是尽量减少小分组传输的数量。

TCP的交互式数据流

Ø         经受时延的确认技术

TCP的交互式数据流通常使用"经过时延的确认"技术。通常Server在接收到从Client发送过来的数据时,并不马上发送ACK,而是等一小段时间,看看本机是否有数据要反馈给Client,如果有,就将数据包含在此ACK包中,以前发送给Client。一般情况下这个时延为200ms。需要注意的时这个200ms的定时器时相对于内核的时钟滴答的,也就是jeffs的。加入一个数据分组到达后,此定时器已经pass100ms,那么再过100ms ACK才会被发送,如果在这100ms内有数据要反馈,则在100msACK会和数据一起发送。

      

Ø         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段发送数据的频率相关,和网络拥塞程度相关,和ClientServer两端的处理能力相关,总是是一个多因素决定的结果。

 

Ø         TCP的滑动窗口协议

TCP使用滑动窗口协议来进行流量控制。特别需要注意的是,滑动窗口是一个抽象的概念,它是针对每一个TCP连接的,而且是有方向的,一个TCP连接应该有两个滑动窗口,每个数据传输方向上有一个,而不是针对连接的每一端的。

窗口左边沿向右边滑动叫做窗口合拢,表示发送方发送了数据或者接收到了确认;窗口右边沿向右边滑动叫做窗口的张开,表示数据已经被用户空间进程接收并且释放了缓存;窗口左边沿向左移动则表明此ACK是重复ACK,应该丢弃;窗口右边沿向左移动叫做窗口收缩,一般不会有人这样做。

当左边沿和右边沿重合的时候表明窗口大小是0,此时发送方不应该在发送数据了,因为接收方的接收缓冲区已满,用户进程还没以接收。当用户进程接收完成后,接收方应该发送一个ACK,表明此时的接收窗口已经恢复,此ACK的序号同前一个win0ACK相同。

同样,在实现中,发送方不必发送一个全窗口的数据,但是它当然可以这样做。ACK总是将窗口向右边滑动,窗口的大小可以减小,接收方在发送ACK之前不必等待窗口被填满(即变为0),很多实现是收到两个数据报文段后立刻发送ACK

 

Ø         TCP窗口大小的调整

TCP窗口的大小通常由接收端来确认,也就是在TCP建立连接的第二个SYN+ACK报文的Win字段来确认。

当然,程序可以随时改变这个窗口(缓存)的大小。默认的窗口大小是4096字节,但是对于文件传输来说这并不是一个理想的数字,如果程序的主要目的是传输文件,那么最好将这个缓存设置到最大,但是这样可能会造成发送端连续发送多个数据报文段后,接收方才反馈一个ACK的情况,当然,这也没有什么不可以的,只要不超时,就不算错。

Ø         TCPPUSH

PUSHTCP报头中的一个标志位,发送方在发送数据的时候可以设置这个标志位。该标志通知接收方将接收到的数据全部提交给接收进程。这里所说的数据包括与此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的数据传输有意想不到的影响。

       RTTRound-Trip Time :往返时间。是指一个报文段从发出去到收到此报文段的ACK所经历的时间。通常一个报文段的RTT与传播时延和发送时延两个因素相关。

       在发送的过程中有可能发生这样的情况,即TCP两端的传输"管道"被填满,即整个管道上都有数据在跑,此时不管拥塞窗口和通告窗口是多少,管道上都不能在容纳更多的数据了。此时每当接收方从网络上移去一个报文段,发送方就发送一个,但是管道上的ACK总是固定的,这种情况就是连接的理想稳定状态。

       一般情况下带宽*时延就是一条线路的容量,因此吧RTT减小可以增加一条线路的容量,注意RTT加大的意思时传输时间减小!

       当数据由一个大的管道向一个小的管道传输时,就有可能发生拥塞,例如,当若干输入流到达一个路由器,而此路由器的输出带宽小于这些输入流的带宽总和时,就会发生拥塞。这种情况普遍见于局域网与广域网的接口处。如果发送方处于局域网,而且不使用慢启动,使用局域网的带宽尽快的发送报文,那么返回的ACK之间的间隔与最慢的广域网链路一致。而且,由于路由器转发包速度慢,所以路由器就有可能主动丢失分组包。

 

Ø         TCP的紧急方式

TCP提供了一种"紧急方式"的数据传输方式,TCP的一端可以告诉另一端有些具有某种方式的紧急数据被放在了普通的数据流中,接收方可以自行选择处理。紧急方式客厅通过设置TCPURG标识位与紧急指针的偏移量来设置。这个紧急指针指向紧急数据的最后一个字节(也有可能是最后一个字节的下一个字节)。

现在有许多实现将紧急方式叫做"带外数据",其实这是不正确的。

       目前紧急指针被用来禁止停止FTP的数据传输。不过总的来说,用的不多。

       对于数据传输来说,如果用紧急数据来传输大量数据,这种方法显然是不可取的,再建立一个TCP连接不是更简单有效吗?

评论

此博客中的热门博文

【转】AMBA、AHB、APB总线简介

AMBA 简介 随着深亚微米工艺技术日益成熟,集成电路芯片的规模越来越大。数字IC从基于时序驱动的设计方法,发展到基于IP复用的设计方法,并在SOC设计中得到了广泛应用。在基于IP复用的SoC设计中,片上总线设计是最关键的问题。为此,业界出现了很多片上总线标准。其中,由ARM公司推出的AMBA片上总线受到了广大IP开发商和SoC系统集成者的青睐,已成为一种流行的工业标准片上结构。AMBA规范主要包括了AHB(Advanced High performance Bus)系统总线和APB(Advanced Peripheral Bus)外围总线。   AMBA 片上总线        AMBA 2.0 规范包括四个部分:AHB、ASB、APB和Test Methodology。AHB的相互连接采用了传统的带有主模块和从模块的共享总线,接口与互连功能分离,这对芯片上模块之间的互连具有重要意义。AMBA已不仅是一种总线,更是一种带有接口模块的互连体系。下面将简要介绍比较重要的AHB和APB总线。 基于 AMBA 的片上系统        一个典型的基于AMBA总线的系统框图如图3所示。        大多数挂在总线上的模块(包括处理器)只是单一属性的功能模块:主模块或者从模块。主模块是向从模块发出读写操作的模块,如CPU,DSP等;从模块是接受命令并做出反应的模块,如片上的RAM,AHB/APB 桥等。另外,还有一些模块同时具有两种属性,例如直接存储器存取(DMA)在被编程时是从模块,但在系统读传输数据时必须是主模块。如果总线上存在多个主模块,就需要仲裁器来决定如何控制各种主模块对总线的访问。虽然仲裁规范是AMBA总线规范中的一部分,但具体使用的算法由RTL设计工程师决定,其中两个最常用的算法是固定优先级算法和循环制算法。AHB总线上最多可以有16个主模块和任意多个从模块,如果主模块数目大于16,则需再加一层结构(具体参阅ARM公司推出的Multi-layer AHB规范)。APB 桥既是APB总线上唯一的主模块,也是AHB系统总线上的从模块。其主要功能是锁存来自AHB系统总线的地址、数据和控制信号,并提供二级译码以产生APB外围设备的选择信号,从而实现AHB协议到APB协议的转换。 ====================================

【转】GPIO编程模拟I2C入门

ARM编程:ARM普通GPIO口线模拟I2C  请教个问题: 因为需要很多EEPROM进行点对点控制,所以我现在要用ARM的GPIO模拟I2C,管脚方向我设 置的是向外的。我用网上的RW24C08的万能程序修改了一下,先进行两根线的模拟,SDA6, SCL6,但是读出来的数不对。我做了一个简单的实验,模拟SDA6,SCL6输出方波,在示波 器上看到正确方波,也就是说,我的输出控制是没问题的。 哪位大哥能指点一下,是否在接收时管脚方向要设为向内?(不过IOPIN不管什么方向都可 以读出当前状态值的阿) 附修改的RW24C08()程序: #define  SomeNOP() delay(300); /**/ /* *********************************  RW24C08   **************************************** */ /**/ /* ----------------------------------------------------------------------------- ---  调用方式:void I2CInit(void)   函数说明:私有函数,I2C专用 ------------------------------------------------------------------------------- -- */ void  I2CInit( void ) ... {  IO0CLR  =  SCL6;      // 初始状态关闭总线  SomeNOP();  // 延时   I2CStop();  // 确保初始化,此时数据线是高电平 }   /**/ /* ---------------------------------------------------------------------------- ----  调用方式:void I2CStart(void)   函数说明:私有函数,I2C专用 ------------------------------------------------------------------------------- -- */ void  I2CStart( void )

【转】cs8900网卡的移植至基于linux2.6内核的s3c2410平台

cs8900网卡的移植至基于linux2.6内核的s3c2410平台(转) 2008-03-11 20:58 硬件环境:SBC-2410X开发板(CPU:S3C2410X) 内核版本:2.6.11.1 运行环境:Debian2.6.8 交叉编译环境:gcc-3.3.4-glibc-2.3.3 第一部分 网卡CS8900A驱动程序的移植 一、从网上将Linux内核源代码下载到本机上,并将其解压: #tar jxf linux-2.6.11.1.tar.bz2 二、打开内核顶层目录中的Makefile文件,这个文件中需要修改的内容包括以下两个方面。 (1)指定目标平台。 移植前:         ARCH?= $(SUBARCH) 移植后: ARCH            :=arm (2)指定交叉编译器。 移植前: CROSS_COMPILE ?= 移植后: CROSS_COMPILE   :=/opt/crosstool/arm-s3c2410-linux-gnu/gcc-3.3.4-glibc-2.3.3/bin/arm-s3c2410-linux-gnu- 注:这里假设编译器就放在本机的那个目录下。 三、添加驱动程序源代码,这涉及到以下几个方面。(1)、从网上下载了cs8900.c和cs8900.h两个针对2.6.7的内核的驱动程序源代码,将其放在drivers/net/arm/目录下面。 #cp cs8900.c ./drivers/net/arm/ #cp cs8900.h ./drivers/net/arm/ 并在cs8900_probe()函数中,memset (&priv,0,sizeof (cs8900_t));函数之后添加如下两条语句: __raw_writel(0x2211d110,S3C2410_BWSCON); __raw_writel(0x1f7c,S3C2410_BANKCON3); 注:其原因在"第二部分"解释。 (2)、修改drivers/net/arm/目录下的Kconfig文件,在最后添加如下内容: Config ARM_CS8900    tristate "CS8900 support" depends on NET_ETHERNET && A