zoukankan      html  css  js  c++  java
  • TCP 中的三次握手和四次挥手

    Table of Contents

    1. 前言
    2. 数据报头部
    3. 三次握手
      1. SYN 攻击
    4. 四次挥手
      1. 半连接
      2. TIME_WAIT
    5. 结语
    6. 参考链接

    前言

    TCP 中的三次握手和四次挥手应该是非常著名的两个问题了,一方面这两个过程基本上属于面试必考题目,另一方面,这两个过程在实际的使用中也非常重要。

    这里就来简单的看一下这两个过程是怎么一回事吧。

    数据报头部

    在学习三次握手和四次挥手的具体过程之前,我觉得有必要先对 TCP/IP 的数据报头部进行一定的了解,当然,不需要了解所有信息。

    上面的图片是 IP 数据报的头部结构,在这里,我们只需要明白:IP 数据报的头部会携带 源地址目的地址 的信息就足够了。

    然后就是 TCP 数据报的头部结构:

    TCP 头部的结构和 IP 头部一样,都比较复杂,但是在这里,我们也只需要关注其中的部分信息:

    TCP 头部 作用或含义
    源端口目的端口 和 IP 头部的 源地址目的地址 一起唯一地标识了每个连接
    序列号 用来标识发送的字节流,即:发送的每个字节都是进行了编号的
    确认号 确认发送方希望接下来接收到的数据报的序列号,即:确认收到了对方发送的前一个数据报
    SYN 标志 建立新连接时该字段启用,表明本次发送的 序列号 为自身的 初始序列号
    ACK 标志 表明 确认号 字段有效,连接建立以后一般都处于启用状态
    FIN 标志 表明该报文的发送方已经结束向对方发送数据

    三次握手

    在对 TCP/IP 数据报的头部结构有了一定的了解后,就可以进入正题了,首先是三次握手的过程:

    三次握手过程的文字描述:

    1. 第一次握手,客户端向服务端发送数据报,该数据报的 SYN 标志为 1,而序列号的值为 x
    2. 第二次握手,服务端向客户端发送数据报,该数据报的 SYN 标志和 ACK 标志为 1,而序列号的值为 y,确认号的值为 x + 1
    3. 第三次握手,客户端向服务端发送数据报,该数据报的 ACK 表示为 1,而序列号的值为 x + 1,确认号的值为 y + 1

    这个过程其实不难,但是,更重要的是对这个过程的理解,或者说,就是需要明白:为什么要进行三次握手?

    这个问题存在很多种解释,个人感觉最好的一个解释应该是 知乎 上的一个回答,这个回答从三次握手的目的出发对为什么需要三次握手进行了解释:

    1. 三次握手的目的除了让通信双方了解一个连接正在建立以外,还在于利用数据报的头部交换彼此的 初始序列号
    2. 当 SYN 标志位 1 时,会表明当前数据报头部的序列号就是 初始序列号
    3. 第一次握手时,客户端将自身的 初始序列号 发送给了服务端
    4. 第二次握手时,服务端通过确认号确认了客户端的 初始序列号
    5. 第二次握手时,服务端将自身的 初始序列号 发送给了客户端
    6. 第三次握手时,客户端通过确认号确认了服务端的 初始序列号

    也就是说,三次握手的过程可以简化为客户端和服务端交换彼此的 初始序列号 的过程,每次交换需要:

    1. 发送 初始序列号 给另一方
    2. 接受到另一方的 确认号 表明 初始序列号 已经成功交给另一方

    在这个过程中,由于单个数据报可以同时携带确认号、初始序列号,因此,将下面四个过程压缩成了三次握手:

    1. 客户端 -> 服务端:我的初始序列号为 X
    2. 服务端 -> 客户端:确认你的初始序列号为 X
    3. 服务端 -> 客户端:我的初始序列号为 Y
    4. 客户端 -> 服务端:确认你的初始序列号为 Y

    可以看到,这个过程必然是需要三次握手的,少一次显得不够,多一次显得多余。

    当然了,还有其他的一些解释,有兴趣的可以看一下参考链接中的文章。

    SYN 攻击

    SYN 攻击是针对三次握手过程的一种攻击方式,通过观察三次握手的过程可以发现,当服务端向客户端发送 SYN-ACK 后还未进入完整的连接状态,而是处于 半连接 状态。只有在接收到客户端的 ACK 后才会转入完整的连接状态。

    而 SYN 攻击便是通过短时间内伪造大量不存在的 IP 地址,向服务器不断地发送 SYN 包实现的。这使得服务端存在大量未确认的半连接,这些半连接只有等待服务端不断的重发直至超时才会断开。

    这些伪造的 SYN 包将长时间占用未连接队列,正常的 SYN 请求被丢弃,导致目标系统运行缓慢,严重者会引起网络堵塞甚至系统瘫痪。

    四次挥手

    四次挥手似乎没有三次握手那么有名,但也还是十分重要的一个过程,其具体过程如下:

    四次挥手过程的文字描述:

    1. 第一次挥手,主动关闭者 A 向被动关闭者 B 发送 FIN 标志为 1 的数据报,并指明希望接收者看到的自己当前的序列号 u
    2. 第二次挥手,被动关闭者 B 将 u 值加一作为响应的确认号值,表明它已经成功接收到主动关闭者发送的 FIN
    3. 第三次挥手,被动关闭者 B 将身份转变为主动关闭者,并发送自己的 FIN,并指明希望接收者看到的自己当前的序列号 w
    4. 第四次挥手,A 将 w 值加一作为响应的确认号值,表明它已经成功接收到 B 发送的 FIN

    和三次握手一样,我们需要的是对四次挥手过程的理解,这里就附上个人的理解好了:

    1. 四次挥手的过程其实就是关闭连接的过程
    2. 关闭连接的过程中,主动关闭者和被动关闭者需要停止各自的 发送接收 操作
    3. 任何一端只能主动关闭自身的 发送 操作
    4. 任何一端只能在确定对方已经停止 发送 操作以后才能停止相应的 接收 操作

    也就是说,四次挥手的过程我们可以看成是客户端和服务端停止自身的 发送 操作并 通知 另一端的过程:

    1. 第一次挥手,主动关闭者通过发送带有 FIN 标志的数据报告诉被动关闭者:我的数据已经发送完了,你可以停止接受操作了
    2. 第二次挥手,被动关闭者通过发送带有相应确认号的数据报告诉主动关闭者:好的,你的通知我已受到,你可以停止发送操作了
    3. 第三次和第四次操作正好相反,原本的被动关闭者变为主动关闭者,关闭自身的 发送 操作并通知另一端

    由于任何一端停止自身的 发送 操作并 通知 另一端都需要两次挥手的过程,因此,总的来说就需要四次挥手了。

    半连接

    通过对四次挥手过程的理解我们可以发现,连接的关闭过程是由两端分别停止自身的数据 发送 操作完成的,因此,假如一方停止发送操作,而另一方继续发送数据,这时便进入了半连接状态。

    TIME_WAIT

    TIME_WAIT 这个状态也是比较常见的一个问题了,第四次挥手后进行第四次挥手的一方会进入 TIME_WAIT 状态,要至少等待 2MSL 才关闭连接。

    这是为了避免另一端没有收到自己的 ACK 又进行了 FIN 的重发,如果自己直接就把连接关了,那么就收不到这个 FIN 数据报了。这样一来,另一端就会长时间处在 LAST_ACK的状态。

    虽然 TIME_WAIT 这个状态是出于好意,但有些时候还是为造成一些问题,特别是在 Web 服务器这种需要主动关闭连接的服务端。

    2MSL 的时间长度默认情况下并不短,通常情况下可能有 30~300 秒,这意味着在这个时间段类相应的 端口 资源是一直被占据的,这对相当依赖有限的端口资源的服务器来说是难以接受的。

    因此,可以考虑通过将 2MSL 调低来解决这样问题。

    结语

    说起来,学习计算机网络基础的时候,并没有怎么学习关于三次握手和四次挥手的内容,基本上都是简单的了解了一下就完事了。

    直到面试遇到了这个问题 @_@

    然后才发现,这里面的弯弯道道也还不少,而且,似乎离我们并不是那么远,也许,实际操作中的一些问题就是由这两个过程导致的。

    所以说,这两个过程能称为面试问题中的常客也不是没有道理的,是真的很重要。

    注:三次握手和四次挥手中还有一个比较重要的内容是状态的转换,这里基本上没有提及这方面的内容,有需要或有兴趣的可以查阅相关的资料。

    参考链接

  • 相关阅读:
    Transform XML using XSLT
    HyperV Remote Management Configuration utility
    UI testing via Reflection in .NET
    Auto Test: Test Case Structure
    UI Testing via windows API
    风讯CMS常见问题锦集
    深入了解ASP.NET运行内幕 .txt
    公布一个简单的日志记录方法 【转】要研究
    服务器asp.net权限设置问题及解决方法时间:
    C#如何去掉字符串中所有空格
  • 原文地址:https://www.cnblogs.com/rgbit/p/10991100.html
Copyright © 2011-2022 走看看