【TCP四次挥手】

news2025/1/16 0:15:16

文章目录

    • TCP 四次挥手过程是怎样的?
    • 为什么挥手需要四次?
    • 第一次挥手丢失了,会发生什么?
    • 第二次挥手丢失了,会发生什么?
    • 第三次挥手丢失了,会发生什么?
    • 第四次挥手丢失了,会发生什么?
    • 为什么 TIME_WAIT 等待的时间是 2MSL?
    • 为什么需要 TIME_WAIT 状态?
    • TIME_WAIT 过多有什么危害?
    • 服务器出现大量 TIME_WAIT 状态的原因有哪些?
    • 服务器出现大量 CLOSE_WAIT 状态的原因有哪些?
    • 如果已经建立了连接,但是客户端突然出现故障了怎么办?
    • 如果已经建立了连接,但是服务端的进程崩溃会发生什么?

TCP 四次挥手过程是怎样的?

四次挥手流程

  • 第一次挥手:
    • 客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文,之后客户端进入 FIN_WAIT_1 状态。
  • 第二次挥手:
    • 服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSE_WAIT 状态。
    • 客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。
  • 第三次挥手:
    • 等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态。
  • 第四次挥手:
    • 客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入 TIME_WAIT 状态
    • 服务端收到了 ACK 应答报文后,就进入了 CLOSE 状态,至此服务端已经完成连接的关闭。
    • 客户端在经过 2MSL 一段时间后,自动进入 CLOSE 状态,至此客户端也完成连接的关闭。

注意是:主动关闭连接的,才有 TIME_WAIT 状态。
在这里插入图片描述

为什么挥手需要四次?

  • 关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
  • 服务端收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。
  • 在特定情况下,四次挥手是可以变成三次挥手的

第一次挥手丢失了,会发生什么?

  • 当客户端(主动关闭方)调用 close 函数后,就会向服务端发送 FIN 报文,试图与服务端断开连接,此时客户端的连接进入到 FIN_WAIT_1 状态。
  • 如果能及时收到服务端(被动关闭方)的 ACK,则会很快变为 FIN_WAIT2状态。

第一次挥手丢失?

  • 如果第一次挥手丢失了,那么客户端迟迟收不到被动方的 ACK 的话,也就会触发超时重传机制,重传 FIN 报文,重发次数由 tcp_orphan_retries 参数控制。
  • 当客户端重传 FIN 报文的次数超过 tcp_orphan_retries 后,就不再发送 FIN 报文,则会在等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到第二次挥手,那么直接进入到 close 状态。

第二次挥手丢失了,会发生什么?

  • 当服务端收到客户端的第一次挥手后,就会先回一个 ACK 确认报文,此时服务端的连接进入到 CLOSE_WAIT 状态。

第二次挥手丢失?

  • ACK 报文是不会重传的,所以如果服务端的第二次挥手丢失了,客户端就会触发超时重传机制,重传 FIN 报文,直到收到服务端的第二次挥手,或者达到最大的重传次数。

注意

  • 当客户端收到第二次挥手,也就是收到服务端发送的 ACK 报文后,客户端就会处于 FIN_WAIT2 状态,在这个状态需要等服务端发送第三次挥手,也就是服务端的 FIN 报文。
  • 对于 close 函数关闭的连接,由于无法再发送和接收数据,所以FIN_WAIT2 状态不可以持续太久
  • 对于调用 close 关闭的连接,如果在 60 秒后还没有收到 FIN 报文,客户端(主动关闭方)的连接就会直接关闭

第三次挥手丢失了,会发生什么?

  • 当服务端(被动关闭方)收到客户端(主动关闭方)的 FIN 报文后,内核会自动回复 ACK,同时连接处于 CLOSE_WAIT 状态,顾名思义,它表示等待应用进程调用 close 函数关闭连接。
  • 服务端处于 CLOSE_WAIT 状态时,必须由进程主动调用 close 函数,来触发服务端发送 FIN 报文,同时连接进入 LAST_ACK 状态,等待客户端返回 ACK 来确认连接关闭。
  • 迟迟收不到这个 ACK,服务端就会重发 FIN 报文
    客户端因为是通过 close 函数关闭连接的,处于 FIN_WAIT_2 状态是有时长限制的,如果 tcp_fin_timeout 时间内还是没能收到服务端的第三次挥手(FIN 报文),那么客户端就会断开连接。

第四次挥手丢失了,会发生什么?

  • 当客户端收到服务端的第三次挥手的 FIN 报文后,就会回 ACK 报文,也就是第四次挥手,此时客户端连接进入 TIME_WAIT 状态
  • 在 Linux 系统,TIME_WAIT 状态会持续 2MSL 后才会进入关闭状态。然后,服务端(被动关闭方)没有收到 ACK 报文前,还是处于 LAST_ACK 状态。如果第四次挥手的 ACK 报文没有到达服务端,服务端就会重发 FIN 报文
  • 客户端在收到第三次挥手后,就会进入 TIME_WAIT 状态,开启时长为 2MSL 的定时器,如果途中再次收到第三次挥手(FIN 报文)后,就会重置定时器,当等待 2MSL 时长后,客户端就会断开连接。

为什么 TIME_WAIT 等待的时间是 2MSL?

  • MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
  • 因为 TCP 报文基于是 IP 协议的,而 IP 头中有一个 TTL 字段,是 IP 数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。
  • MSL 与 TTL 的区别: MSL 的单位是时间,而 TTL 是经过路由跳数。
  • 2MSL时长 这其实是相当于至少允许报文丢失一次。
  • 当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间。
  • 2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。

为什么需要 TIME_WAIT 状态?

  • 主动发起关闭连接的一方,才会有 TIME-WAIT 状态。
  • 需要 TIME-WAIT 状态,主要是两个原因:
    • 防止历史连接中的数据,被后面相同四元组的连接错误的接收;
    • 保证「被动关闭连接」的一方,能被正确的关闭;
  • 序列号,是 TCP 一个头部字段,标识了 TCP 发送端到 TCP 接收端的数据流的一个字节,因为 TCP 是面向字节流的可靠协议,为了保证消息的顺序性和可靠性,TCP 为每个传输方向上的每个字节都赋予了一个编号,以便于传输成功后确认、丢失后重传以及在接收端保证不会乱序。序列号是一个 32 位的无符号数,因此在到达 4G 之后再循环回到 0。

  • 初始序列号,在 TCP 建立连接的时候,客户端和服务端都会各自生成一个初始序列号,它是基于时钟生成的一个随机数,来保证每个连接都拥有不同的初始序列号。初始化序列号可被视为一个 32 位的计数器,该计数器的数值每 4 微秒加 1,循环一次需要 4.55 小时。

原因一:防止历史连接中的数据,被后面相同四元组的连接错误的接收

  • 服务端在关闭连接之前发送的 SEQ = 301 报文,被网络延迟了。
  • 接着,服务端以相同的四元组重新打开了新连接,前面被延迟的 SEQ = 301 这时抵达了客户端,而且该数据报文的序列号刚好在客户端接收窗口内,因此客户端会正常接收这个数据报文,但是这个数据报文是上一个连接残留下来的,这样就产生数据错乱等严重的问题。
  • 为了防止历史连接中的数据,被后面相同四元组的连接错误的接收,因此 TCP 设计了 TIME_WAIT 状态,状态会持续 2MSL 时长,这个时间足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。

原因二:保证「被动关闭连接」的一方,能被正确的关闭

  • TIME-WAIT 作用是等待足够的时间以确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。
  • 假设客户端没有 TIME_WAIT 状态,而是在发完最后一次回 ACK 报文就直接进入 CLOSE 状态,如果该 ACK 报文丢失了,服务端则重传的 FIN 报文,而这时客户端已经进入到关闭状态了

TIME_WAIT 过多有什么危害?

  • 第一是占用系统资源,比如文件描述符、内存资源、CPU 资源、线程资源等;
  • 第二是占用端口资源,端口资源也是有限的,一般可以开启的端口为 32768~61000,也可以通过 net.ipv4.ip_local_port_range参数指定范围。
    • 如果客户端(主动发起关闭连接方)的 TIME_WAIT 状态过多,占满了所有端口资源,那么就无法对「目的 IP+ 目的 PORT」都一样的服务端发起连接了
    • 如果服务端(主动发起关闭连接方)的 TIME_WAIT 状态过多,并不会导致端口资源受限,但是 TCP 连接过多,会占用系统资源,比如文件描述符、内存资源、CPU 资源、线程资源等。

服务器出现大量 TIME_WAIT 状态的原因有哪些?

  • TIME_WAIT 状态是主动关闭连接方才会出现的状态,所以如果服务器出现大量的 TIME_WAIT 状态的 TCP 连接,就是说明服务器主动断开了很多 TCP 连接。
  • 什么场景下服务端会主动断开连接呢?
    • 第一个场景:HTTP 没有使用长连接
    • 第二个场景:HTTP 长连接超时
    • 第三个场景:HTTP 长连接的请求数量达到上限

第一个场景:HTTP 没有使用长连接

  • 在 HTTP/1.0 中默认是关闭的,如果浏览器要开启 Keep-Alive,它必须在请求的 header 中添加:Connection: Keep-Alive
  • 从 HTTP/1.1 开始, 就默认是开启了 Keep-Alive
  • 只要客户端和服务端任意一方的 HTTP header 中有 Connection:close 信息,那么就无法使用 HTTP 长连接的机制。
  • 当服务端出现大量的 TIME_WAIT 状态连接的时候,可以排查下是否客户端和服务端都开启了 HTTP Keep-Alive,因为任意一方没有开启 HTTP Keep-Alive,都会导致服务端在处理完一个 HTTP 请求后,就主动关闭连接,此时服务端上就
    会出现大量的 TIME_WAIT 状态的连接。

解决的方式也很简单,让客户端和服务端都开启 HTTP Keep-Alive 机制。

第二个场景:HTTP 长连接超时

  • HTTP 长连接可以在同一个 TCP 连接上接收和发送多个 HTTP 请求/应答,避免了连接建立和释放的开销。
  • 如果客户端完成一个 HTTP 请求后,就不再发起新的请求,此时这个 TCP 连接超过设置的时间,此时服务端上就会出现 TIME_WAIT 状态的连接。

第三个场景:HTTP 长连接的请求数量达到上限

  • Web 服务端通常会有个参数,来定义一条 HTTP 长连接上最大能处理的请求数量,当超过最大限制时,就会主动关闭连接。

服务器出现大量 CLOSE_WAIT 状态的原因有哪些?

  • CLOSE_WAIT 状态是「被动关闭方」才会有的状态,而且如果「被动关闭方」没有调用 close 函数关闭连接,那么就无法发出 FIN 报文,从而无法使得 CLOSE_WAIT 状态的连接转变为 LAST_ACK 状态。
  • 当服务端出现大量 CLOSE_WAIT 状态的连接的时候,说明服务端的程序没有调用 close 函数关闭连接。

一个普通的 TCP 服务端的流程:

  1. 创建服务端 socket,bind 绑定端口、listen 监听端口
  2. 将服务端 socket 注册到 epoll
  3. epoll_wait 等待连接到来,连接到来时,调用 accpet 获取已连接的 socket
  4. 将已连接的 socket 注册到 epoll
  5. epoll_wait 等待事件发生
  6. 对方连接关闭时,我方调用 close

大量CLOSE_WAIT原因

  • 第一个原因:第 2 步没有做,没有将服务端 socket 注册到 epoll,这样有新连接到来时,服务端没办法感知这个事件,也就无法获取到已连接的 socket,那服务端自然就没机会对 socket 调用 close 函数了。
  • 第二个原因: 第 3 步没有做,有新连接到来时没有调用 accpet 获取该连接的 socket,导致当有大量的客户端主动断开了连接,而服务端没机会对这些 socket 调用 close 函数,从而导致服务端出现大量 CLOSE_WAIT 状态的连接。
  • 第三个原因:第 4 步没有做,通过 accpet 获取已连接的 socket 后,没有将其注册到 epoll,导致后续收到 FIN 报文的时候,服务端没办法感知这个事件,那服务端就没机会调用 close 函数了。

如果已经建立了连接,但是客户端突然出现故障了怎么办?

  • 客户端如果发生了宕机,或者断电的场景。如果服务端一直不会发送数据给客户端,那么服务端是永远无法感知到客户端宕机这个事件的,也就是服务端的 TCP 连接将一直处于 ESTABLISH 状态,占用着系统资源。
  • 为了避免这种情况,TCP 有个保活机制
    • 定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。
  • TCP 保活的这个机制检测的时间是有点长,可以在应用层实现一个心跳机制。
    • web 服务软件一般都会提供keepalive_timeout参数,用来指定 HTTP 长连接的超时时间。如果设置了 HTTP 长连接的超时时间是 60 秒,web 服务软件就会启动一个定时器,如果客户端在完成一个 HTTP 请求后,在 60 秒内都没有再发起新的请求,定时器的时间一到,就会触发回调函数来释放该连接。

如果已经建立了连接,但是服务端的进程崩溃会发生什么?

  • TCP 的连接信息是由内核维护的,所以当服务端的进程崩溃后,内核需要回收该进程的所有 TCP 连接资源,于是内核会发送第一次挥手 FIN 报文,后续的挥手过程也都是在内核完成,并不需要进程的参与,所以即使服务端的进程退出了,还是能与客户端完成 TCP 四次挥手的过程。

文章参考:https://xiaolincoding.com/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/501416.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Lecture 13(Extra Material):Q-Learning

目录 Introduction of Q-Learning Tips of Q-Learning Double DQN Dueling DQN Prioritized Reply Multi-step Noisy Net Distributional Q-function Rainbow Q-Learning for Continuous Actions Introduction of Q-Learning Critic: The output values of a critic…

为生信写的Python简明教程 | 视频3

开源生信 Python教程 生信专用简明 Python 文字和视频教程 源码在:https://github.com/Tong-Chen/Bioinfo_course_python 目录 背景介绍 编程开篇为什么学习Python如何安装Python如何运行Python命令和脚本使用什么编辑器写Python脚本Python程序事例Python基本语法 数…

PySpark基础入门(7):Spark SQL

概述 SparkSQL和Hive的异同 Hive和Spark 均是:“分布式SQL计算引擎”SparkSQL使用内存计算,而Hive使用磁盘迭代,所以SparkSQL性能较好二者都可以运行在YARN之上SparkSQL无元数据管理,但可以和hive集成,集成之后可以借…

极光笔记 | 极光推出“运营增长”解决方案,开启企业增长新引擎

摘要: 移动互联网流量红利见底,营销获客面临更多挑战 随着移动互联网流量红利见顶,越来越多的企业客户发现获取新客户的难度直线上升,获客成本持续攀高。 传统的移动互联网营销以PUSH为代表,采用简单粗暴的方式给用户…

PaddleVideo 简介以及文件目录详解

简介特性许可证书 PaddleVideo 文件目录总述applications 文件夹详述configs 文件夹详述docs 文件夹详述paddlevideo 文件夹详述utils 文件夹tasks 文件夹loader 文件夹modeling 文件夹solver 文件夹metrics 文件夹 简介 PaddleVideo 旨在打造一套丰富、领先且实用的 Video 工…

【阿里云】秒懂云通信

目录 一、秒懂云通信-第一回听什么? 二、短信的使用场景 1. 短信的三种类型:短信通知、验证、会员营销 三、短信平台的选择 1、看成功率 2、看价格 3、看体验 四、秒懂云通信 五、如何使用 Step 1:业务入口 Step 2:注册账号 Step…

云安全技术——Snort安装与配置

目录 一、Snort简介 二、安装Centos7 Minimal系统 三、基本环境配置 四、安装Snort 五、下载规则 六、配置Snort 七、测试Snort 一、Snort简介 Snort是一个开源的网络入侵检测系统,主要用于监控网络数据包并检测可能的攻击行为。它可以实时分析网络流量&…

HJ37 统计每个月兔子的总数

HJ37 统计每个月兔子的总数 描述示例解题思路以及代码分析解法1解法2 描述 描述 有一种兔子,从出生后第3个月起每个月都生一只兔子,小兔子长到第三个月后每个月又生一只兔子。 例子:假设一只兔子第3个月出生,那么它第5个月开始会…

ASEMI代理ADUM3211TRZ-RL7原装ADI车规级ADUM3211TRZ-RL7

编辑:ll ASEMI代理ADUM3211TRZ-RL7原装ADI车规级ADUM3211TRZ-RL7 型号:ADUM3211TRZ-RL7 品牌:ADI/亚德诺 封装:SOIC-8 批号:2023 引脚数量:8 工作温度:-40C~125C 安装类型:表…

操作系统原理 —— 操作系统什么时候会发生进程的调度(十二)

操作系统什么时候需要进程调度? 进程调度的层次中,有一个低级调度,就是按照某种算法从就绪队列中选择一个进程为其分配 CPU。 那操作系统会在什么时候触发进程调度呢? 在这里一共可以分为两大类: 当前运行的进程主动…

04-微服务部署2023系列-centos安装gitlab

目的:为了将来的devops快速部署搭建自己的代码库,保证速度和私密性 前面01-03小节: 完成基本的服务器环境 centos_nginx_java_docker; 这个基础环境是将来集群中每台服务器的基本, 可以先打一个镜像备份。 阿里云的镜像备份比较简单。以后搭建新服务器时,以这个为基础,安…

JUC并发包详解AQS同步队列

一、AQS介绍 在JUC并发包中,AQS为其最关键的作用,全称为abstractQueuedSynchroinzed同步器,为信号量semaphore、同步锁的基础抽象类。 其中内部主要有二大块 state 共享资源,通过并发操作state修改改值为1,如果修改成…

《Linux 内核设计与实现》09. 内核同步介绍

共享资源之所以要防止并发访问,是因为如果多个执行线程同时访问和操作数据,就有可能发生各线程之间相互覆盖共享数据的情况,从而造成被访问的数据不一致状态。 临界区和竞争条件 临界区:访问和操作共享数据的代码段。原子操作&a…

键控流水灯

项目文件 文件 关于项目的内容知识点可以见专栏单片机原理及应用 的第四章 IO口编写 在电路图的基础上,编写可键控的流水灯程序。要求实现的功能为,K1是总开关,当K1首次按下时,流水灯由下往上流动;当K2按下时停止流动,且全部灯灭…

ASK,FSK和PSK

一、ASK,FSK和PSK 数字信号只有有限个离散值,使用数字信号对载波进行调制的方式称为键控(Keying),分为幅度键控(ASK)、频移键控(FSK)和相移键控(PSK)。 幅度键控可以通过乘法器和开关电路来实现,在数字信…

SpringBoot【开发实用篇】---- 配置高级

SpringBoot【开发实用篇】---- 配置高级 1. ConfigurationProperties2. 宽松绑定/松散绑定3. 常用计量单位绑定4. 校验5. 数据类型转换 进入开发实用篇第二章内容,配置高级,其实配置在基础篇讲了一部分,在运维实用篇讲了一部分,这…

离线安装Percona

前言 安装还是比较简单,这边简单进行记录一下。 版本差异 一、离线安装Percona 下载percona官网 去下载你需要对应的版本 jemalloc-3.6.0-1.el7.x86_64.rpm 需要单独下载 安装Percona 进入RPM安装文件目录,执行下面的脚本 yum localinstall *.rpm修改…

C语言CRC-16 X25格式校验函数

C语言CRC-16 X25格式校验函数 CRC-16校验产生2个字节长度的数据校验码,通过计算得到的校验码和获得的校验码比较,用于验证获得的数据的正确性。基本的CRC-16校验算法实现,参考: C语言标准CRC-16校验函数。 不同应用规范通过对输…

聊聊Doris向量化执行引擎-过滤操作

聊聊Doris向量化执行引擎-过滤操作 Doris是开源的新一代极速MPP数据库,和StarRocks同源,采用全面向量化技术,充分利用CPU单核资源,将单核执行性能做到极致。本文,我们聊聊过滤操作是如何利用SIMD指令进行向量化操作。 …

PCB设计流程步骤中的注意事项

PCB中文名称为印制电路板,又称印刷线路板,几乎所有电子设备中都会应用到PCB。这种由贵金属制成的绿色电路板连接了设备的所有电气组件,并使其能够正常运行。PCB原理图是一个计划,是一个蓝图。它说明的并不是组件将专门放置在何处&…