⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcp协议规范.htm

📁 internet协议集
💻 HTM
📖 第 1 页 / 共 5 页
字号:
      <P align=justify>SEG.SEQ = 一个数据段的第一个序列号;</P>
      <P align=justify>SEG.LEN = 数据段中包括的字节数;</P>
      <P align=justify>SEG.SEQ+SEG.LEN-1 = 数据段的最后一个序列号。</P>
      <P align=justify>请注意下面的关系:</P>
      <P align=justify>SND.UNA &lt; SEG.ACK =&lt; SND.NXT</P>
      <P align=justify>如果一个数据段的序列号小于等于确认号的值,那么整个数据段就被确认了。而在接收数据时下面的比较操作是必须的:</P>
      <P align=justify>RCV.NXT = 期待的序列号和接收窗口的最低沿;</P>
      <P align=justify>RCV.NXT+RCV.WND-1 = 最后一个序列号和接收窗口的最高沿; </P>
      <P align=justify>SEG.SEQ = 接收到的第一个序列号;</P>
      <P align=justify>SEG.SEQ+SEG.LEN-1 = 接收到的最后一个序列号;</P>
      <P align=justify> </P>
      <P align=justify>上面几个量有如下关系:</P>
      <P align=justify>RCV.NXT =&lt; SEG.SEQ &lt; RCV.NXT+RCV.WND 或 RCV.NXT 
      =&lt; SEG.SEQ+SEG.LEN-1 &lt; RCV.NXT+RCV.WND</P>
      <P 
      align=justify>测试的第一部分是检查数据段的开始部分是否在接收窗口中,第二部分是检查数据段的结束部分是否也在接收窗口内;上面两个检查通过任何一个就说明它包括窗口要求的数据。实际中的情况会更复杂一些,因为有零窗口和零数据段长,因此我们有下面四种情况: 
      </P></FONT>
      <TABLE cellSpacing=1 cellPadding=7 width="100%" border=1>
        <TBODY>
        <TR>
          <TD vAlign=top width="13%"><FONT face=宋体 size=3>
            <P align=justify>段长度</FONT></P></TD>
          <TD vAlign=top width="16%">
            <BLOCKQUOTE><FONT face=宋体 size=3>
              <P align=justify>接收窗口</FONT></P></BLOCKQUOTE></TD>
          <TD vAlign=top width="71%"><FONT face=宋体 size=3>
            <P align=justify>测试</FONT></P></TD></TR>
        <TR>
          <TD vAlign=top width="13%"><FONT face=宋体 size=3>
            <P align=justify>0</FONT></P></TD>
          <TD vAlign=top width="16%">
            <BLOCKQUOTE><FONT face=宋体 size=3>
              <P align=justify>0</FONT></P></BLOCKQUOTE></TD>
          <TD vAlign=top width="71%"><FONT face=宋体 size=3>
            <P align=justify>SEG.SEQ = RCV.NXT</FONT></P></TD></TR>
        <TR>
          <TD vAlign=top width="13%"><FONT face=宋体 size=3>
            <P align=justify>0</FONT></P></TD>
          <TD vAlign=top width="16%">
            <BLOCKQUOTE><FONT face=宋体 size=3>
              <P align=justify>&gt;0</FONT></P></BLOCKQUOTE></TD>
          <TD vAlign=top width="71%"><FONT face=宋体 size=3>
            <P align=justify>RCV.NXT =&lt; SEG.SEQ &lt; 
            RCV.NXT+RCV.WND</FONT></P></TD></TR>
        <TR>
          <TD vAlign=top width="13%"><FONT face=宋体 size=3>
            <P align=justify>&gt;0</FONT></P></TD>
          <TD vAlign=top width="16%">
            <BLOCKQUOTE><FONT face=宋体 size=3>
              <P align=justify>0</FONT></P></BLOCKQUOTE></TD>
          <TD vAlign=top width="71%"><FONT face=宋体 size=3>
            <P align=justify>不可接受</FONT></P></TD></TR>
        <TR>
          <TD vAlign=top width="13%"><FONT face=宋体 size=3>
            <P align=justify>&gt;0</FONT></P></TD>
          <TD vAlign=top width="16%">
            <BLOCKQUOTE><FONT face=宋体 size=3>
              <P align=justify>&gt;0</FONT></P></BLOCKQUOTE></TD>
          <TD vAlign=top width="71%"><FONT face=宋体 size=3>
            <P align=justify>RCV.NXT =&lt; SEG.SEQ &lt; RCV.NXT+RCV.WND或RCV.NXT 
            =&lt; SEG.SEQ+SEG.LEN-1 &lt; 
      RCV.NXT+RCV.WND</FONT></P></TD></TR></TBODY></TABLE><FONT face=宋体 size=3>
      <P 
      align=justify>请注意接收窗口的大小可以为零,在窗口为零时它只用来接收ACK信息,因此对于一个TCP来说,它可以使用零大小窗口在发送数据的同时接收数据。即使接收窗口的大小为零,TCP必须处理所有接收到信息的RST和URG域。</P>
      <P 
      align=justify>我们也应用计数的方式保护了一些特定的控制信息,这是通过隐式地使用一些控制标记使数据段能够可靠地重新发送(或确认)为达到的。控制信息并不在段数据空间中传送,因此,我们必须采用隐式指定序列号进行控制。SYN和FIN是需要保护的控制量,这两个控制量也只在连接打开和关闭时使用。SYN被认为是在第一个实际数据之间的数据,而FIN是最后一个实际数据之后的数据。段长度(SEG.LEN)包括数据和序列号空间,如果出现了SYN,那么SEG.SEQ是SYN的序列号。</P>
      <P align=justify>初始序列号选择</P>
      <P 
      align=justify>协议对于特定连接被重复使用没有什么限制。连接是由一对套接字定义的。新的连接实例被定义为连接的另一次恢复,这就带来了问题:TCP如果确定多个数据段是从以前连接的另一次恢复中取得的呢?这个问题在连接迅速打开和关闭,或因为内存原因被关闭然后又迅速建立后显示特别突出。</P>
      <P 
      align=justify>为了避免混乱,用户必须避免因此恢复使用某一连接,而使序列号发生混乱。我们必须保证序列号的正确性,即使TCP失败,根本不知道以前的序列号是什么的情况下也要保证序列号的正确性。当新的连接被创建时,产生一个新的初始序列号(ISN)产生子,它用来选择一个新的32位ISN。产生子和32位时钟的低度位字节相关,低位字节的刷新频率大概是4微秒,因此ISN的循环时间大概是4.55小时。因此我们把网络包的最长生存时间(MSL)小于4.55小时,因此我们可以认为ISN是唯一的。对于每个连接都有发送序列号和接收序列号,初始发送序列号(ISS)由发送TCP选择,而初始接收序列号是在连接建立过程中产生的。</P>
      <P 
      align=justify>对于将要连接或初始化的连接,两个TCP必须和对方的初始序列号同步。这通过交换一个控制位SYN和初始序列号完成。我们把带有SYN的数据段称为"SYNs"。同步的获得过程这里就不重复了,每方必须发送自己的序列号并返回对对方序列号的确认。</P>
      <P align=justify>1) A --&gt; B SYN 本方序列号是X </P>
      <P align=justify>2) A &lt;-- B ACK 本方序列号被确认</P>
      <P align=justify>3) A &lt;-- B SYN 对方序列号是Y </P>
      <P align=justify>4) A --&gt; B ACK 确认对方序列号</P>
      <P 
      align=justify>上面的第2步和第3步可以合并,这时可以成为3阶段,所以我们可以称它为三消息握手。这个过程是必须的,因为序列号不和全局时钟关联,TCP也可以有不同的机制选择ISN。接收到第一个SYN的接收方不可能知道这个数据段是不是被延时,除非它记住了在连接上使用的最近的序列号(这通常是不可能的),因此它必须要求发送者确认。</P>
      <P 
      align=justify>为了保证TCP获得的确认是刚才发送的段产生的,而不是仍然在网络中的老数据段产生的,因此TCP必须在MSL时间之内保持沉默。在本文中,我们假设MSL=2小时,这是出于工程的需要,如果用户觉得可以,他可以改变MSL。请注意如果TCP重新初始化,而内存中的序列号正在使用,不需要等待,但必须确认使用的序列号比当前使用的要大。</P>
      <P 
      align=justify>如果一台主机在未保留任何序列号的情况下失败,那么它应该在MSL时间之内不发出任何数据段。下面将会这一情况进行说明。TCP的实现可以不遵守这个规定,但是这会造成老数据被当成新数据接收,而新数据被当成老数据拒绝的情况。</P>
      <P 
      align=justify>每当数据段形成并进入输出队列,TCP会为它指定序列空间中的一个值。TCP中多复本检测和序列算法都依赖于这个地址空间,在对方发送或接收之前不会超过2的32次方个包存在于输出队列中。所有多余的数据段都会被删除。如果没有这个规定,会出现多个数据段被指定同一个序列号的情况,会造成混乱。数据段中序列号的多少和数据段中的字节数一样多。</P>
      <P 
      align=justify>在通常情况下,TCP保留下一个要发送的序列号和还未确认的最老的序列号,不要在没有确认的时候就再次使用,这样会有些风险,也正是因为这样的目的,所以序列空间很大。对于2M的网络,要4.5小时来耗尽序列空间,因为一个数据段可能的最大生存时间也不过十几分之一秒,这就留下了足够的空间;而在100M的网络上需要5.4分钟,虽然少了点,但也可以了。</P>
      <P 
      align=justify>如果在实现TCP时没有为保存序列号留下空间,那清除多余的包可能就不能实现了,因此推荐这种类型的TCP实现最好在失败后等待MSL时间,这样保证多余的包被删除。这种情况有时候也可能会出现在保留序列号的TCP实现中。如果TCP在选择一个另一个TCP连接正在使用的序列号时,这台主机突然失败了,这就产生了问题。这个问题的实质在于主机不知道它失败了多久,也不知道多余的复本是不是还在网络中。</P>
      <P 
      align=justify>处理这种问题的方法是等待MSL时间,如果不这样就要冒着对方错误接收数据的危险,要等待的时间也就称为“沉默时间”。实现者可以让用户选择是不是等待,但是无论用户如何也不见得非要等待MSL时间。</P>
      <P align=justify>3.4. 建立一个连接</P>
      <P 
      align=justify>建立连接应用的是三消息握手。如果双方同时都发送SYN也没有关系,双方会发现这个SYN中没有确认,于是就知道了这种情况,通常来说,应该发送一个"reset"段来解决这种情况。三消息握手减少了连接失败的可能性。下面就是一个例子,在尖括号是的就是数据段中的内容和标记。其它的就不多说了。</P>
      <P align=center><IMG height=132 alt=基本三消息同步 
      src="TCP协议规范.files/TCPDetail-7.gif" width=506></P>
      <P align=justify>在第2行,TCP A发送SYN初始化序列号,表示它要使用序列号100;第3行中,TCP 
      B给出确认,并且期待着A的带有序列号101的数据段;第4行,TCP 
      A给出确认,而在第5行,它也给出确认,并发送了一些数据,注意第4行的序列号与第5号的一样,因为ACK信息不占用序列号空间内的序列号。同时产生请求的情况如下图所示,只复杂一点。</P>
      <P align=center><IMG height=163 alt=同时连接同步 
      src="TCP协议规范.files/TCPDetail-8.gif" width=507></P>
      <P 
      align=justify>使用三消息握手的主要原因是为了防止使用过期的数据段。为了这个目的,必须引入新的控制消息,RESET。如果接收TCP处理非同步状态,在接收到RESET后返回到LISTEN状态。如果TCP处理下面几种状态ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT时,放弃连接并通过用户。我们下面就详细说明后一种情况。</P>
      <P align=center><IMG height=182 alt=过期SYN的恢复 
      src="TCP协议规范.files/TCPDetail-9.gif" width=507></P>
      <P 
      align=justify>通过上面的例子,我们可以看出TCP连接是如何从过期数据段的干扰下恢复的。请注意第4行和第5行中的RST(RESET信号)。</P>
      <P align=justify>半开连接和其它非正常状态</P>
      <P 
      align=justify>如果一方在未通过另一方的情况下关闭连接,或双方虽然失败而不同步的情况我们称为半开连接状态。在一方试图发送数据时连接会自动RESET。然而这种情况毕竟属于不正常情况。应该做出相应的处理。如果A处的连接已经关闭,B处并不知道。当B希望发送数据到A时,就会收到RESET信号,表示这个TCP连接有误,要中止当前连接。</P>
      <P 
      align=justify>假设A和B两个进程相互通信的时候A的TCP发生了失败,A依靠操作系统支持TCP的存在,通常这种情况下会有恢复机制起作用,当TCP重新恢复的时候,A可能希望从恢复点开始工作。这样A可能会试图OPEN连接,然后在这个它认为还是打开的连接上传送数据,这时A会从本地(也就是A的)TCP上获得错误消息“未打开连接”。A的TCP将发送包括SYN的数据段。下面的例子将显示这一过程:</P>
      <P align=center><IMG height=168 alt=关开连接检测 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -