主要是想跟大家分享一下对 Linux 内核报 Out of Socket memory 错误的相关研究,涉及几个内核参数的讲解:

  • cat /proc/net/sockstat

  • net.ipv4.tcp_mem = 4631232   6174976    9262464

  • net.ipv4.tcp_max_orphans = 262144

  • net.ipv4.tcp_orphan_retries = 0

------- 正文开始:------------------

举一个网上看到的例子:

一台 Nginx server,到晚上高峰 messages 出现大量的如下信息:

Apr 23 22:43:21 rs1 kernel: […] Out of socket memory

两种情况内核会出发 "Out of socket memory" 的信息:

  1. 有很多的孤儿套接字(orphan sockets) net.ipv4.tcp_max_orphans(常见

  2. tcp socket 用尽了内核所定义的内存限制 net.ipv4.tcp_mem(不常见

- 阅读剩余部分 -


在数据中心网络内,机器之间数据传输的往返时间(rtt)一般在10ms以内,为此调内部服务的超时时间一般会设置成50ms、200ms、500ms等,如果在传输过程中出现丢包,这样的服务超时时间,tcp层有机会发现并重传一次数据么?如果设置成200ms以内,答案是没有机会,原因是linux系统下第一次重传时间等于传输的往返时间上至少加上200ms的预测偏差值,即如果rtt值是7ms,第一次重传超时时间至少是207ms,这样如果对某个接口的超时时间设置成200ms以内, 即便是rtt时间很小,仍然无法容忍一次丢包,因为在tcp发现丢包之前,该接口已经超时了。

本文针对linux系统tcp数据包第一次重传时间的计算进行探究,结果会让人大吃一惊。提出的优化方法,理论上能够降低内部服务调用时延和出错量。

tcp发送数据包后,会设置一个定时器,到期后如果还没有收到对方的回复(ack),就会重传数据包。从发出数据包到第一次重传之间的间隔时间称为retransmission timeout(RTO),rto由数据包的往返时间(rtt)加上rtt的预测偏差(波动值)计算出来。

即 rto = srtt + rttvar,其中srtt是rtt的平滑值,而rttvar是波动值,代表可能的预测偏差。

接下来我们做一个试验。

先ping一下www.weibo.com,看一下数据包的往返时间,如下:

[xiaohong@localhost ~]$ ping www.weibo.com
PING www.weibo.com (123.125.104.197) 56(84) bytes of data.
64 bytes from 123.125.104.197: icmp_seq=1 ttl=55 time=3.65 ms
64 bytes from 123.125.104.197: icmp_seq=2 ttl=55 time=3.38 ms
64 bytes from 123.125.104.197: icmp_seq=3 ttl=55 time=4.34 ms
64 bytes from 123.125.104.197: icmp_seq=4 ttl=55 time=7.82 ms

再看一下tcp对到www.weibo.com的rtt相关数据,下面的命令是针对centos7(如果是以下的版本,运行的命令是ip route list tab cache)如下:

[xiaohong@localhost ~]$ sudo ip tcp_metrics
123.125.104.197 age 22.255sec rtt 7375us rttvar 7250us cwnd 10

由上面看出,平滑后的rtt值约为7ms,rttvar约为7ms,那按理说rto值应该是14ms左右,也就是等14ms后,如果没有收到对方的响应,就会重传数据。实际的情况会是这样么?

在一个命令窗口里,运行下面的命令:

[xiaohong@localhost ~]$ nc www.weibo.com 80
GET / HTTP/1.1
Host: www.weibo.com
Connection:

同时再开一个命令行窗口里,运行下面的命令:

[xiaohong@localhost iproute2-3.19.0]$ ss -eipn '( dport = :www )'
tcp   ESTAB      0      0              10.209.80.111:56486       123.125.104.197:80     users:(("nc",1713,3)) uid:1000 ino:14243 sk:ffff88002c992d00 <->
ts sack cubic wscale:0,7 rto:207 rtt:7.375/7.25 mss:1448 cwnd:10 send 15.7Mbps rcv_space:14600

从上面的结果可以看出,实际的rto值是207ms,相当于rtt值加上200ms,为什么呢?

- 阅读剩余部分 -


  1. <p>长肥管道( Long Fat Network ,即LFN,发音为 <span style="background-color: inherit; line-height: 1.5;">“elefan(t)s”):</span></p>
  2. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  3. <p><span style="background-color: inherit; line-height: 1.5;">在20.7节,我们把一个连接的容量表示为 </span><span style="background-color: inherit; line-height: 1.5;">capacity(b) = bandwidth (b/s) × round-trip time(s) </span><span style="background-color: inherit; line-height: 1.5;">并称之为带宽时延乘积。也可称它为两端的管道大小。 </span></p>
  4. <p><span style="background-color: inherit; line-height: 1.5;">当这个乘积变得越来越大时, TCP的某些局限性就会暴露出来。</span></p>
  5. <p><span style="background-color: inherit; line-height: 1.5;">具有大的带宽时延乘积的网络被称为长肥网络( Long Fat Network ,即LFN,发音为 </span><span style="background-color: inherit; line-height: 1.5;">“elefan(t)s”),而一个运行在LFN上的TCP连接被称为长肥管道。</span><span style="line-height: 1.5;"></span></p>
  6. </ul>
  7. <p>使<span style="background-color: inherit; line-height: 1.5;">用长肥管道会遇到多种问题。 </span></p>
  8. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  9. <p><span style="background-color: inherit; line-height: 1.5;">1) TCP首部中窗口大小为16 bit,从而将窗口限制在65535个字节内。但是从图24-5的最</span><span style="background-color: inherit; line-height: 1.5;">后一列可以看到,现有的网络需要一个更大的窗口来提供最大的吞吐量。在24.4节介</span><span style="background-color: inherit; line-height: 1.5;">绍的窗口扩大选项可以解决这个问题。 </span></p>
  10. <p><span style="background-color: inherit; line-height: 1.5;">2) 在一个长肥网络LFN内的分组丢失会使吞吐量急剧减少。如果只有一个报文段丢失, </span><span style="background-color: inherit; line-height: 1.5;">我们需要利用21.7节介绍的快速重传和快速恢复算法来使管道避免耗尽。但是即使使</span><span style="background-color: inherit; line-height: 1.5;">用这些算法,在一个窗口内发生的多个分组丢失也会典型地使管道耗尽(如果管道耗</span><span style="background-color: inherit; line-height: 1.5;">尽了,慢启动会使它渐渐填满,但这个过程将需要经过多个RTT)。 </span><span style="background-color: inherit; line-height: 1.5;">在RFC 1072 [Jacobson and Braden 1988]中<span style="color:#ff0000;background-color: inherit;">建议使用有选择的确认(SACK)来处理在</span></span><span style="background-color: inherit; line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">一个窗口发生的多个分组丢失</span>。</span></p>
  11. <p><span style="background-color: inherit; line-height: 1.5;">3) 我们在第21.4节看到许多TCP实现对每个窗口的RTT仅进行一次测量。它们并不对每个 </span><span style="background-color: inherit; line-height: 1.5;">报文段进行RTT测量。在一个长肥网络LFN上需要更好的RTT测量机制。我们将在24.5 </span><span style="background-color: inherit; line-height: 1.5;">节介绍时间戳选项,它允许更多的报文段被计时,包括重传。</span></p>
  12. <p><span style="background-color: inherit; line-height: 1.5;">4) TCP对每个字节数据使用一个<span style="color:#ff0000;background-color: inherit;">32 bit无符号的序号</span>来进行标识。如一个 </span><span style="background-color: inherit; line-height: 1.5;">相同序号N在MSL期间是否被重用的问题,也就是说,网络是否足够快以至于在不到 </span><span style="background-color: inherit; line-height: 1.5;">一个MSL的时候序号就发生了回绕。由于序号空间是有限的,在 </span><span style="background-color: inherit; line-height: 1.5;">已经传输了4 294 967 296个字节以后序号会被重用。FDDI(100 Mb/s)这个时间为5分钟,而一个千</span><span style="background-color: inherit; line-height: 1.5;">兆比网络(1000 Mb/s)则为34秒。在24.6节,我们将介绍一种对付这种情况的办法:使用TCP的时间戳选项的PAWS </span><span style="background-color: inherit; line-height: 1.5;">(Protection Against Wrapped Sequence numbers)算法(保护回绕的序号)。</span></p>
  13. </ul>
  14. <p><span style="background-color: inherit; line-height: 1.5;">TCP选项:</span></p>
  15. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  16. <p><br/></p>
  17. <p><span style="background-color: inherit; line-height: 1.5;">每个选项的开始是1字节kind字段,说明选项的类型。kind字段为0和1的选项仅占1个字节。 </span></p>
  18. <p><span style="background-color: inherit; line-height: 1.5;">其他的选项在kind字节后还有len字节。它说明的长度是指总长度,包括kind字节和len字节。 </span></p>
  19. <p><span style="background-color: inherit; line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">设置无操作选项的原因在于允许发方填充字段为4字节的倍数。</span>(因为TCP首部有一个“Header Length首部长度”字段 :首部中32 bit字的数目。)</span></p>
  20. </ul><!--more-->
  21. <p><span style="line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">MSS(Maximum Segment Size)</span>:最大报文段长度(MSS)表示TCP传往另一端的最大块<span style="color:#ff0000;background-color: inherit;">数据的长度</span>。</span></p>
  22. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  23. <p><span style="line-height: 1.5;">当一个连接建立时, </span><span style="line-height: 1.5;">连接的双方都要通告各自的MSS。</span></p>
  24. <p><span style="line-height: 1.5;">当TCP发送一个SYN时,或者是因为一个本地应用进程想发起一个连接,或 </span><span style="line-height: 1.5;">者是因为另一端的主机收到了一个连接请求,它能将MSS值设置为外出接口上的MTU长度减</span><span style="line-height: 1.5;">去固定的IP首部和TCP首部长度。</span></p>
  25. <p><span style="line-height: 1.5;">对于一个以太网,MSS值可达1460字节。</span></p>
  26. </ul>
  27. <p><span style="line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">窗口扩大选项</span></span><span style="line-height: 1.5;">:<span style="line-height: 1.5;">窗口扩大选项</span></span>使TCP的窗口定义从16 bit增加为32 bit。这并不是通过修改TCP首部来实现 <span style="background-color: inherit; line-height: 1.5;">的, TCP首部仍然使用16 bit ,而是通过定义一个选项实现对16 bit 的扩大操作( scaling </span><span style="background-color: inherit; line-height: 1.5;">operation )来完成的。于是TCP在内部将实际的窗口大小维持为32 bit的值。窗口扩大选项使最大的TCP窗口从65535增加到1千兆字节以上。</span></p>
  28. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  29. <p><span style="background-color: inherit; line-height: 1.5;">考虑一个使用窗口扩大选项的TCP连接,<span style="color:#ff0000;background-color: inherit;">其最大可能的窗口大小为1千兆字节</span>(最</span><span style="background-color: inherit; line-height: 1.5;">大的窗口是65535×2^14,而不是2^16×2^14)。</span></p>
  30. <p><span style="background-color: inherit; line-height: 1.5;">一个字节的移位记数器取值为0(没有扩大窗</span><span style="background-color: inherit; line-height: 1.5;">口的操作)到14。这个最大值14表示窗口大小为1 073 725 440字节(65535×2^14)。</span></p>
  31. <p><span style="background-color: inherit; line-height: 1.5;">这个选项只能够出现在一个SYN报文段中,因此当连接建立起来后,在每个方向的扩大 </span><span style="background-color: inherit; line-height: 1.5;">因子是固定的。</span></p>
  32. <p><span style="background-color: inherit; line-height: 1.5;">为了使用窗口扩大,两端必须在它们的SYN报文段中发送这个选项。</span></p>
  33. <p><span style="background-color: inherit; line-height: 1.5;">主动建 </span><span style="background-color: inherit; line-height: 1.5;">立连接的一方在其SYN中发送这个选项,但是被动建立连接的一方只能够在收到带有这个选 </span><span style="background-color: inherit; line-height: 1.5;">项的SYN之后才可以发送这个选项。</span></p>
  34. <p><span style="background-color: inherit; line-height: 1.5;">每个方向上的扩大因子可以不同。</span></p>
  35. <p><span style="background-color: inherit; line-height: 1.5;">如果主动连接的一方发送一个非零的扩大因子,但是没有从另一端收到一个窗口扩大选 </span><span style="background-color: inherit; line-height: 1.5;">项,它就将发送和接收的移位记数器置为0(即放弃窗口扩大)。这就允许较新的系统能够与较旧的、不理解新选</span><span style="background-color: inherit; line-height: 1.5;">项的系统进行互操作。</span></p>
  36. <p><span style="background-color: inherit; line-height: 1.5;">假定我们正在使用窗口扩大选项,发送移位记数为S,而接收移位记数则为R。于是我们 </span><span style="background-color: inherit; line-height: 1.5;">从另一端收到的每一个16 bit的<span style="color:#ff0000;background-color: inherit;">通告窗口将被左移R位以获得实际的通告窗口大小</span>。</span></p>
  37. </ul>
  38. <p><span style="line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">时间戳选项</span></span><span style="line-height: 1.5;">:时间戳选项允许多个报</span><span style="background-color: inherit; line-height: 1.5;">文段被精确计时,并允许接收方提供序号回绕保护(PAWS)。这对于高速连接是必须的。</span></p>
  39. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  40. <p><span style="background-color: inherit; line-height: 1.5;">时间戳选项使发送方在每个报文段中放置一个时间戳值。接收方在确认中返回这个数值,</span><span style="background-color: inherit; line-height: 1.5;">从而允许发送方为每一个收到的ACK计算RTT(我们必须说“每一个收到的ACK”而不是 </span><span style="background-color: inherit; line-height: 1.5;">“每一个报文段”,是因为TCP通常用一个ACK来确认多个报文段)。</span></p>
  41. <p><span style="background-color: inherit; line-height: 1.5;">时间戳是一个单调递增的值。由于接收方只需要回显收到的内容,因此不需要关注时间 </span><span style="background-color: inherit; line-height: 1.5;">戳单元是什么。这个选项不需要在两个主机之间进行任何形式的时钟同步。RFC 1323推荐在1</span><span style="background-color: inherit; line-height: 1.5;">毫秒和1秒之间将时间戳的值加1。 </span><span style="background-color: inherit; line-height: 1.5;">4.4BSD在启动时将时间戳始终设置为0,然后每隔500 ms将时间戳时钟加1。</span></p>
  42. <p><span style="background-color: inherit; line-height: 1.5;">我们提到过目前许多实现 </span><span style="background-color: inherit; line-height: 1.5;">为每一个窗口只计算一个RTT,对于包含8个报文段的窗口而言这是正确的。然而,较大的窗</span><span style="background-color: inherit; line-height: 1.5;">口大小则需要进行更好的RTT计算。</span></p>
  43. <p><span style="background-color: inherit; line-height: 1.5;">包含这个选项的TCP首部长度将从正常的20字节增加为32字节。</span></p>
  44. <p><span style="background-color: inherit; line-height: 1.5;">在连接建立阶段,对这个选项的规定与前一节讲的窗口扩大选项类似。主动发起连接的 </span><span style="background-color: inherit; line-height: 1.5;">一方在它的SYN中指定选项。只有在它从另一方的SYN中收到了这个选项之后,该选项才会</span><span style="background-color: inherit; line-height: 1.5;">在以后的报文段中进行设置。</span></p>
  45. </ul>
  46. <p>PAWS:防止回绕的序号:</p>
  47. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  48. <p><span style="background-color: inherit; line-height: 1.5;">从图24-8中观察到使用时间戳可以避免这种情况(回绕的序号)。接收方将时间戳视为序列</span><span style="background-color: inherit; line-height: 1.5;">号的一个32 bit的扩展。由于在时间E重新出现的报文段的时间戳为2,这比最近有效的时间戳</span><span style="background-color: inherit; line-height: 1.5;">小(5或6),因此PAWS算法将其丢弃。</span></p>
  49. <p><span style="background-color: inherit; line-height: 1.5;">PAWS算法不需要在发送方和接收方之间进行任何形式的时间同步。接收方所需要的就是 </span><span style="background-color: inherit; line-height: 1.5;">时间戳的值应该单调递增,并且每个窗口至少增加1。</span></p>
  50. </ul>
  51. <p>T/TCP:为事务用的TCP扩展,即T/TCP,允许一个客户/服务器的请求-应答序列在通常的情况下<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;">用SYN和FIN标志。</span></p>
  52. <p>TCP的性能:</p>
  53. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  54. <p><span style="background-color: inherit; line-height: 1.5;">不能够比由接收方提供的窗口大小除以往返时间所得结果运行得更快(这就是带宽时 </span><span style="background-color: inherit; line-height: 1.5;">延乘积公式,使用窗口大小作为带宽时延乘积,并解出带宽)。如果使用24.4节的最大窗口扩</span><span style="background-color: inherit; line-height: 1.5;">大因子14,则窗口大小为1.073 千兆字节,所以这除以RTT的结果就是带宽的极限。</span></p>
  55. <p><span style="background-color: inherit; line-height: 1.5;">所有这些数字的重要意义就是TCP的最高运行速率的真正上限是由TCP的窗口大小和光速</span><span style="background-color: inherit; line-height: 1.5;">决定的。</span></p><p><br/></p>
  56. </ul>
<p><br/></p>

- 阅读剩余部分 -


  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>

- 阅读剩余部分 -


  1. <p>以太网帧的封装格式:</p>
  2. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  3. <p>帧头8个字节(目的地址、源地址、类型)、数据(46-1500字节,1500即MTU)、帧尾4个字节(CRC校验);</p>
  4. <p>以太网规定数据部分最少为46字节,为了保证这一点,必须在不足的空间插入填充(pad)字节。 </p>
  5. <p>一个正常的以太网帧的长度在64-1518字节之间,由头部、数据部分和帧检测序列三部分组成。</p>
  6. </ul>
  7. <p>环回接口:[图2-4 环回接口处理IP数据报的过程 ]<span style="background-color: inherit; line-height: 1.5;">图中需要指出的关键点是: </span></p>
  8. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  9. <p><span style="background-color: inherit; line-height: 1.5;">传给环回地址(一般是127.0.0.1)的任何数据均作为IP输入。 </span></p>
  10. <p><span style="background-color: inherit; line-height: 1.5;">传给广播地址或多播地址的数据报复制一份传给环回接口,然后送到以太网上。这是 </span><span style="background-color: inherit; line-height: 1.5;">因为广播传送和多播传送的定义(第12章)包含主机本身。 </span><span style="background-color: inherit; line-height: 1.5;">这是广播的一般特性:发送主机也能通过某种内部环回机制收到一份广播报文拷贝。由于术语“广播”的定义是指局域网上的所有主机,因此它必须包括发送</span><span style="background-color: inherit; line-height: 1.5;">主机在内(参见图2 - 4,当以太网驱动程序识别出目的地址是广播地址后,它就把分组送到网</span><span style="background-color: inherit; line-height: 1.5;">络上,同时传一份拷贝到环回接口)。</span></p>
  11. <p><span style="background-color: inherit; line-height: 1.5;">任何传给该主机IP地址的数据均送到环回接口。</span></p>
  12. </ul>
  13. <p>ARP:</p>
  14. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  15. <p>在ARP背后有一个基本概念,那就是网络接口有一个硬件地址(一个48 bit的值,标识不 <span style="background-color: inherit; line-height: 1.5;">同的以太网或令牌环网络接口)。在硬件层次上进行的数据帧交换必须有正确的接口地址。但</span><span style="background-color: inherit; line-height: 1.5;">是,TCP/IP有自己的地址: 32 bit的IP地址。<span style="color:#ff0000;background-color: inherit;">知道主机的IP地址并不能让内核发送一帧数据给</span></span><span style="background-color: inherit; line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">主机。</span>内核(如以太网驱动程序)必须知道目的端的硬件地址才能发送数据。ARP的功能是</span><span style="background-color: inherit; line-height: 1.5;">在32 bit的IP地址和采用不同网络技术的硬件地址之间提供动态映射。</span></p>
  16. <p><span style="background-color: inherit; line-height: 1.5;">ARP请求/应答完成前:注意,在线路上始终看不到TCP的报文段。我们能看到的是ARP请求。直到ARP回答返回 </span><span style="background-color: inherit; line-height: 1.5;">时,TCP报文段才可以被发送,因为硬件地址到这时才可能知道。如果我们用过滤模式运行</span><span style="background-color: inherit; line-height: 1.5;">tcpdump命令,只查看TCP数据,那么将没有任何输出。</span></p>
  17. <p><span style="background-color: inherit; line-height: 1.5;">ff:ff:ff:ff:ff:ff,这是一个以太网广播地址。尽管ARP请求是广播的,但是ARP应答是直接送到请求端主机的,而不是广播的。</span></p>
  18. <p><span style="background-color: inherit; line-height: 1.5;">一般情况下,当系统收到ARP请求或发送ARP应答时,都要把请求端的硬件地址和IP地 </span><span style="background-color: inherit; line-height: 1.5;">址存入ARP高速缓存。</span></p>
  19. </ul>
  20. <p><span style="color:#ff0000;background-color: inherit; line-height: 1.5;">免费ARP(gratuitous ARP)</span><span style="background-color: inherit; line-height: 1.5;">:它是指主机发送ARP查 </span><span style="background-color: inherit; line-height: 1.5;">找自己的IP地址。通常,它发生在系统引导期间进行接口配置的时候(检测IP地址是否冲突)。</span><span style="background-color: inherit; line-height: 1.5;">免费ARP可以有两个方面的作用:</span></p>
  21. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  22. <p><span style="background-color: inherit; line-height: 1.5;">1) 一个主机可以通过它来确定另一个主机是否设置了相同的IP地址(检测IP冲突)。</span></p>
  23. <p><span style="background-color: inherit; line-height: 1.5;">2)如果发送免费ARP的主机正好改变了硬件地址(很可能是主机关机了,并换了一块接 </span><span style="background-color: inherit; line-height: 1.5;">口卡,然后重新启动),那么这个分组就可以使其他主机高速缓存中旧的硬件地址进行相应的</span><span style="background-color: inherit; line-height: 1.5;">更新。一个比较著名的ARP协议事实[Plummer 1982]是,如果主机收到某个IP地址的ARP请求, </span><span style="background-color: inherit; line-height: 1.5;">而且它已经在接收者的高速缓存中,那么就要用ARP请求中的发送端硬件地址(如以太网地</span><span style="background-color: inherit; line-height: 1.5;">址)对高速缓存中相应的内容进行更新。主机接收到任何ARP请求都要完成这个操作( ARP</span><span style="background-color: inherit; line-height: 1.5;">请求是在网上广播的,因此每次发送ARP请求时网络上的所有主机都要这样做)。</span></p>
  24. </ul><!--more-->
  25. <p>ARP代理 :</p>
  26. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  27. <p><span style="background-color: inherit; line-height: 1.5;">如果ARP请求是从一个网络的主机发往另一个网络上的主机,那么连接这两个网络的路</span><span style="background-color: inherit; line-height: 1.5;">由器就可以回答该请求,这个过程称作委托ARP或ARP代理(Proxy ARP)。</span></p>
  28. <p><span style="background-color: inherit; line-height: 1.5;">这样可以欺骗发起</span><span style="background-color: inherit; line-height: 1.5;">ARP请求的发送端,使它误以为路由器就是目的主机,而事实上目的主机是在路由器的“另</span><span style="background-color: inherit; line-height: 1.5;">一边”。</span></p>
  29. <p><span style="background-color: inherit; line-height: 1.5;">路由器的功能相当于目的主机的代理,把分组从其他主机转发给它。</span></p>
  30. </ul>
  31. <p>RARP:</p>
  32. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  33. <p>无盘工作站获取自身的IP地址(不含掩码);</p>
  34. <p>ICMP地址掩码请求:无盘系统在引导过程中获取自己的子网掩码;</p>
  35. <p>BOOTP:无盘系统在引导过程中获取自己的子网掩码的另一个方法是BOOTP协议;</p>
  36. </ul>
  37. <p><span style="color:#ff0000;background-color: inherit;">以太网帧的最小长度是64字节,最大长度是1518字节:</span></p>
  38. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  39. <p><span style="line-height: 1.5;">在图5-1中 </span><span style="line-height: 1.5;">需要指出的另一点是,第2行中的以太网数据帧长度比最小长度还要小(在4.5节中我们说过应该</span><span style="line-height: 1.5;">是60字节——不包含4字节帧尾)。其<span style="color:#0070c0;background-color: inherit;">原因是我们在发送该以太网数据帧的系统(bsdi)上运行tcpdump命令</span>。应用程</span><span style="line-height: 1.5;">序rarpd写42字节到BSD分组过滤设备上(其中14字节为以太网数据帧的报头,剩下的28字节是</span><span style="line-height: 1.5;">RARP应答),这就是tcpdump收到的副本。但是以太网设备驱动程序要把这一短帧填充空白字</span><span style="line-height: 1.5;">符以达到最小传输长度(60)。<span style="color:#0070c0;background-color: inherit;">如果我们在另一个系统上运行tcpdump命令,其长度将会是60</span>。</span></p>
  40. </ul>
  41. <p>这里需要注意的另一点是TFTP客户程序所采用的不太好的超时重传算法:</p>
  42. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  43. <p>它只是假定5 <span style="line-height: 1.5;">秒是足够的,因此每隔5秒就重传一次,总共需要25秒钟的时间。</span></p>
  44. <p><span style="line-height: 1.5;">在后面我们将看到TCP有一</span><span style="line-height: 1.5;">个较好的超时重发算法。</span></p>
  45. <p><span style="line-height: 1.5;">TFTP客户程序所采用的超时重传算法已被RFC所禁用。不过,在作者所在子网上</span><span style="line-height: 1.5;">的三个系统以及Solaris 2.2仍然在使用它。</span></p>
  46. <p><span style="line-height: 1.5;">AIX 3.2.2采用一种指数退避方法来设置超时</span><span style="line-height: 1.5;">值,分别在0、5、15和35秒时重发报文,这正是所推荐的方法。</span></p>
  47. </ul>
  48. <p><span style="line-height: 1.5;">ICMP:</span></p>
  49. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  50. <p><span style="line-height: 1.5;">当发送一份ICMP差错报文时,报文始终包含IP的首部和产生ICMP差错报文的IP数据报的 </span><span style="line-height: 1.5;">前8个字节。这样,接收ICMP差错报文的模块就会把它与某个特定的协议(根据IP数据报首部中的协议字段来判断)和用户进程(根据包含在IP数据报前8个字节中的TCP或UDP报文首 </span><span style="line-height: 1.5;">部中的TCP或UDP端口号来判断)联系起来。</span></p>
  51. <p><span style="line-height: 1.5;">ICMP不可达差错:ICMP端口不可达差错,一种常见的ICMP差错。对返回的ICMP差错信息进 </span><span style="line-height: 1.5;">行了分析:导致差错的IP数据报的首部及后续8个字节。这个信息对于ICMP差错的接收方来 </span><span style="line-height: 1.5;">说是必要的,可以更多地了解导致差错的原因。这是因为TCP和UDP都在它们的首部前8个字</span><span style="line-height: 1.5;">节中存入源端口号和目的端口号。</span></p>
  52. <p><span style="line-height: 1.5;">ICMP地址掩码请求与应答:<span style="color:#ff0000;background-color: inherit;">广播的一般特性:发送主机也能通过某种内部环回机制收到一 </span></span><span style="line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">份广播报文拷贝</span>。由于术语“广播”的定义是指局域网上的所有主机,因此它必须包括发送主机在内(参见图2 - 4,当以太网驱动程序识别出目的地址是广播地址后,它就把分组送到网 </span><span style="line-height: 1.5;">络上,同时传一份拷贝到环回接口)。</span></p>
  53. </ul>
  54. <p><span style="line-height: 1.5;">ICMP:不会导致产生ICMP差错报文的情况:(防止ICMP差错报文对广播分组响应所带来的广播风暴)</span></p>
  55. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  56. <p><span style="line-height: 1.5;">在对ICMP差错报文进行响应时,不会生成另一份ICMP差错报文(防止循环) </span></p>
  57. <p><span style="line-height: 1.5;">目的地址是广播地址或多播地址的IP数据报 </span></p>
  58. <p><span style="line-height: 1.5;">作为链路层广播的数据报 </span></p>
  59. <p><span style="line-height: 1.5;">不是IP分片的第一片 </span></p>
  60. <p><span style="line-height: 1.5;">源地址不是单个主机的数据报(源地址不能为零地址、环回地址、广播地址或多播地址)</span></p>
  61. </ul>
  62. <p><span style="line-height: 1.5;">Ping:</span></p>
  63. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  64. <p><span style="line-height: 1.5;">ping程序通过在ICMP报文数据中存放发送请求的时间值来计算往返时间。当应答返回</span><span style="line-height: 1.5;">时,用当前时间减去存放在ICMP报文中的时间值,即是往返时间。</span></p>
  65. <p><span style="line-height: 1.5;">Unix系统在实现ping程序时是把ICMP报文中的标识符字段置成发送进程的ID号。这样 </span><span style="line-height: 1.5;">即使在同一台主机上同时运行了多个ping程序实例, ping程序也可以识别出返回的信息。</span></p>
  66. <p><span style="line-height: 1.5;">注:在Linux系统上抓包测试ping,发现identifier不是进程ID号。</span></p>
  67. </ul>
  68. <p><span style="line-height: 1.5;">Traceroute:</span></p>
  69. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  70. <p>UDP的规则之一是,如果收到一份UDP数据报而目的端口与某个正在使用的进程不相符, 那么UDP返回一个ICMP不可达报文。Traceroute程序所要做的就是区分接收到的ICMP报文是超时还是端口不可达,以判断什么时候结束。</p>
  71. <p><span style="line-height: 1.5;">当路由器收到一份IP数据报,如果其TTL字段是0或1,则路由器不转发该数据报(接收到 </span><span style="line-height: 1.5;">这种数据报的目的主机可以将它交给应用程序,这是因为不需要转发该数据报。但是在通常</span><span style="line-height: 1.5;">情况下,系统不应该接收TTL字段为0的数据报)。相反,路由器将该数据报丢弃,并给信源 </span><span style="line-height: 1.5;">机发一份ICMP“超时”信息。</span></p>
  72. <p><span style="line-height: 1.5;">返回的ICMP报文中的信源IP地址是UDP数据报到达的路由器接口的IP地址。这与 </span><span style="line-height: 1.5;">IP记录路由选项( 7.3节)不同,记录的IP地址指的是发送接口地址。</span></p>
  73. <p><span style="line-height: 1.5;">Traceroute RTT往返时延计算:往返时间是由发送主机的traceroute程序计算的。它是指从traceroute程序到该路</span><span style="line-height: 1.5;">由器的总往返时间。</span></p>
  74. <p><span style="line-height: 1.5;">Traceroute程序将其发送的UDP数据报</span><span style="line-height: 1.5;">的源端口号设置为Unix进程号与32768之间的逻辑或值。对于在同一台主机上多次运行 </span><span style="line-height: 1.5;">traceroute程序的情况,每个进程都查看ICMP返回的UDP首部的源端口号,并且只处理那</span><span style="line-height: 1.5;">些对自己发送应答的报文。</span></p>
  75. </ul>
  76. <p><span style="line-height: 1.5;">IP选路:对于一个给定的路由器,可以打印出五种不同的标志(flag):</span></p>
  77. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  78. <p><span style="line-height: 1.5;">U 该路由可以使用。</span></p>
  79. <p><span style="line-height: 1.5;">G 该路由是到一个网关(路由器)。如果没有设置该标志,说明目的地是直接相连的。</span></p>
  80. <p><span style="line-height: 1.5;">H 该路由是到一个主机,也就是说,目的地址是一个完整的主机地址。如果没有设置该 </span><span style="line-height: 1.5;">标志,说明该路由是到一个网络,而目的地址是一个网络地址:一个网络号,或者网</span><span style="line-height: 1.5;">络号与子网号的组合。 </span></p>
  81. <p><span style="line-height: 1.5;">D 该路由是由重定向报文创建的( 9 . 5节)。</span></p>
  82. <p><span style="line-height: 1.5;">M 该路由已被重定向报文修改( 9 . 5节)。</span></p>
  83. </ul>
  84. <p>RIP路由协议:RIP报文包含中在UDP数据报中;RIP常用的UDP端口号是520。</p>
  85. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  86. <p>定期选路更新:每过30秒,所有或部分路由器会将其完整路由表发送给相邻路由器。发 <span style="background-color: inherit; line-height: 1.5;">送路由表可以是广播形式的(如在以太网上),或是发送给点对点链路的其他终点的。 </span></p>
  87. <p>触发更新:每当一条路由的度量发生变化时,就对它进行更新。不需要发送完整路由表, <span style="background-color: inherit; line-height: 1.5;">而只需要发送那些发生变化的表项。 </span></p>
  88. <p>每条路由都有与之相关的定时器:如果运行RIP的系统发现一条路由在3分钟内未更新, <span style="background-color: inherit; line-height: 1.5;">就将该路由的度量设置成无穷大(16),并标注为删除。这意味着已经在6个30秒更新时间里</span><span style="background-color: inherit; line-height: 1.5;">没收到通告该路由的路由器的更新了。再过60秒,将从本地路由表中删除该路由,以保证该</span><span style="background-color: inherit; line-height: 1.5;">路由的失效已被传播开。</span></p>
  89. </ul>
  90. <p>RIP度量:</p>
  91. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  92. <p><span style="background-color: inherit; line-height: 1.5;">RIP所使用的度量是以跳( hop )计算的。所有直接连接接口的跳数为1。</span></p>
  93. <p><span style="background-color: inherit; line-height: 1.5;">如果相邻路由器通告它与其他网络路 </span><span style="background-color: inherit; line-height: 1.5;">由的跳数为1,那么我们与那个网络的度量就是2,这是因为为了发送报文到该网络,我们必</span><span style="background-color: inherit; line-height: 1.5;">须经过那个路由器。</span></p>
  94. <p><span style="background-color: inherit; line-height: 1.5;">如果在该AS内从一个路由器到一个网络有多条路由,那么路由器将选择跳数最 </span><span style="background-color: inherit; line-height: 1.5;">小的路由,而忽略其他路由。 </span></p>
  95. <p><span style="background-color: inherit; line-height: 1.5;">跳数的最大值是15,这意味着RIP只能用在主机间最大跳数值为15的AS内。度量为16表 </span><span style="background-color: inherit; line-height: 1.5;">示到无路由到达该IP地址。</span></p>
  96. </ul>
  97. <p>RIP的缺陷:<span style="background-color: inherit; line-height: 1.5;">它有一些缺陷。</span></p>
  98. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  99. <p><span style="background-color: inherit; line-height: 1.5;">首先, RIP没有子网地址的概念;</span></p>
  100. <p><span style="background-color: inherit; line-height: 1.5;">其次,在路由器或链路发生故障后,需要很长的一段时间才能稳定下来。这段时间通常 </span><span style="background-color: inherit; line-height: 1.5;">需要几分钟。在这段建立时间里,可能会发生路由环路。</span></p>
  101. <p><span style="background-color: inherit; line-height: 1.5;">采用跳数作为路由度量忽略了其他一些应该考虑的因素。同时,度量最大值为15则限制 </span><span style="background-color: inherit; line-height: 1.5;">了可以使用RIP的网络的大小。</span></p>
  102. <p><span style="background-color: inherit; line-height: 1.5;">第2 </span><span style="background-color: inherit; line-height: 1.5;">版RIP是其最近的一个改进版,它支持子网,还有一些其他改进技术。称为RIPv2。</span></p>
  103. </ul>
  104. <p>OSPF路由协议:与采用距离向量的RIP协议不同的是, OSPF是一个链路状态协议。</p>
  105. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  106. <p><span style="background-color: inherit; line-height: 1.5;">OSPF与RIP(以及其他选路协议)的不同点在于, OSPF直接使用IP。也就是说,它并不 </span><span style="background-color: inherit; line-height: 1.5;">使用UDP或TCP。对于IP首部的protocol字段,OSPF有其自己的值(图3 - 1);</span></p>
  107. <p><span style="background-color: inherit; line-height: 1.5;">当对同一个目的地址存在着多个相同费用的路由时,OSPF在这些路由上平均分配流量。 </span><span style="background-color: inherit; line-height: 1.5;">我们称之为流量平衡。</span></p>
  108. <p><span style="background-color: inherit; line-height: 1.5;">OSPF支持子网:子网掩码与每个通告路由相连。到一个主机的路由是通过全1子网掩码进行通告的。默认路由是以IP地址为0.0.0.0、网络掩码 </span><span style="background-color: inherit; line-height: 1.5;">为全0进行通告的。</span></p>
  109. <p><span style="background-color: inherit; line-height: 1.5;">OSPF采用多播(第12章),而不是广播形式,以减少不参与OSPF的系统负载。</span></p>
  110. </ul>
  111. <p>BGP协议:BGP是一种不同自治系统的路由器之间进行通信的外部网关协议。1993年开发第4版的BGP(见RFC 1467 [Topolcic <span style="background-color: inherit; line-height: 1.5;">1993]),以支持我们将在10.8节描述的CIDR。</span></p>
  112. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  113. <p>BGP允许使用基于策略的选路。由自治系统管理员制订策略,选路策略与政治、安全或经济因素有关。</p>
  114. <p>BGP与RIP和OSPF的不同之处在于BGP使用TCP作为其传输层协议;</p>
  115. <p>BGP是一个距离向量协议,但是与(通告到目的地址跳数的) RIP不同的是,BGP列举了 <span style="background-color: inherit; line-height: 1.5;">到每个目的地址的路由(自治系统到达目的地址的序列号)</span></p>
  116. <p>BGP通过定期发送keepalive报文给其邻站来检测TCP连接对端的链路或主机失败。两个报 <span style="background-color: inherit; line-height: 1.5;">文之间的时间间隔建议值为30秒。应用层的keepalive报文与TCP的keepalive选项(第23章) </span><span style="background-color: inherit; line-height: 1.5;">是独立的。</span></p>
  117. </ul>
  118. <p>CIDR:无类型域间选路。无类型域间选路( CIDR) <span style="background-color: inherit; line-height: 1.5;">是一个防止Internet路由表膨胀的方法,它也称为超网( supernetting)。可以减小Internet路由表的大 </span><span style="background-color: inherit; line-height: 1.5;">小。</span></p>
  119. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  120. <p><span style="background-color: inherit; line-height: 1.5;">CIDR的基本观点是采用一种分配多个IP地址的方式,使其能够将路由表中的许多表项总 </span><span style="background-color: inherit; line-height: 1.5;">和( summarization )成更少的数目;</span><span style="background-color: inherit; line-height: 1.5;">要使用这种总和,必须满足以下三种特性: </span></p>
  121. <p><span style="background-color: inherit; line-height: 1.5;">1) 为进行选路要对多个I P地址进行总和时,这些I P地址必须具有相同的高位地址比特。</span></p>
  122. <p><span style="background-color: inherit; line-height: 1.5;">2) 路由表和选路算法必须扩展成根据32 bit IP地址和32 bit掩码做出选路决策。</span></p>
  123. <p><span style="background-color: inherit; line-height: 1.5;">3) 必须扩展选路协议使其除了32 bit地址外,还要有32 bit掩码。OSPF(10.6节)和RIP-2 </span><span style="background-color: inherit; line-height: 1.5;">(10.5节)都能够携带第4版BGP所提出的32 bit掩码。</span></p>
  124. <p><span style="background-color: inherit; line-height: 1.5;">“无类型”的意思是现在的选路决策是基于整个32 bit IP地址的掩码操作,而不管其IP地 </span><span style="background-color: inherit; line-height: 1.5;">址是A类、B类或是C类,都没有什么区别。</span></p>
  125. </ul>
  126. <p>MTU<span style="background-color: inherit; line-height: 1.5;"><span style="color:#ff0000;background-color: inherit;">(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>
  127. <p>IP分片:正如我们在2.8节描述的那样,物理网络层一般要限制每次发送数据帧的最大长度。</p>
  128. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  129. <p><span style="background-color: inherit; line-height: 1.5;">任何</span><span style="background-color: inherit; line-height: 1.5;">时候IP层接收到一份要发送的IP数据报时,它要判断向本地哪个接口发送数据(选路),并查</span><span style="background-color: inherit; line-height: 1.5;">询该接口获得其MTU。IP把MTU与数据报长度进行比较,如果需要则进行分片。分片可以发</span><span style="background-color: inherit; line-height: 1.5;">生在原始发送端主机上,也可以发生在中间路由器上。</span></p>
  130. <p><span style="background-color: inherit; line-height: 1.5;">当IP数据报被分片后,每一片都成为一个分组,具有自己的IP首部,并在选择路由时与 </span><span style="background-color: inherit; line-height: 1.5;">其他分组独立。这样,当数据报的这些片到达目的端时有可能会失序,但是在IP首部中有足</span><span style="background-color: inherit; line-height: 1.5;">够的信息让接收端能正确组装这些数据报片。</span></p>
  131. <p><span style="background-color: inherit; line-height: 1.5;">需要重申的是,任</span><span style="background-color: inherit; line-height: 1.5;">何运输层首部只出现在第1片数据中(如端口号在UDP首部,只能在第1片中被发现)。</span></p>
  132. <p><span style="background-color: inherit; line-height: 1.5;">最后,标志字段中有一个比特称作“不分片”位。如果将这一比特置1,IP将不对数据报 </span><span style="background-color: inherit; line-height: 1.5;">进行分片。相反把数据报丢弃并发送一个ICMP差错报文(“需要进行分片但设置了不分片比</span><span style="background-color: inherit; line-height: 1.5;">特”,见图6 - 3)给起始端。</span></p>
  133. </ul>
  134. <p><span style="color:#ff0000;background-color: inherit;">IP分片重组:</span><span style="line-height: 1.5;">回忆IP首部(图3 - 1),下面这些字段用于分片过程。</span></p>
  135. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  136. <p><span style="background-color: inherit; line-height: 1.5;">对于发送端发送的每份IP数据报来说, </span><span style="background-color: inherit; line-height: 1.5;">其标识(Identification)字段都包含一个唯一值。该值在数据报分片时被复制到每个片中(我们现在已经看到</span><span style="background-color: inherit; line-height: 1.5;">这个字段的用途)。</span></p>
  137. <p><span style="background-color: inherit; line-height: 1.5;">标志(flags)字段用其中一个比特来表示“更多的片”。除了最后一片外,其他每</span><span style="background-color: inherit; line-height: 1.5;">个组成数据报的片都要把该比特置1。</span></p>
  138. <p><span style="background-color: inherit; line-height: 1.5;">片偏移(fragment offset)字段指的是该片偏移原始数据报开始处的位置。 </span><span style="background-color: inherit; line-height: 1.5;">另外,当数据报被分片后,每个片的总长度值(total length)要改为该片的长度值。</span></p>
  139. </ul>
  140. <p>广播和多播</p>
  141. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  142. <p><span style="background-color: inherit; line-height: 1.5;">广播和多播仅应用于UDP,它们对需将报文同时传往多个接收者的应用来说十分重要。</span></p>
  143. <p><span style="background-color: inherit; line-height: 1.5;">有时一个主机要向网上的所有其他主机发送帧, </span><span style="background-color: inherit; line-height: 1.5;">这就是广播。通过ARP和RARP可以看到这一过程。多播 </span><span style="background-color: inherit; line-height: 1.5;">(multicast) 处于单播和广播之间:帧仅传送给属于多播组的</span><span style="background-color: inherit; line-height: 1.5;">多个主机。</span></p>
  144. <p><span style="background-color: inherit; line-height: 1.5;">对于以太网,当地址中 </span><span style="background-color: inherit; line-height: 1.5;">最高字节的最低位设置为1时表示该地址是一个多播地址, </span><span style="background-color: inherit; line-height: 1.5;">用十六进制可表示为01:00:00:00:00:00(以太网广播地址</span><span style="background-color: inherit; line-height: 1.5;">ff:ff:ff:ff:ff:ff可看作是以太网多播地址的特例)。</span></p>
  145. <p><span style="background-color: inherit; line-height: 1.5;">多播是一种将报文发往多个接收者的通信方式。在许多应用中,它比广播更好,因为多 </span><span style="background-color: inherit; line-height: 1.5;">播降低了不参与通信的主机的负担。简单的主机成员报告协议(IGMP)是多播的基本模块。</span></p>
  146. </ul>
  147. <p>广播地址:</p>
  148. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  149. <p><span style="background-color: inherit; line-height: 1.5;">受限的广播:受限的广播地址是255.255.255.255。该地址用于主机配置过程中IP数据报的目的地址, </span><span style="background-color: inherit; line-height: 1.5;">此时,主机可能还不知道它所在网络的网络掩码,甚至连它的IP地址也不知道。在任何情况下,路由器都不转发目的地址为受限的广播地址的数据报,这样的数据报仅 </span><span style="background-color: inherit; line-height: 1.5;">出现在本地网络中。</span></p>
  150. <p><span style="background-color: inherit; line-height: 1.5;">指向网络的广播:指向网络的广播地址是主机号为全1的地址。A类网络广播地址为netid.255.255.255,其中 </span><span style="background-color: inherit; line-height: 1.5;">netid为A类网络的网络号。一个路由器必须转发指向网络的广播,但它也必须有一个不进行转发的选择。</span></p>
  151. <p><span style="background-color: inherit; line-height: 1.5;">指向子网的广播:指向子网的广播地址为主机号为全1且有特定子网号的地址。</span><span style="background-color: inherit; line-height: 1.5;">指向所有子网的广播:指向所有子网的广播也需要了解目的网络的子网掩码,以便与指向网络的广播地址区分</span><span style="background-color: inherit; line-height: 1.5;">开。指向所有子网的广播地址的子网号及主机号为全1。例如,如果目的子网掩码为</span><span style="background-color: inherit; line-height: 1.5;">255.255.255.0,那么IP地址128.1.255.255是一个指向所有子网的广播地址。然而,如果网络</span><span style="background-color: inherit; line-height: 1.5;">没有划分子网,这就是一个指向网络的广播。</span></p>
  152. </ul>
  153. <p>多播地址:</p>
  154. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  155. <p><span style="background-color: inherit; line-height: 1.5;">知名多播地址:224.0.0.1代表“该子网内的所有系统组”,224.0.0.2代表“该子网内的所有路由器 </span><span style="background-color: inherit; line-height: 1.5;">组”。多播地址224.0.1.1用作网络时间协议NTP,224.0.0.9用作RIP-2 (见10.5节),224.0.1.2用</span><span style="background-color: inherit; line-height: 1.5;">作SGI公司的dogfight应用。</span></p>
  156. <p><span style="background-color: inherit; line-height: 1.5;">对于以太网,当地址中 </span><span style="background-color: inherit; line-height: 1.5;">最高字节的最低位设置为1时表示该地址是一个多播地址, </span><span style="background-color: inherit; line-height: 1.5;">用十六进制可表示为01:00:00:00:00:00(以太网广播地址</span><span style="background-color: inherit; line-height: 1.5;">ff:ff:ff:ff:ff:ff可看作是以太网多播地址的特例)。</span></p>
  157. <p><span style="background-color: inherit; line-height: 1.5;">为了指明一个多播地址,任何一个以太网地址的首字节必须是01,这意味着与IP多播 </span><span style="background-color: inherit; line-height: 1.5;">相对应的以太网地址范围从01:00:5e:00:00:00到01:00:5e:7f:ff:ff。</span></p>
  158. </ul>
  159. <p>IGMP协议:用于支持主机和路由器进行多播的 <span style="background-color: inherit; line-height: 1.5;">Internet组管理协议(IGMP)。它让一个物理网络上的所</span><span style="background-color: inherit; line-height: 1.5;">有系统知道主机当前所在的多播组。多播路由器需要这</span><span style="background-color: inherit; line-height: 1.5;">些信息以便知道多播数据报应该向哪些接口转发。</span></p>
  160. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  161. <p><span style="background-color: inherit; line-height: 1.5;">路由器的IGMP查询被送到目的IP地址224.0.0.1。该地址被称为 </span><span style="background-color: inherit; line-height: 1.5;">所有主机组地址。它涉及在一个物理网络中的所有具备多播能力的主机和路由器。</span></p>
  162. <p><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></p>
  163. </ul>
  164. <p><span style="color:#ff0000;background-color: inherit;">帧的过滤过程:</span>为了弄清广播和多播,需要了解主机对由信道传送过 <span style="background-color: inherit; line-height: 1.5;">来帧的过滤过程。图12-1说明了这一过程。 </span></p>
  165. <ul style="background-color: inherit;" class=" list-paddingleft-2">
  166. <p>首先,网卡查看由信道传送过来的帧,确定是否接收<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;">一个复制。作为一个例子, tcpdump使用这种模式。 </span></p>
  167. <p><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;">最高字节的最低位设置为1时表示该地址是一个多播地址,</span><span style="background-color: inherit; line-height: 1.5;">用十六进制可表示为01:00:00:00:00:00(以太网广播地址</span><span style="background-color: inherit; line-height: 1.5;">ff:ff:ff:ff:ff:ff可看作是以太网多播地址的特例)。 </span></p>
  168. <p><span style="background-color: inherit; line-height: 1.5;">如果网卡收到一个帧,这个帧将被传送给设备驱动程序(如果帧检验和错,网卡将丢弃</span><span style="background-color: inherit; line-height: 1.5;">该帧)。设备驱动程序将进行另外的帧过滤。首先,帧类型中必须指定要使用的协议( IP、</span><span style="background-color: inherit; line-height: 1.5;">ARP等等)。其次,进行多播过滤来检测该主机是否属于多播地址说明的多播组。</span></p>
  169. <p><span style="background-color: inherit; line-height: 1.5;">设备驱动程序随后将数据帧传送给下一层,比如,当帧类型指定为IP数据报时,就传往</span><span style="background-color: inherit; line-height: 1.5;">IP层。IP根据IP地址中的源地址和目的地址进行更多的过滤检测。如果正常,就将数据报传送</span><span style="background-color: inherit; line-height: 1.5;">给下一层(如TCP或UDP)。</span></p>
  170. <p><span style="background-color: inherit; line-height: 1.5;">每次UDP收到由IP传送来的数据报,就根据目的端口号,有时还有源端口号进行数据报过滤。如果当前没有进程使用该目的端口号,就丢弃该数据报并产生一个ICMP不可达报文</span><span style="background-color: inherit; line-height: 1.5;">(TCP根据它的端口号作相似的过滤)。如果UDP数据报存在检验和错,将被丢弃。</span></p>
  171. </ul>
  172. <p>TFTP:TFTP是一个简单的协议,适合于只读存储器,仅用于无盘系统进行系统引导。它只使用 <span style="background-color: inherit; line-height: 1.5;">几种报文格式,是一种<span style="color:#ff0000;background-color: inherit;">停止等待协议</span>。<span style="line-height: 1.5;">数据发送方在发送下一个数据块之前需要<span style="background-color: inherit;"> </span></span><span style="line-height: 1.5;">等待接收方对已发送数据的确认。</span></span></p>
<p><br/></p>

- 阅读剩余部分 -