zoukankan      html  css  js  c++  java
  • TCP为什么需要三次握手而不是两次?

    面试的时候遇到一个问题:TCP为什么需要三次握手而不是两次?

    网上对此问题的回答不尽相同,主要有两种声音:

    1. 因为TCP需要确认通信双方的序列号,所以,A要对B的序列号确认;

    2. 防止已经失效的连接请求报文段突然又传到服务端,因而产生错误。

    第一条的讨论可以参见 StackExchange

    第二条主要来自于谢希仁老师的《计算机网络》,第7版在P239。
    书中举了一个例子:A发出的第一个连接请求报文段没有丢失,而是在网络中滞留,以致延迟到连接释放以后的某个时间才到达B,B接收到这个失效的报文段之后,就认为是新的连接请求,于是接受连接请求,分配资源,但是后续A并没有数据发送过来,这样B的资源就白白浪费了。于是,三次握手可以避免这种情况。

    面试的时候,我答的就是第二条,面试官反驳说,服务器都会有超时机制,资源的浪费还会被回收,这个不是主要原因。

    那什么是主要原因呢?下面说一下我的分析(最后补充面试官的答案)。

    首先我依然认为第二条的说法是正确的,因为第二条就是来自 RFC793,原话是这样的:

    The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.

    翻译过来就是第二条:防止失效的重复的连接请求造成困扰。

    但是,RFC中所表述的重点在:通过三次握手,通信双方能够正确交换序列号并正常通信,而不是造成资源浪费。(有点第一条的味道)。

    RFC中举了下面的例子来说明该情况,这也是区别于第一条的本质所在。

    
          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行,B接收到了A已经失效的序列号为90的报文段,认为是新的连接请求,于是状态由LISTEN变成了SYN-RECEIVED;

    在第4行,A接收到了B对于失效的连接请求的确认,但是注意,在A这边,91这个序列号已经失效了,使用91是无法与B进行通信的,而B的状态在第3行中不是LISTEN了,怎么再次建立连接呢?

    在第5行A向B发送了控制位RST(reset)为1的报文段,将连接状态重置,所以B的状态回到了LISTEN;

    接着在第6行B接收到序列号为100的连接请求,于是接下来就可以正常进行三次握手。

    可以看到,第三次握手不一定会发送正确的ACK报文段,而是有可能重置B的连接状态的。假如没有第三次握手,在有失效连接报文段的情况下,A认为B的确认无效,无法使用91序列号,B却已经建立连接,通信就无法进行下去,所以必须要有第三次握手。

    PS:面试官的解释:

    A向B第一次发送的报文段可能在网络中滞留,而后A可能发送了一个序列号也一样的报文段,但是第一个报文段先到达B,于是建立连接并对A确认,A也认为连接建立了,但是后续通信的内容可能不是第一个TCP请求计划的那样,双方在数据格式以及加密解密上肯能存在不一致的情况,通信会发生错误。

    当时听的时候就感觉有问题(不过当时被唬住了),后来想一想,他的说法完全没有道理,相同的连接请求,完全无法区分,就可以认为是同一个,之后的数据通信是按照最新的连接来的,一定不会产生通信的错误。

    -------------------------------------
    吾生也有涯,而知也无涯。
  • 相关阅读:
    LeetCode 139. Word Break
    Amazon behavior question
    学习笔记之100 TOP Ikm C++ Online Test Questions
    学习笔记之IKM C++ 11
    学习笔记之C/C++指针使用常见的坑
    LeetCode 208. Implement Trie (Prefix Tree)
    队列 & 栈//岛屿的个数
    队列 & 栈//设计循环队列
    队列 & 栈//设计循环队列
    查找表类算法//存在重复元素 III
  • 原文地址:https://www.cnblogs.com/SanjiApollo/p/12724990.html
Copyright © 2011-2022 走看看