TCP创建连接和关闭连接

来源:互联网 发布:全景展示系统源码 编辑:程序博客网 时间:2024/05/16 13:46

让我们来看看调用connect的背后,TCP做了什么?

创建连接

     图示说明:行号用于引用。右箭头( --> )表示一个从TCP A到TCP B的分段。左箭头( <-- )与此相反。省略号(…)表示一个仍然在网络中的Segment
(延迟了)。“XXX”表示一个丢失或被丢弃的segment。圆括号中的是注释。TCP状态表示发送或收到一个segment之后的状态。每一行的中间部分
简略显示了段的内容,包括:系列号,控制标志,ACK域。出于简明考虑,省略了其它部分。

使用捎带数据段做同步是合法合理的(握手阶段捎带数据)。数据会被缓存起来直到连接到达ESTABLISHED状态。
最简单的三次握手如图 1 所示:

     

    TCP A                                                                                                                 TCP B
  1.  CLOSED                                                                                                               LISTEN
  2.  SYN-SENT         -->    <SEQ=100><CTL=SYN>                                         --> SYN-RECEIVED
  3.  ESTABLISHED <--     <SEQ=300><ACK=101><CTL=SYN,ACK>             <-- SYN-RECEIVED
  4.  ESTABLISHED -->     <SEQ=101><ACK=301><CTL=ACK>                      --> ESTABLISHED
  5.  ESTABLISHED -->     <SEQ=101><ACK=301><CTL=ACK><DATA>       --> ESTABLISHED
                                                                       图 1

图1的第2行,TCP A以发送一个SYN段开始,初始系列号是100。在第3行,TCP B发送了一个SYN,并确认它收到了A发送的SYN(ACK=101)。

第4行,TCP A回应一个对TCP B的SYN的确认。第5行,A发送了一些数据。注意,第4行和第5行的段的系列号是相同的,因为ACK不占用系列号空


同时打开(Simultaneous initiation)稍微有点复杂。如图2所示。每一个TCP都要经历从CLOSED 到 SYN-SENT 到 SYN-RECEIVED 
到 ESTABLISHED的过程。
PS:同时打开在TCP NAT穿透中用到。
     
     TCP A                                                                                                           TCP B
  1.  CLOSED                                                                                                  CLOSED
  2.  SYN-SENT           -->        <SEQ=100><CTL=SYN>                                   ...
  3.  SYN-RECEIVED <--        <SEQ=300><CTL=SYN>                               <-- SYN-SENT
  4.                                ...        <SEQ=100><CTL=SYN>                               --> SYN-RECEIVED
  5.  SYN-RECEIVED -->        <SEQ=100><ACK=301><CTL=SYN,ACK>    ...
  6.  ESTABLISHED   <--        <SEQ=300><ACK=101><CTL=SYN,ACK>   <-- SYN-RECEIVED
  7.                               ...        <SEQ=101><ACK=301><CTL=ACK>             --> ESTABLISHED
                                               Simultaneous Connection Synchronization
                                                                  图2

     三次握手的合理原则是能够避免旧的连接初始化副本(延迟一段时间后才到达主机,已经失效)引起的曲解。为了处理这种情况,发明了一个专用
   的控制消息,reset。处于非同步状态(例如:SYN-SENT, SYN-RECEIVED)的receiving TCP,收到reset后将返回到LISTEN状态。处于同步状
   态(ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT)的TCP,收到reset后将终止连接并通知用户。

   图3是一个从旧的SYN中恢复的例子。
      TCP A                                                                                                 TCP B
  1.  CLOSED                                                                                                LISTEN
  2.  SYN-SENT          -->      <SEQ=100><CTL=SYN>                                ...
  3.              (duplicate) ...       <SEQ=90><CTL=SYN>                                 --> SYN-RECEIVED
  4.  SYN-SENT         <--      <SEQ=300><ACK=91><CTL=SYN,ACK>     <-- SYN-RECEIVED
  5.  SYN-SENT         -->      <SEQ=91><CTL=RST>                                  --> LISTEN
  6.                             ...        <SEQ=100><CTL=SYN>                               --> SYN-RECEIVED
  7.  SYN-SENT        <--       <SEQ=400><ACK=101><CTL=SYN,ACK>    <-- SYN-RECEIVED
  8.  ESTABLISHED -->       <SEQ=101><ACK=401><CTL=ACK>            --> ESTABLISHED
                                          Recovery from Old Duplicate SYN
                                                              图 3

在第3行,一个旧的SYN到达TCP B,TCP B不知道这是一个旧的SYN(之前没有收到过SYN),因此它把这个SYN看做是一个正常的SYN并响应。
TCP A发现 ACK域是不正确的并返回一个RST,注意SEQ域要设置正确。TCP B收到RST之后,返回到LISTEN状态。

当“正确”的SYN最终到达时(第6行),同步过程按正常情况继续。如果第6行的SYN在RST之前到达,情况就更复杂了,两端都要发送RST


半开连接和其它异常

对于一个已建立的连接,如果其中一端关闭或终止了连接,但另一方还不知道,或者由于Crash导致内存丢失而使两端变得不同步,我们就说该连接
是“half-open”的。试图在这样的连接上发送数据将自动触发reset。然而,半开连接是不正常的,需要合适的恢复过程使其变得正常。

如果连接的A端不存在了,B端的用户试图发送数据,就会导致B收到一个reset控制消息。 这样的消息告诉B有哪里不对劲了,连接应该被终止。

 

假设两个用户进程A和B正在彼此通信,这时发生了一个crash,导致A的TCP内存丢失。对于这样的情况,一些操作系统会有错误恢复机制。 再次使用
TCP时,A可能重新开始也可能从某个点恢复。即,A可能尝试重新打开连接或者试图通过之前的连接(它相信该连接是打开的)发送数据。对于后一
种情况,将会收到一个来自本地TCP的错误消息“连接未打开”。 尝试创建连接时,ATCP将发送一个SYN段。这种情况如图4所示。TCP A奔溃后,
用户尝试重新连接。而TCP B,认为连接已经是打开的。

       TCP A                                                                                               TCP B
  1.  (CRASH)                                                                                             (send 300,receive 100)
  2.  CLOSED                                                                                              ESTABLISHED
  3.  SYN-SENT       -->         <SEQ=400><CTL=SYN>                          --> (??)
  4.  (!!)                   <--          <SEQ=300><ACK=100><CTL=ACK>     <-- ESTABLISHED
  5.  SYN-SENT       -->         <SEQ=100><CTL=RST>                          --> (Abort!!)
  6.  SYN-SENT                                                                                          CLOSED
  7.  SYN-SENT       -->          <SEQ=400><CTL=SYN>                        -->
                                              Half-Open Connection Discovery
                                                                图 4

3行,当SYN到来时,TCP B,正处于同步状态,而且收到的段不在接收窗口内,随即响应一个ACK指明它期望的下一个字节(ACK 100)。TCP A
到,这个段没有确认任何它发送的东西,就知道处于不同步状态了,也就是发现了一个“半开”连接,A就会发送一个RST。第5行,TCP B终止了连
接。TCP A将继续尝试建立连接。接下来问题简化为如图 1所示的简单的三次握手。
另一个有趣的例子是,TCP A 奔溃了,而TCP B尝试在它认为是同步的连接上发送数据。如图 5所示。在这种情况下,从 TCP B到达TCP A的数据,是
不能被接受的,因为连接不存在(没有进程在相应端口监听数据),所以TCP A会发送一个RSTTCP B收到RST(该RST要是合适的)之后就会终止连
接。


        TCP A                                                                                             TCP B
  1.  (CRASH)                                                                                      (send 300,receive 100)
  2.  (??)    <-- <SEQ=300><ACK=100><DATA=10><CTL=ACK>  <-- ESTABLISHED
  3.          --> <SEQ=100><CTL=RST>                                             --> (ABORT!!)
                    Active Side Causes Half-Open Connection Discovery
                                                        图 5

在图 6中,两个TCPs A和B处于被动连接状态,都在等待着SYN。一个旧的SYN副本(An old duplicate到达TCP B(第2行)。TCP返回一个
SYN-ACK(第3行),这导致TCP A产生一个RST(第3行的ACK是不被接受的,因为在A看来,它没有发送过SYN)。TCP B接受reset并返回到被动接
收状态。

      TCP A                                                                                             TCP B
  1.  LISTEN                                                                                              LISTEN
  2.                   ...         <SEQ=Z><CTL=SYN>                                  -->  SYN-RECEIVED
  3.  (??)          <--        <SEQ=X><ACK=Z+1><CTL=SYN,ACK>     <--  SYN-RECEIVED
  4.                  -->        <SEQ=Z+1><CTL=RST>                               -->  (return to LISTEN!)
  5.  LISTEN                                                                                              LISTEN
                      Old Duplicate SYN Initiates a Reset on two Passive Sockets
                                                        图 6

还有很多其它的可能情况,它们都遵从如下规则来产生和处理RST。

RST的产生

一个通用的规则是,只要收到一个显然不属于当前连接的段就发送一个RST。否则就不要发送。
比如,没有发送SYN,但是收到了SYN-ACK
可以分为如下三类:
  1. 如果连接不存在(CLOSED),收到任何非RST段都会回应一个RST。典型的,任何发送到不存在连接的SYNs都以这种方式来处理。比如:向某
    个主机上没有进程监听的端口发起连接,connect返回ECONNREFUSED。
  2. 如果连接处于任何非同步状态(LISTEN, SYS-SENT, SYN-RECEIVED),并且收到的段的确认在窗口外,或者安全层不匹配,就会发
    送一个RST.
  3. 如果连接处于同步状态(ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT),收到任何
    不可接受的段(SEQ在窗口外,或者ACK是是无效的),都要回应一个ACK段,其中包含当前的SEQ和一个指明下一个期望接收字节的
    ACK,连接状态保持不变。

RST的处理

在所有非SYN-SENT状态下,TCP首先通过检查SEQ域来验证所有的RST段的合法性。如果它的SEQ在窗口内,就是合法的。在SYN-SENT状
态下,如果ACK域确认了SYN,那么这个RST就是合法的。RST的接收者首先验证它(非法的RST都会忽略掉),然后再决定要不要改变状
态。如果接收者正处于LISTEN状态,就忽略RST。如果接收者处于SYN-RECEIVED状态,并且之前处于LISTEN状态,就返回到LISTEN
态,否则接收者终止连接并转换到CLOSED状态。如果接收者处于其它状态,收到RST后,就终止连接并建议用户转换到CLOSED状态。

关闭连接

基本上有3种情况导致连接关闭:
  1. 本地用户告诉TCP关闭连接
  2. 远程TCP通过发送FIN控制信号关闭
  3. 两端用户同时CLOSE
Case 1:本地用户主动关闭
在这种情况下,会构造一个FIN段并放到发送队列中。TCP不在接收任何SENDs,并且进入FIN-WAIT-1状态。这一状态允许RECEIVEs。之前发送的数
据段和FIN段都会被重传直到收到确认。当另一端的TCP已经确认了FIN并且发送了它自己的FIN,第一个TCP可以确认这个FIN。注意一个收到FIN的
TCP将会发送相应的ACK,但是不会发送它自己的FIN,除非用户也关闭了该连接。

Case 2:TCP从网络上接收到一个FIN
从网络上接收到FIN的TCP能够ACK它,并且告诉用户连接正在关闭。用户对此的反应是,调用close,于是TCP在发送完所有待发送的数据之后发送一
个FIN到连接的另一端。TCP一直等到它发送的FIN被确认,然后删除该连接。如果一直没有收到ACK,超时之后连接就会被终止并通知用户。

Case 3: 两边的用户同时关闭
同时关闭的情况下,连接双方交换FIN,并进行确认,当两边都收到ACK后,连接就被删除。
     
  TCP A                                                                                      TCP B
  1.  ESTABLISHED                                                                                               ESTABLISHED
  2.  (Close)
      FIN-WAIT-1  -->           <SEQ=100><ACK=300><CTL=FIN,ACK>          --> CLOSE-WAIT
  3.  FIN-WAIT-2  <--          <SEQ=300><ACK=101><CTL=ACK>                  <-- CLOSE-WAIT
  4.                                                       (Close)
      TIME-WAIT  <--           <SEQ=300><ACK=101><CTL=FIN,ACK>          <-- LAST-ACK
  5.  TIME-WAIT  -->          <SEQ=101><ACK=301><CTL=ACK>                   --> CLOSED
  6.  (2 MSL)
      CLOSED
                                                               正常关闭系列图
                                图 5

  TCP A                                                                                                                    TCP B
  1.  ESTABLISHED                                                                                          ESTABLISHED
  2.  (Close)                                                                                                            (Close)
      FIN-WAIT-1  -->        <SEQ=100><ACK=300><CTL=FIN,ACK>      ... FIN-WAIT-1
                              <--        <SEQ=300><ACK=100><CTL=FIN,ACK>      <--
                                ...        <SEQ=100><ACK=300><CTL=FIN,ACK>      -->
  3.  CLOSING      -->       <SEQ=101><ACK=301><CTL=ACK>              ... CLOSING
                              <--       <SEQ=301><ACK=101><CTL=ACK>              <--
                                ...       <SEQ=101><ACK=301><CTL=ACK>              -->
  4.  TIME-WAIT                                                                                          TIME-WAIT
      (2 MSL)                                                                                                     (2 MSL)
      CLOSED                                                                                                      CLOSED
                                                                同时打开系列图



原创粉丝点击