Java:吊打面试官!近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题( 五 )


如果是两次握手连接 , 就不能判断当前连接是否是历史连接 , 三次握手则可以在客户端(发送方)准备发送第三次报文时 , 客户端因有足够的上下文来判断当前连接是否是历史连接:

  • 如果是历史连接(序列号过期或超时) , 则第三次握手发送的报文是 RST 报文 , 以此中止历史连接;
  • 如果不是历史连接 , 则第三次发送的报文是 ACK 报文 , 通信双方就会成功建立连接;
所以 ,TCP 使用三次握手建立连接的最主要原因是防止历史连接初始化了连接 。
原因二:同步双方初始序列号
TCP 协议的通信双方 ,都必须维护一个「序列号」 ,序列号是可靠传输的一个关键因素 , 它的作用:
  • 接收方可以去除重复的数据;
  • 接收方可以根据数据包的序列号按序接收;
  • 可以标识发送出去的数据包中 ,哪些是已经被对方收到的;
可见 , 序列号在 TCP 连接中占据着非常重要的作用 , 所以当客户端发送携带「初始序列号」的 SYN 报文的时候 , 需要服务端回一个 ACK 应答报文 , 表示客户端的 SYN 报文已被服务端成功接收 , 那当服务端发送「初始序列号」给客户端的时候 , 依然也要得到客户端的应答回应 , 这样一来一回 , 才能确保双方的初始序列号能被可靠的同步 。
Java:吊打面试官!近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题
本文插图
四次握手与三次握手
四次握手其实也能够可靠的同步双方的初始化序号 , 但由于第二步和第三步可以优化成一步 , 所以就成了「三次握手」 。
而两次握手只保证了一方的初始序列号能被对方成功接收 , 没办法保证双方的初始序列号都能被确认接收 。
原因三:避免资源浪费
如果只有「两次握手」 , 当客户端的 SYN 请求连接在网络中阻塞 , 客户端没有接收到 ACK 报文 , 就会重新发送 SYN, 由于没有第三次握手 , 服务器不清楚客户端是否收到了自己发送的建立连接的 ACK 确认信号 , 所以每收到一个 SYN 就只能先主动建立一个连接 , 这会造成什么情况呢?
如果客户端的 SYN 阻塞了 , 重复发送多次 SYN 报文 , 那么服务器在收到请求后就会建立多个冗余的无效链接 , 造成不必要的资源浪费 。
Java:吊打面试官!近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题
本文插图
两次握手会造成资源浪费
即两次握手会造成消息滞留情况下 , 服务器重复接受无用的连接请求 SYN 报文 , 而造成重复分配资源 。
小结
TCP 建立连接时 , 通过三次握手能防止历史连接的建立 , 能减少双方不必要的资源开销 , 能帮助双方同步初始化序列号 。 序列号能够保证数据包不重复、不丢弃和按序传输 。
不使用「两次握手」和「四次握手」的原因:
  • 「两次握手」:无法防止历史连接的建立 , 会造成双方资源的浪费 , 也无法可靠的同步双方序列号;
  • 「四次握手」:三次握手就已经理论上最少可靠连接建立 , 所以不需要使用更多的通信次数 。
为什么客户端和服务端的初始序列号 ISN 是不相同的?
因为网络中的报文会延迟、会复制重发、也有可能丢失 , 这样会造成的不同连接之间产生互相影响 , 所以为了避免互相影响 , 客户端和服务端的初始序列号是随机且不同的 。
初始序列号 ISN 是如何随机产生的?
起始 ISN 是基于时钟的 , 每 4 毫秒 + 1 , 转一圈要 4.55 个小时 。
RFC1948 中提出了一个较好的初始化序列号 ISN 随机生成算法 。
ISN = M + F (localhost, localport, remotehost, remoteport)