TCP和UDP处在同一层---运输层,可是TCP和UDP最不同的地方是,TCP提供了一种可靠的数据传输服务,TCP是面向连接的,也就是说,利用TCP通信的两台主机首先要经历一个“拨打电话”的过程,等到通信准备结束才開始数据传输,最后结束通话。所以TCP要比UDP可靠的多,UDP是把数据直接发出去,而无论对方是不是在收信,就算是UDP无法送达,也不会产生ICMP差错报文,这一经时重申了非常多遍了。

把TCP保证可靠性的简单工作原理摘抄例如以下

  • 应用数据被切割成TCP觉得最适合发送的数据块。这和UDP全然不同。应用程序产生的 数据报长度将保持不变。由TCP传递给IP的信息单位称为报文段或段( segment)(參见图1 - 7)。在1 8.4节我们将看到TCP怎样确定报文段的长度。
  • 当TCP发出一个段后,它启动一个定时器。等待目的端确认收到这个报文段。

    假设不能 及时收到一个确认,将重发这个报文段。

    在第21章我们将了解TCP协议中自适应的超时 及重传策略。

  • 当TCP收到发自TCP连接还有一端的数据,它将发送一个确认。这个确认不是马上发送,通常将推迟几分之中的一个秒,这将在1 9.3节讨论。
  • TCP将保持它首部和数据的检验和。

    这是一个端到端的检验和,目的是检測数据在传输 过程中的不论什么变化。假设收到段的检验和有差错。 T P将丢弃这个报文段和不确认收到此报文段(希望发端超时并重发)。

  • 既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段 的到达也可能会失序。假设必要, TCP将对收到的数据进行又一次排序,将收到的数据以正确的顺序交给应用层。
  • TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端仅仅同意还有一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。

从这段话中能够看到,TCP中保持可靠性的方式就是超时重发。这是有道理的,尽管TCP也能够用各种各样的ICMP报文来处理这些。可是这也不是可靠的。最可靠的方式就是仅仅要不得到确认。就又一次发送数据报,直到得到对方的确觉得止。

TCP的首部和UDP首部一样,都有发送port号和接收port号。可是显然,TCP的首部信息要比UDP的多。能够看到,TCP协议提供了发送和确认所须要的全部必要的信息。

这在P171-173有具体地介绍。能够想象一个TCP数据的发送应该是例如以下的一个过程。

  • 两方建立连接
  • 发送方给接受方TCP数据报。然后等待对方的确认TCP数据报,假设没有,就又一次发,假设有,就发送下一个数据报。

  • 接受方等待发送方的数据报,假设得到数据报并检验无误,就发送ACK(确认)数据报。并等待下一个TCP数据报的到来。直到接收到FIN(发送完毕数据报)
  • 中止连接

能够想见,为了建立一个TCP连接,系统可能会建立一个新的进程(最差也是一个线程),来进行数据的传送

 

TCP是一个面向连接的协议。所以在连接两方发送数据之前,都须要首先建立一条连接。这和前面讲到的协议全然不同。

前面讲的全部协议都仅仅是发送数据而已。大多数都不关心发送的数据是不是送到,UDP尤其明显。从编程的角度来说,UDP编程也要简单的多----UDP都不用考虑数据分片。


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连接不是更简单有效吗?

 

====================================================================

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则把乱序等信息会全部告诉对方,

由接收方提供的窗体的大小通常能够由接收进程控制。这将影响TCP的性能。
sock API雨荨进程设置发送和接收缓存的大小。接收缓存的大小是该连接上所能够通告的最大窗体大小。
发送发一開始便向网络发送多个报文段,直至的窗体达到接收方通告大小为止。

当发送发和接收方处于同一个局域网时。这样的方式是能够的。

可是假设在发送方和接收方之间存在多个路由器和速率较慢的链路时,就有可能出现一些问题。

一些中间路由器必须缓存分组。并有可能耗尽存储器的空间。

TCP须要支持一种被称为慢启动的算法。该算法通过观察到新分组进入网络的速率应该与还有一端返回确认的速率同样而进行工作。

慢启动为发送方的TCP添加了还有一个窗体:拥塞窗体。cwnd。
当与还有一个网络的主机建立TCP连接时,拥塞窗体被初始化为1个报文段。即还有一端通过的报文段大小。每收到一个ACK,拥塞窗体就添加一个报文段,cwnd以字节为单位。可是慢启动以报文段大小为单位进行添加。发送方取拥塞窗体与通告窗体中的最小值作为发送上限、拥塞窗体是发送方使用的流量控制。而通告窗体则是接收方使用的流量控制。
发送方開始时发送一个报文段,然后等待ACK。当接收该ACK时,拥塞窗体从1添加为2,即能够发送2个报文段。当收到这2个报文段的ACK时,拥塞窗体就添加为4。

这是一种指数添加的关系。