《TCP/IP详解-卷1》 要点笔记(2)

      访问: 3,231 次      评论    

  1. <p>TCP提供一种面向连接的、可靠的字节流服务。</p>
  2. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  3. <p><br/></p>
  4. <p><span style="background-color: inherit; line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">首部长度给出首部中32 bit字的数目。</span>需要这个值是因为任选字段的长度是可变的。这个</span><span style="background-color: inherit; line-height: 1.5;">字段占4 bit,因此TCP最多有60字节(32bit (2^4-1) = 415 = 60)的首部。然而,没有任选字段,正常的长度是20字节。</span></p>
  5. </ul>
  6. <p>在TCP首部中有6个标志比特。它们中的多个可同时被设置为1。我们在这儿简单介绍它<span style="background-color: inherit; line-height: 1.5;">们的用法: </span></p>
  7. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  8. <p><span style="background-color: inherit; line-height: 1.5;">URG 紧急指针( urgent pointer)有效(见20.8节)。 它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)。 </span></p>
  9. <p><span style="background-color: inherit; line-height: 1.5;">ACK 确认序号有效。 </span></p>
  10. <p><span style="background-color: inherit; line-height: 1.5;">PSH 接收方应该尽快将这个报文段交给应用层。 而不再等到整个缓存都填满了后再向上交付。  </span></p>
  11. <p><span style="background-color: inherit; line-height: 1.5;">RST 重建连接。 </span></p>
  12. <p><span style="background-color: inherit; line-height: 1.5;">SYN 同步序号用来发起一个连接。这个标志和下一个标志将在第18章介绍。 </span></p>
  13. <p><span style="background-color: inherit; line-height: 1.5;">FIN 发端完成发送任务。</span></p>
  14. </ul>
  15. <p>两个进程在使用TCP交换数据之前,它们之间必须建立一条连接。一个TCP连接由一个4元组唯一确定:本地IP地址、本地端口号、远端IP地址和远端端口<span style="background-color: inherit; line-height: 1.5;">号。</span></p>
  16. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  17. <p><br/></p>
  18. </ul><!--more-->
  19. <p><span style="color:#ff0000;background-color: inherit;">ISN(Initial Sequence Number)</span>:当一端为建立连接而发送它的SYN时,它为连接选择一个初始序号。</p>
  20. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  21. <p><span style="background-color: inherit; line-height: 1.5;">ISN随时间而变化, </span><span style="background-color: inherit; line-height: 1.5;">因此每个连接都将具有不同的ISN。</span></p>
  22. <p><span style="background-color: inherit; line-height: 1.5;">RFC 793 [Postel 1981c]指出ISN可看作是一个32比特的计</span><span style="background-color: inherit; line-height: 1.5;">数器,每4ms加1。</span></p>
  23. <p><span style="background-color: inherit; line-height: 1.5;">这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,而</span><span style="background-color: inherit; line-height: 1.5;">导致某个连接的一方对它作错误的解释。</span></p>
  24. </ul>
  25. <p>第一次超时时间:不准确的原因</p>
  26. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  27. <p><span style="background-color: inherit; line-height: 1.5;">在图18-6中一个令人困惑的问题是第一次超时时间为5.8秒,接近6秒,但不准确,相比之 </span><span style="background-color: inherit; line-height: 1.5;">下第二个超时时间几乎准确地为24秒。运行十多次测试,发现第一次超时时间在5.59秒~ 5.93 </span><span style="background-color: inherit; line-height: 1.5;">秒之间变化。然而,第二次超时时间则总是24.00秒(精确到小数点后面两位)。 </span></p>
  28. <p><span style="background-color: inherit; line-height: 1.5;">这是因为BSD版的TCP软件采用一种500 ms的定时器。这种500 ms的定时器用于确定本章</span><span style="background-color: inherit; line-height: 1.5;">中所有的各种各样的TCP超时。当我们键入telnet命令,将建立一个6秒的定时器(12个时钟滴</span><span style="background-color: inherit; line-height: 1.5;">答(tick)),但它可能在之后的5.5秒~ 6秒内的任意时刻超时。</span></p>
  29. <p><span style="background-color: inherit; line-height: 1.5;">图18-7显示了这一发生过程。 </span><span style="background-color: inherit; line-height: 1.5;">尽管定时器初始化为12个时钟滴答,但定时计数器会在设置后的第一个0~500ms中的任意时</span><span style="background-color: inherit; line-height: 1.5;">刻减1。从那以后,定时计数器大约每隔500ms减1,但在第1个500ms内是可变的(我们使用</span><span style="background-color: inherit; line-height: 1.5;">限定词“大约”是因为在TCP每隔500ms获得系统控制的瞬间,系统内核可能会优先处理其</span><span style="background-color: inherit; line-height: 1.5;">他中断)。</span></p>
  30. <p><br/></p>
  31. </ul>
  32. <p>TCP终止连接:无论何时关闭一个连接,一端必须保持这个连接,我们看到TIME_WAIT状态将处理这个<span style="background-color: inherit; line-height: 1.5;">问题。<span style="color:#ff0000;background-color: inherit;">处理的原则是执行主动打开的一端在进入这个状态时要保持的时间为TCP实现中规定</span></span><span style="background-color: inherit; line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">的MSL值的两倍。</span></span></p>
  33. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  34. <p>建立一个连接需要三次握手,而终止一个连接要经过4次握手。这由TCP的半关闭(half-<span style="background-color: inherit; line-height: 1.5;">close)造成的。既然一个TCP连接是全双工(即数据在两个方向上能同时传递),因此每个方</span><span style="background-color: inherit; line-height: 1.5;">向必须单独地进行关闭。</span></p>
  35. <p><span style="background-color: inherit; line-height: 1.5;">首先进行关闭的一方(即发送第一个FIN)将执行主动关闭,而另一方(收到这个FIN) </span><span style="background-color: inherit; line-height: 1.5;">执行被动关闭。</span></p>
  36. <p>收到一个FIN只意味着在这一方向上没有数据流动。一个TCP连接在收到一个FIN后仍能 发送数据。TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。这就是所谓的半关闭。</p>
  37. <p><span style="background-color: inherit; line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">TIME_WAIT状态也称为2MSL等待状态。</span>报文段最大生</span><span style="background-color: inherit; line-height: 1.5;">存时间MSL(Maximum Segment Lifetime),它是任何报文段被丢弃前在网络内的最长时间。</span></p>
  38. <p><span style="background-color: inherit; line-height: 1.5;">对一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最</span><span style="background-color: inherit; line-height: 1.5;">后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可让TCP再次发</span><span style="background-color: inherit; line-height: 1.5;">送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)。</span></p>
  39. <p><span style="background-color: inherit; line-height: 1.5;">这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的SOCKET</span><span style="background-color: inherit; line-height: 1.5;">(客户的I P地址和端口号,服务器的I P地址和端口号)不能再被使用。这个连接只能在2MSL</span><span style="background-color: inherit; line-height: 1.5;">结束后才能再被使用。</span></p>
  40. <p><span style="background-color: inherit; line-height: 1.5;">我们说图18-13中客户执行主动关闭并进入TIME_WAIT是正常的。服务器通常执行被动 </span><span style="background-color: inherit; line-height: 1.5;">关闭,不会进入TIME_WAIT状态。这暗示如果我们终止一个客户程序,并立即重新启动这个</span><span style="background-color: inherit; line-height: 1.5;">客户程序,则这个新客户程序将不能重用相同的本地端口。</span></p>
  41. <p><span style="background-color: inherit; line-height: 1.5;">这违反了TCP规范,但被大多数的伯克利版实现所支持。这些实现允许 </span><span style="background-color: inherit; line-height: 1.5;">一个新的连接请求到达仍处于TIME_WAIT状态的连接,只要新的序号大于该连接前一个替身 </span><span style="background-color: inherit; line-height: 1.5;">的最后序号。对于同一连接的前一个替身,这个具体实现中的特性让客户程序和服务器程序能连续地 </span><span style="background-color: inherit; line-height: 1.5;">重用每一端的相同端口号,但这只有在服务器执行主动关闭才有效。</span></p>
  42. <p><span style="background-color: inherit; line-height: 1.5;">RST:产生复位的一种常见情况是当连接请求到达时,目的端口没有进程正在听。对于UDP, </span><span style="background-color: inherit; line-height: 1.5;">我们在6.5节看到这种情况,当一个数据报到达目的端口时,该端口没在使用,它将产生一个 </span><span style="background-color: inherit; line-height: 1.5;">ICMP端口不可达的信息。而TCP则使用复位。</span></p>
  43. <p><span style="background-color: inherit; line-height: 1.5;">RST:需要注意的是RST报文段不会导致另一端产生任何响 </span><span style="background-color: inherit; line-height: 1.5;">应,另一端根本不进行确认。收到RST的一方将终止该连接,并通知应用层连接复位。</span></p>
  44. </ul>
  45. <p>当服务器正处于忙时, TCP是如何处理这些呼入的连接请求? <span style="background-color: inherit; line-height: 1.5;">在伯克利的TCP实现中采用以下规则: </span></p>
  46. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  47. <p><span style="background-color: inherit; line-height: 1.5;">1) 正等待连接请求的一端有一个固定长度的连接队列,该队列中的连接已被TCP接受 </span><span style="background-color: inherit; line-height: 1.5;">(即三次握手已经完成),但还没有被应用层所接受。 </span><span style="background-color: inherit; line-height: 1.5;">注意区分TCP接受一个连接是将其放入这个队列,而应用层接受连接是将其从该队列</span><span style="background-color: inherit; line-height: 1.5;">中移出。 </span></p>
  48. <p><span style="background-color: inherit; line-height: 1.5;">2) 应用层将指明该队列的最大长度,这个值通常称为积压值( <span style="color:#ff0000;background-color: inherit;">backlog </span>)。注意,积压值说明的是TCP监听的端点已 </span><span style="background-color: inherit; line-height: 1.5;">被TCP接受而等待应用层接受的最大连接数。</span></p>
  49. <p><span style="background-color: inherit; line-height: 1.5;">3) 当一个连接请求(即SYN)到达时,TCP使 </span><span style="background-color: inherit; line-height: 1.5;">用一个算法,根据当前连接队列中的连接数 </span><span style="background-color: inherit; line-height: 1.5;">来确定是否接收这个连接。</span></p>
  50. <p><span style="background-color: inherit; line-height: 1.5;">4) 如果对于新的连接请求,该TCP监听的端点的连接队列中还有空间(基于图18-23),</span><span style="background-color: inherit; line-height: 1.5;">TCP模块将对SYN进行确认并完成连接的建立。但应用层只有在三次握手中的第三个</span><span style="background-color: inherit; line-height: 1.5;">报文段收到后才会知道这个新连接时。另外,当客户进程的主动打开成功但服务器的</span><span style="background-color: inherit; line-height: 1.5;">应用层还不知道这个新的连接时,它可能会认为服务器进程已经准备好接收数据了</span><span style="background-color: inherit; line-height: 1.5;">(如果发生这种情况,服务器的TCP仅将接收的数据放入缓冲队列)。 </span></p>
  51. <p><span style="background-color: inherit; line-height: 1.5;">5) 如果对于新的连接请求,连接队列中已没有空间, TCP将不理会收到的SYN。也不发 </span><span style="background-color: inherit; line-height: 1.5;">回任何报文段(即不发回RST)。如果应用层不能及时接受已被TCP接受的连接,这些</span><span style="background-color: inherit; line-height: 1.5;">连接可能占满整个连接队列,客户的主动打开最终将超时。</span></p>
  52. </ul>
  53. <p>经受时延的确认(<span style="color:#ff0000;background-color: inherit;">Delayed ACK</span>):通常TCP在接收到数据时并不<span style="background-color: inherit; line-height: 1.5;">立即发送ACK;相反,它推迟发送,以便将ACK与需要沿该方向发送的数据一起发送(有时</span><span style="background-color: inherit; line-height: 1.5;">称这种现象为数据捎带ACK)。绝大多数实现采用的时延为200ms,也就是说,TCP将以最大 </span><span style="background-color: inherit; line-height: 1.5;">200ms 的时延等待是否有数据一起发送。</span></p>
  54. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  55. <p><span style="background-color: inherit; line-height: 1.5;">图20-1中报文段11 ~ 16说明了通常使用的“</span><span style="color:#ff0000;background-color: inherit; line-height: 1.5;">隔一个报文段确认</span><span style="background-color: inherit; line-height: 1.5;">”的策略:</span></p>
  56. <p><span style="background-color: inherit; line-height: 1.5;">报文段11、12和13到达并</span><span style="background-color: inherit; line-height: 1.5;">被放入IP的接收队列。</span></p>
  57. <p><span style="background-color: inherit; line-height: 1.5;">当报文段11被处理时,连接被标记为产生一个经受时延的确认。</span><span style="background-color: inherit; line-height: 1.5;">当报</span><span style="background-color: inherit; line-height: 1.5;">文段12被处理时,它们的ACK(报文段14)被产生且连接的经受时延的确认标志被清除。</span></p>
  58. <p><span style="background-color: inherit; line-height: 1.5;">报</span><span style="background-color: inherit; line-height: 1.5;">文段13使得连接再次被标记为产生经受时延。但在时延定时器溢出之前,报文段15处理完毕, </span><span style="background-color: inherit; line-height: 1.5;">因此该确认立刻被发送。</span></p>
  59. </ul>
  60. <p><span style="color:#ff0000;background-color: inherit;">Nagle算法</span>:(With Minshall's modification)该算法要求一个TCP连接上最多只能有一个未被确认的未完成的小分组,在该分组的确<span style="background-color: inherit; line-height: 1.5;">认到达之前不能发送其他的小分组。相反, TCP收集这些少量的分组,并在确认到来时以一</span><span style="background-color: inherit; line-height: 1.5;">个分组的方式发出去。</span></p>
  61. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  62. <p><span style="background-color: inherit; line-height: 1.5;">该算法的优越之处在于它是自适应的:确认到达得越快,数据也就发</span><span style="background-color: inherit; line-height: 1.5;">送得越快。</span></p>
  63. <p><span style="background-color: inherit; line-height: 1.5;">该算法适宜于较慢的链路上的<span style="color:#c00000;background-color: inherit;">交互型会话</span>(比如书中的例子rlogin服务,还有telnet和ssh服务)。</span></p>
  64. <p><span style="background-color: inherit; line-height: 1.5;">在局域网 (低时延)</span><span style="background-color: inherit; line-height: 1.5;">环境下两个主机之间发送数据时很少使用这个算法。(因为会显著增加时延)</span></p>
  65. <p><span style="background-color: inherit; line-height: 1.5;">插口(SOCKET)API用户可以使用<span style="color:#ff0000;background-color: inherit;">TCP_NODELAY</span>选项来关闭Nagle算法。</span></p>
  66. </ul>
  67. <p>窗口大小通告:</p>
  68. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  69. <p><span style="background-color: inherit; line-height: 1.5;">主机通告窗口大小为4096字节,或通告其窗口大 </span><span style="background-color: inherit; line-height: 1.5;">小为8192个字节。</span></p>
  70. <p><span style="background-color: inherit; line-height: 1.5;">然而,报文段5通告的窗口大小为4095个字节,这意味着在TCP的缓冲区中仍然有一个字 </span><span style="background-color: inherit; line-height: 1.5;">节等待应用程序( Rlogin客户)读取。</span></p>
  71. <p><span style="background-color: inherit; line-height: 1.5;">窗口大小是一个16 bit字段, </span><span style="background-color: inherit; line-height: 1.5;">因而<span style="color:#ff0000;background-color: inherit;">窗口大小最大为65535字节</span>。在24.4节我们将看到新的窗口刻度选项,它允许这个值按比</span><span style="background-color: inherit; line-height: 1.5;">例变化以提供更大的窗口。</span></p>
  72. </ul>
  73. <p><span style="color:#ff0000;background-color: inherit;">停等协议</span>:在第15章我们看到<span style="color:#ff0000;background-color: inherit;">TFTP</span>使用了停止等待协议(TFTP使用UDP)。数据发送方在发送下一个数据块之前需要 <span style="background-color: inherit; line-height: 1.5;">等待接收方对已发送数据的确认。</span></p>
  74. <p><span style="color:#ff0000;background-color: inherit;">滑动窗口协议</span>:本章我们将介绍TCP所使用的被称为滑动窗口协议的另一种<span style="background-color: inherit; line-height: 1.5;">形式的流量控制方法。该协议允许发送方在停止并等待确认前可以连续发送多个分组。由于</span><span style="background-color: inherit; line-height: 1.5;">发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输。</span></p>
  75. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  76. <p><span style="background-color: inherit; line-height: 1.5;">注意到报文段7、14和16中的ACK确认了两个收到的报文段是很重要的。使用TCP的滑动 </span><span style="background-color: inherit; line-height: 1.5;">窗口协议时,接收方不必确认每一个收到的分组。在TCP中,<span style="color:#ff0000;background-color: inherit;">ACK是累积的</span>—它们表示接</span><span style="background-color: inherit; line-height: 1.5;">收方已经正确收到了一直到确认序号减1的所有字节。</span></p>
  77. <p><span style="background-color: inherit; line-height: 1.5;">窗口更新:另一个ACK(称为窗口更新)在17.4 ms后发送,表明接收方现在可以接收另外的4096个字节 </span><span style="background-color: inherit; line-height: 1.5;">的数据。虽然这看起来像一个ACK,但由于它并不确认任何新数据,只是用来增加窗口的右</span><span style="background-color: inherit; line-height: 1.5;">边沿,因此被称为窗口更新。</span></p>
  78. <p><span style="background-color: inherit; line-height: 1.5;">窗口更新:许多TCP实现在窗口大小增加了 </span><span style="background-color: inherit; line-height: 1.5;">两个最大报文段长度(本例中为2048字节,因为MSS为1024字节)或者最大可能窗口的50% </span><span style="background-color: inherit; line-height: 1.5;">(本例中为2048字节,因为最大窗口大小为4096字节)时发送这个窗口更新。</span></p>
  79. <p><br/></p>
  80. </ul>
  81. <p><span style="color:#ff0000;background-color: inherit;">PUSH标志</span>:发送方使用 <span style="background-color: inherit; line-height: 1.5;">该标志通知接收方将所收到的数据全部提交给接收进程。</span></p>
  82. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  83. <p><span style="background-color: inherit; line-height: 1.5;">通过允</span><span style="background-color: inherit; line-height: 1.5;">许客户应用程序通知其TCP设置PUSH标志,客户进程通知TCP在向服务器发送一个报文段时不要因等待额外数据而使已提交数据在缓存中滞留。</span></p>
  84. <p><span style="background-color: inherit; line-height: 1.5;">类似地,当服务器的TCP接收到一个设</span><span style="background-color: inherit; line-height: 1.5;">置了PUSH标志的报文段时,它需要立即将这些数据递交给服务器进程而不能等待判断是否还</span><span style="background-color: inherit; line-height: 1.5;">会有额外的数据到达。</span></p>
  85. <p><span style="background-color: inherit; line-height: 1.5;">如果待发送数据将清空发送缓存,则大多数的源于伯克利的实现能够自动设置PUSH标志。</span></p>
  86. <p><span style="background-color: inherit; line-height: 1.5;">观察图20-7,我们预计报文段12中的PUSH标志被置1,因为它是最后一个报文段。为什么发送方知道有更多的数据需要发送还设置报文段7中的PUSH标志呢?这是因为虽然我</span><span style="background-color: inherit; line-height: 1.5;">们指定写的是8192个字节的数据,但发送方的发送缓存却是4096个字节。</span></p>
  87. <p><br/></p>
  88. <p><br/></p>
  89. </ul>
  90. <p><span style="color:#ff0000;background-color: inherit;">慢启动(slow start):</span></p>
  91. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  92. <p><span style="background-color: inherit; line-height: 1.5;">慢启动为发送方的TCP增加了另一个窗口:拥塞窗口(congestion window),记为cwnd。</span><span style="background-color: inherit; line-height: 1.5;">当</span><span style="background-color: inherit; line-height: 1.5;">与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段(即另一端通告的报文 </span><span style="background-color: inherit; line-height: 1.5;">段大小)。</span></p>
  93. <p><span style="background-color: inherit; line-height: 1.5;">每收到一个ACK,拥塞窗口就增加一个报文段( cwnd以字节为单位,但是慢启动 </span><span style="background-color: inherit; line-height: 1.5;">以报文段大小为单位进行增加)。</span></p>
  94. <p><span style="background-color: inherit; line-height: 1.5;">发送方取拥塞窗口与通告窗口中的最小值作为发送上限。拥 </span><span style="background-color: inherit; line-height: 1.5;">塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。</span></p>
  95. </ul>
  96. <p><span style="color:#ff0000;background-color: inherit;">拥塞避免算法:</span><span style="background-color: inherit; line-height: 1.5;">拥塞避免算法和慢启动算法是两个目的不同、独立的算法。但是当拥塞发生时,我们希 </span><span style="background-color: inherit; line-height: 1.5;">望降低分组进入网络的传输速率,于是可以调用慢启动来作到这一点。在实际中这两个算法 </span><span style="background-color: inherit; line-height: 1.5;">通常在一起实现。 </span><span style="background-color: inherit; line-height: 1.5;">拥塞避免算法和慢启动算法需要对每个连接维持两个变量:一个拥塞窗口cwnd和一个慢 </span><span style="background-color: inherit; line-height: 1.5;">启动门限ssthresh。这样得到的算法的工作过程如下: </span></p>
  97. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  98. <p>1) 对一个给定的连接,初始化cwnd为1个报文段, ssthresh为65535个字节。</p>
  99. <p>2) TCP输出例程的输出不能超过cwnd和接收方通告窗口的大小。拥塞避免是发送方使用 <span style="background-color: inherit; line-height: 1.5;">的流量控制,而通告窗口则是接收方进行的流量控制。前者是发送方感受到的网络拥塞的估</span><span style="background-color: inherit; line-height: 1.5;">计,而后者则与接收方在该连接上的可用缓存大小有关。 </span></p>
  100. <p>3) 当拥塞发生时(超时或收到重复确认),ssthresh被设置为当前窗口大小的一半( cwnd <span style="background-color: inherit; line-height: 1.5;">和接收方通告窗口大小的最小值,<span style="color:#ff0000;background-color: inherit;">但最少为2个报文段</span>)。此外,如果是超时引起了拥塞,则 </span><span style="background-color: inherit; line-height: 1.5;">cwnd被设置为1个报文段(这就是慢启动)。 </span></p>
  101. <p><span style="background-color: inherit; line-height: 1.5;">4) 当新的数据被对方确认时,就增加cwnd,但增加的方法依赖于我们是否正在进行慢启</span><span style="background-color: inherit; line-height: 1.5;">动或拥塞避免。如果cwnd小于或等于ssthresh,则正在进行慢启动,否则正在进行拥塞避免。 </span></p>
  102. <p><span style="background-color: inherit; line-height: 1.5;">慢启动一直持续到我们回到当拥塞发生时所处位置的半时候才停止(因为我们记录了在步骤2</span><span style="background-color: inherit; line-height: 1.5;">中给我们制造麻烦的窗口大小的一半),然后转为执行拥塞避免。 </span></p>
  103. <p><span style="background-color: inherit; line-height: 1.5;">慢启动算法初始设置cwnd为1个报文段,此后每收到一个确认就加1。正正如20.6节描述的 </span><span style="background-color: inherit; line-height: 1.5;">那样,这会使窗口按指数方式增长:发送1个报文段,然后是2个,接着是4个⋯⋯。</span></p>
  104. <p><span style="background-color: inherit; line-height: 1.5;">拥塞避免算法要求每次收到一个确认时将cwnd增加1/cwnd。与慢启动的指数增加比起来, </span><span style="background-color: inherit; line-height: 1.5;">这是一种加性增长(additive increase)。</span></p>
  105. </ul>
  106. <p>慢启动和拥塞避免一起工作的示例:</p>
  107. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  108. <p><br/></p>
  109. <p><span style="background-color: inherit; line-height: 1.5;">在该图中,假定当cwnd为32个报文段时就会发生拥塞。于是设置ssthresh为16个报文段, </span><span style="background-color: inherit; line-height: 1.5;">而cwnd为1个报文段。</span></p>
  110. <p><span style="background-color: inherit; line-height: 1.5;">在时刻0发送了一个报文段,并假定在时刻1接收到它的ACK,此时</span><span style="background-color: inherit; line-height: 1.5;">cwnd增加为2。</span></p>
  111. <p><span style="background-color: inherit; line-height: 1.5;">接着发送了2个报文段,并假定在时刻2接收到它们的ACK,于是cwnd增加为4 </span><span style="background-color: inherit; line-height: 1.5;">(对每个ACK增加1次)。</span></p>
  112. <p><span style="background-color: inherit; line-height: 1.5;">这种指数增加算法一直进行到在时刻3和4之间收到8个ACK后cwnd等</span><span style="background-color: inherit; line-height: 1.5;">于ssthresh时才停止,从该时刻起,cwnd以线性方式增加,在每个往返时间内最多增加1个报</span><span style="background-color: inherit; line-height: 1.5;">文段。</span> </p>
  113. </ul>
  114. <p>带宽时延乘积:可以计算通道的容量为: <span style="background-color: inherit; line-height: 1.5;">capacity(bit) = bandwidth(b/s) × round-trip time(s) </span><span style="background-color: inherit; line-height: 1.5;">一般称之为带宽时延乘积。</span></p>
  115. <p>对每个连接,TCP管理4个不同的定时器。</p>
  116. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  117. <p><span style="background-color: inherit; line-height: 1.5;">1) 重传定时器使用于当希望收到另一端的确认(</span><span style="background-color: inherit; line-height: 1.5;">相关的问题,如拥塞避免)。 </span></p>
  118. <p><span style="background-color: inherit; line-height: 1.5;">2) 坚持( persist )定时器使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口。</span></p>
  119. <p><span style="background-color: inherit; line-height: 1.5;">3) 保活( keepalive )定时器可检测到一个空闲连接的另一端何时崩溃或重启。</span></p>
  120. <p><span style="background-color: inherit; line-height: 1.5;">4) 2MSL定时器测量一个连接处于TIME_WAIT状态的时间。</span></p>
  121. </ul>
  122. <p><span style="color:#ff0000;background-color: inherit;">超时重传:指数退避</span></p>
  123. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  124. <p><span style="background-color: inherit; line-height: 1.5;">现在检查连续重传之间不同的时间差,它们取整后分别为1(1.5)、3、6、12、24、48和多个64 </span><span style="background-color: inherit; line-height: 1.5;">秒。在本章的后面,我们将看到当第一次发送后所设置的超时时间实际上为1.5秒(它在首次</span><span style="background-color: inherit; line-height: 1.5;">发送后的1.0136秒而不是精确的1.5秒后,发生的原因我们已在图18-7中进行了解释),此后该 </span><span style="background-color: inherit; line-height: 1.5;">时间在每次重传时增加1倍并直至64秒。 </span></p>
  125. <p><span style="background-color: inherit; line-height: 1.5;">这个倍乘关系被称为“<span style="color:#ff0000;background-color: inherit;">指数退避(exponential backoff )</span>”。</span></p>
  126. </ul>
  127. <p>RTT往返时间测量:TCP超时与重传中最重要的部分就是对一个给定连接的往返时间( RTT)的测量。(最初算法、Jacobson的算法,Karn算法,较复杂。)</p>
  128. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  129. <p><span style="background-color: inherit; line-height: 1.5;">TCP计算往返时间并使用这些测量结果来维护一个被平滑的RTT估计器和被平滑的均值偏</span><span style="background-color: inherit; line-height: 1.5;">差估计器。这两个估计器用来计算下一个重传时间。</span></p>
  130. <p><span style="background-color: inherit; line-height: 1.5;">许多实现对每个窗口仅测量一次RTT。 </span></p>
  131. <p><span style="background-color: inherit; line-height: 1.5;">Karn算法在分组丢失时可以不测量RTT就能解决重传的二义性问题。</span></p>
  132. </ul>
  133. <p>RTT测量和时钟滴答:图21-3显示了本例中通过tcpdump的输出所得到的实际RTT与时钟滴答计数之间的关系。</p>
  134. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  135. <p><br/></p>
  136. <p><span style="background-color: inherit; line-height: 1.5;">在图的上端表示间隔为500 ms的时钟滴答,图的下端表示tcpdump的输出时间及定时器</span><span style="background-color: inherit; line-height: 1.5;">何时被启动和关闭。</span></p>
  137. <p><span style="background-color: inherit; line-height: 1.5;">在发送报文段1和接收到报文段2之间经历了3个滴答,时间为1.061秒, </span><span style="background-color: inherit; line-height: 1.5;">因此假定第1个滴答发生在0.03秒处(第1个滴答一定在0 ~ 0.061秒之间)。</span></p>
  138. <p><span style="background-color: inherit; line-height: 1.5;">接着该图表示了第2</span><span style="background-color: inherit; line-height: 1.5;">个被测量的RTT为什么被记为1个滴答,而第3个被记为2个滴答。</span></p>
  139. </ul>
  140. <p><span style="color:#ff0000;background-color: inherit;">rfc1122规定,RTT初始值是0秒、RTO初始值是3秒。</span></p>
  141. <p>快速重传与快速恢复算法(拥塞避免算法的修改建议)</p>
  142. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  143. <p><span style="background-color: inherit; line-height: 1.5;">在收到一个失序的报文段时,TCP立即需要产生一个ACK</span><span style="background-color: inherit; line-height: 1.5;">(一个重复的ACK)。这个重复的ACK不应该被迟延。该重复的ACK的目的在于让对方知道收</span><span style="background-color: inherit; line-height: 1.5;">到一个失序的报文段,并告诉对方自己希望收到的序号。 </span></p>
  144. <p><span style="background-color: inherit; line-height: 1.5;">由于我们不知道一个重复的ACK是由一个丢失的报文段引起的,还是由于仅仅出现了几</span><span style="background-color: inherit; line-height: 1.5;">个报文段的重新排序,因此我们等待少量重复的ACK到来。假如这只是一些报文段的重新排</span><span style="background-color: inherit; line-height: 1.5;">序,则在重新排序的报文段被处理并产生一个新的ACK之前,只可能产生1~2个重复的ACK。 </span></p>
  145. <p><span style="background-color: inherit; line-height: 1.5;">如果一连串<span style="color:#ff0000;background-color: inherit;">收到3个或3个以上的重复ACK</span>,就非常可能是一个报文段丢失了(我们在21.5节 </span><span style="background-color: inherit; line-height: 1.5;">中见到过这种现象)。于是我们<span style="color:#ff0000;background-color: inherit;">就重传丢失的数据报文段,而无需等待超时定时器溢出。这就</span></span><span style="background-color: inherit; line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">是快速重传算法。</span></span></p>
  146. <p><span style="background-color: inherit; line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">接下来执行的不是慢启动算法而是拥塞避免算法。这就是快速恢复算法。</span></span></p>
  147. <p><span style="background-color: inherit; line-height: 1.5;">收到重复的ACK不仅仅告诉我们一个分组丢</span><span style="background-color: inherit; line-height: 1.5;">失了。由于接收方只有在收到另一个报文段时才会产生重复的ACK,而该报文段已经离开了</span><span style="background-color: inherit; line-height: 1.5;">网络并进入了接收方的缓存。也就是说,在收发两端之间仍然有流动的数据,而我们不想执</span><span style="background-color: inherit; line-height: 1.5;">行慢启动来突然减少数据流。</span></p>
  148. </ul>
  149. <p>快速重传和快速恢复算法 通常按如下过程进行实现:</p>
  150. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  151. <p><span style="background-color: inherit; line-height: 1.5;">1) 当收到第3个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半。重传丢失的</span><span style="background-color: inherit; line-height: 1.5;">报文段。设置cwnd为ssthresh加上3倍的报文段大小。</span></p>
  152. <p><span style="background-color: inherit; line-height: 1.5;">2) 每次收到另一个重复的ACK时, cwnd增加1个报文段大小并发送1个分组(如果新的</span><span style="background-color: inherit; line-height: 1.5;">cwnd允许发送)。</span></p>
  153. <p><span style="background-color: inherit; line-height: 1.5;">3) 当下一个确认新数据的ACK到达时,设置cwnd为ssthresh(在第1步中设置的值)。这个</span><span style="background-color: inherit; line-height: 1.5;">ACK应该是在进行重传后的一个往返时间内对步骤1中重传的确认。另外,这个ACK也应该</span><span style="background-color: inherit; line-height: 1.5;">是对丢失的分组和收到的第1个重复的ACK之间的所有中间报文段的确认。这一步采用的是拥</span><span style="background-color: inherit; line-height: 1.5;">塞避免,因为当分组丢失时我们将当前的速率减半。</span></p>
  154. </ul>
  155. <p><span style="color:#ff0000;background-color: inherit;">糊涂窗口综合症</span>(Silly Window Syndrome):书上讲的太复杂,网上其他文章解释:</p>
  156. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  157. <p><span style="background-color: inherit; line-height: 1.5;">当发送端应用进程产生数据很慢、或接收端应用进程处理接收缓冲区数据很慢,或二者兼而有之;就会使应用进程间传送的报文段很小,特别是有效载荷很小。 </span></p>
  158. <p><span style="background-color: inherit; line-height: 1.5;">极端情况下,有效载荷可能只有1个字节;而传输开销有40字节(20字节的IP头+20字节的TCP头) 这种现象就叫糊涂窗口综合症。</span></p>
  159. </ul>
  160. <p>长肥管道(long fat pipe),也就是那些具有很大的带宽时延乘积的网络,以及TCP <span style="background-color: inherit; line-height: 1.5;">在这些网络上所具有的局限性。为处理长肥管道,我们描述两个新的TCP选项:窗口扩大选</span><span style="background-color: inherit; line-height: 1.5;">项(用来增加TCP的最大窗口,使之超过65535字节)和时间戳选项。</span></p>
  161. <p><span style="background-color: inherit; line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">MTU(Maximum Transmission Unit</span>):正如在图2-1看到的那样,以太网和802.3对帧的长度都有一个限制,其<span style="line-height: 1.5;">数据部分(用于承载上层协议的地方也就是Data域)的</span>最大值分别是</span><span style="background-color: inherit; line-height: 1.5;">1500和1492字节。链路层的这个特性称作</span><span style="background-color: inherit; line-height: 1.5;">MTU,最大传输单元。</span></p>
  162. <p><span style="color:#ff0000;background-color: inherit;">MTU</span>路径发现:路径MTU发现在MTU较大时,对于非本地连接,允许TCP使用比默认的536大的窗口。 <span style="background-color: inherit; line-height: 1.5;">这样可以提高性能。TCP的路径MTU发现按如下方式进行:</span></p>
  163. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  164. <p><span style="background-color: inherit; line-height: 1.5;">在连接建立时, TCP使用输出接口或对端声明的 </span><span style="background-color: inherit; line-height: 1.5;">MSS中的最小MTU作为起始的报文段大小。</span></p>
  165. <p><span style="background-color: inherit; line-height: 1.5;">一旦选定了起始的报文段大小,在该连接上的所有被TCP发送的IP数据报都将被设置DF </span><span style="background-color: inherit; line-height: 1.5;">比特。</span></p>
  166. <p><span style="background-color: inherit; line-height: 1.5;">如果某个中间路由器需要对一个设置了DF标志的数据报进行分片,它就丢弃这个数据</span><span style="background-color: inherit; line-height: 1.5;">报,并产生一个我们在11.6节介绍的ICMP的“不能分片”差错。</span></p>
  167. <p><span style="background-color: inherit; line-height: 1.5;">如果收到这个ICMP差错, TCP就减少段大小并进行重传。如果路由器产生的是一个较新 </span><span style="background-color: inherit; line-height: 1.5;">的该类ICMP差错,则报文段大小被设置为下一跳的MTU减去IP和TCP的首部长度。</span></p>
  168. <p><span style="background-color: inherit; line-height: 1.5;">如果是一</span><span style="background-color: inherit; line-height: 1.5;">个较旧的该类ICMP差错,则必须尝试下一个可能的最小MTU(见图2 - 5)。</span></p>
  169. <p><span style="background-color: inherit; line-height: 1.5;">当由这个ICMP差 </span><span style="background-color: inherit; line-height: 1.5;">错引起的重传发生时,拥塞窗口不需要变化,但要启动慢启动。</span></p>
  170. <p><span style="background-color: inherit; line-height: 1.5;">由于路由可以<span style="color:#ff0000;background-color: inherit;">动态变化</span>,因此在最后一次减少路径MTU的一段时间以后,可以尝试使用</span><span style="background-color: inherit; line-height: 1.5;">一个较大的值(直到等于对端声明的MSS或输出接口MTU的最小值)。RFC1191推荐这个时</span><span style="background-color: inherit; line-height: 1.5;">间间隔为10分钟。</span></p><p><br/></p>
  171. </ul>

添加新评论