速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

为什么 TCP 协议有 TIME_WAIT 状态

  • 2020-03-20
  • 本文字数:3661 字

    阅读完需:约 12 分钟

为什么 TCP 协议有 TIME_WAIT 状态

为什么这么设计(Why’s THE Design)是一系列关于计算机领域中程序设计决策的文章,我们在这个系列的每一篇文章中都会提出一个具体的问题并从不同的角度讨论这种设计的优缺点、对具体实现造成的影响。


在这个系列前面的文章中,我们已经多次讨论 TCP 协议的设计原理,其中包括 TCP 协议的 三次握手流量控制和重传机制最大数据段 以及 粘包 等问题。本文将继续分析 TCP 协议的实现细节,今天要分析的问题是为什么 TCP 协议需要 TIME_WAIT 状态以及该状态的作用究竟是什么。


TCP 协议中包含 11 种不同的状态,TCP 连接会根据发送或者接收到的消息转换状态,如下图所示的状态机展示了所有可能的转换,其中不仅包含了正常情况下的状态转换过程,还包含了异常状态下的状态转换:



图 1 - TCP 协议状态


使用 TCP 协议通信的双方会在关闭连接时触发 TIME_WAIT 状态,关闭连接的操作其实是告诉通信的另一方自己没有需要发送的数据,但是它仍然保持了接收对方数据的能力,一个常见的关闭连接过程如下1


  1. 当客户端没有待发送的数据时,它会向服务端发送 FIN 消息,发送消息后会进入 FIN_WAIT_1 状态;

  2. 服务端接收到客户端的 FIN 消息后,会进入 CLOSE_WAIT 状态并向客户端发送 ACK 消息,客户端接收到 ACK 消息时会进入 FIN_WAIT_2 状态;

  3. 当服务端没有待发送的数据时,服务端会向客户端发送 FIN 消息;

  4. 客户端接收到 FIN 消息后,会进入 TIME_WAIT 状态并向服务端发送 ACK 消息,服务端收到后会进入 CLOSED 状态;

  5. 客户端等待两个最大数据段生命周期(Maximum segment lifetime,MSL)2的时间后也会进入 CLOSED 状态;



图 2 - TCP 关闭连接的过程


从上述过程中,我们会发现 TIME_WAIT 仅在主动断开连接的一方出现,被动断开连接的一方会直接进入 CLOSED 状态,进入 TIME_WAIT 的客户端需要等待 2 MSL 才可以真正关闭连接。TCP 协议需要 TIME_WAIT 状态的原因和客户端需要等待两个 MSL 不能直接进入 CLOSED 状态的原因是一样的3


  • 防止延迟的数据段被其他使用相同源地址、源端口、目的地址以及目的端口的 TCP 连接收到;

  • 保证 TCP 连接的远程被正确关闭,即等待被动关闭连接的一方收到 FIN 对应的 ACK 消息;


上述两个原因都相对比较简单,我们来展开介绍这两个原因背后可能存在的一些问题。

阻止延迟数据段

每一个 TCP 数据段都包含唯一的序列号,这个序列号能够保证 TCP 协议的可靠性和顺序性,在不考虑序列号溢出归零的情况下,序列号唯一是 TCP 协议中的重要约定,一旦违反了这条规则,就可能造成令人困惑的现象和结果。为了保证新 TCP 连接的数据段不会与还在网络中传输的历史连接的数据段重复,TCP 连接在分配新的序列号之前需要至少静默数据段在网络中能够存活的最长时间,即 MSL4


To be sure that a TCP does not create a segment that carries a sequence number which may be duplicated by an old segment remaining in the network, the TCP must keep quiet for a maximum segment lifetime (MSL) before assigning any sequence numbers upon starting up or recovering from a crash in which memory of sequence numbers in use was lost.



图 3 - TIME-WAIT 较短导致的数据段延迟接收


在如上图所示的 TCP 连接中,服务端发送的 SEQ = 301 消息由于网络延迟直到 TCP 连接关闭后也没有收到;当使用相同端口号的 TCP 连接被重用后,SEQ = 301 的消息才发送到客户端,然而这个过期的消息却可能被客户端正常接收,这就会带来比较严重的问题,所以我们在调整 TIME_WAIT 策略时要非常谨慎,必须清楚自己在干什么。


RFC 793 中虽然指出了 TCP 连接需要在 TIME_WAIT 中等待 2 倍的 MSL,但是并没有解释清楚这里的两倍是从何而来,比较合理的解释是 — 网络中可能存在来自发起方的数据段,当这些发起方的数据段被服务端处理后又会向客户端发送响应,所以一来一回需要等待 2 倍的时间5


RFC 793 文档将 MSL 的时间设置为 120 秒,即两分钟,然而这并不是一个经过严密推断的数值,而是工程上的选择,如果根据服务历史上的经验要求我们改变操作系统的设置,也是没有任何问题的;实际上,较早版本的 Linux 就开始将 TIME_WAIT 的等待时间 TCP_TIMEWAIT_LEN 设置成 60 秒,以便更快地复用 TCP 连接资源:


C


#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT          * state, about 60 seconds  */
复制代码


在 Linux 上,客户端的可以使用端口号 32,768 ~ 61,000,总共 28,232 个端口号与远程服务器建立连接,应用程序可以在将近 3 万的端口号中任意选择一个:


$ sysctl net.ipv4.ip_local_port_rangenet.ipv4.ip_local_port_range = 32768 61000
复制代码


但是如果主机在过去一分钟时间内创建的 TCP 连接数超过 28,232,那么再创建新的 TCP 连接就会发生错误,也就是说如果我们不调整主机的配置,那么每秒能够建立的最大 TCP 连接数为 ~4706

保证连接关闭

从 RFC 793 对 TIME_WAIT 状态的定义中,我们可以发现该状态的另一个重要作用,等待足够长的时间以确定远程的 TCP 连接接收到了其发出的终止连接消息 FIN 对应的 ACK


TIME-WAIT - represents waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request.


如果客户端等待的时间不够长,当服务端还没有收到 ACK 消息时,客户端就重新与服务端建立 TCP 连接就会造成以下问题 — 服务端因为没有收到 ACK 消息,所以仍然认为当前连接是合法的,客户端重新发送 SYN 消息请求握手时会收到服务端的 RST 消息,连接建立的过程就会被终止。



图 4 - TIME-WAIT 较短导致的握手终止


在默认情况下,如果客户端等待足够长的时间就会遇到以下两种情况:


  1. 服务端正常收到了 ACK 消息并关闭当前 TCP 连接;

  2. 服务端没有收到 ACK 消息,重新发送 FIN 关闭连接并等待新的 ACK 消息;


只要客户端等待 2 MSL 的时间,客户端和服务端之间的连接就会正常关闭,新创建的 TCP 连接收到影响的概率也微乎其微,保证了数据传输的可靠性。

总结

在某些场景下,60 秒的等待销毁时间确实是难以接受的,例如:高并发的压力测试。当我们通过并发请求测试远程服务的吞吐量和延迟时,本地就可能产生大量处于 TIME_WAIT 状态的 TCP 连接,在 macOS 上可以使用如下所示的命令查看活跃的连接:


$ netstat -tanActive Internet connections (including servers)Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)tcp4       0      0  192.168.50.109.51284   47.95.49.174.443       TIME_WAITtcp4       0      0  192.168.50.109.51275   47.95.49.174.443       TIME_WAIT...tcp4       0      0  192.168.50.109.51273   203.107.32.116.443     TIME_WAITtcp4       0      0  192.168.50.109.51293   203.107.32.116.443     TIME_WAITtcp4       0      0  192.168.50.109.51297   203.107.32.116.443     TIME_WAIT...
复制代码


当我们在主机上通过几千个并发来测试服务器的压力时,这些用于压力测试的连接会迅速消耗主机上的 TCP 连接资源,几乎所有的 TCP 都会处于 TIME_WAIT 状态等待销毁。如果我们真遇到不得不处理单机上的 TIME_WAIT 状态的时候,那么可以通过以下几种方法处理:


  1. 使用 SO_LINGER 选项并设置暂存时间 l_linger 为 0,在这时如果我们关闭 TCP 连接,内核就会直接丢弃缓冲区中的全部数据并向服务端发送 RST 消息直接终止当前的连接7

  2. 使用 net.ipv4.tcp_tw_reuse 选项,通过 TCP 的时间戳选项允许内核重用处于 TIME_WAIT 状态的 TCP 连接8

  3. 修改 net.ipv4.ip_local_port_range 选项中的可用端口范围,增加可同时存在的 TCP 连接数上限;


需要注意的是,另一个常见的 TCP 配置项 net.ipv4.tcp_tw_recycle 已经在 Linux 4.12 中移除9,所以我们不能再通过该配置解决 TIME_WAIT 设计带来的问题。


TCP 的 TIME_WAIT 状态有着非常重要的作用,它是保证 TCP 协议可靠性不可缺失的设计,如果能通过加机器解决的话就尽量加机器,如果不能解决的话,我们就需要理解其背后的设计原理并尽可能避免修改默认的配置,就像 Linux 手册中说的一样,在修改这些配置时应该咨询技术专家的建议;在这里,我们再重新回顾一下 TCP 协议中 TIME_WAIT 状态存在的原因,如果客户端等待的时间不够长,那么使用相同端口号重新与远程建立连接时会造成以下问题:


  • 因为数据段的网络传输时间不确定,所以可能会收到上一次 TCP 连接中未被收到的数据段;

  • 因为客户端发出的 ACK 可能还没有被服务端接收,服务端可能还处于 LAST_ACK 状态,所以它会回复 RST 消息终止新连接的建立;


TIME_WAIT 状态是 TCP 与不确定的网络延迟斗争的结果,而不确定性是 TCP 协议在保证可靠这条路的最大阻碍。到最后,我们还是来看一些比较开放的相关问题,有兴趣的读者可以仔细思考一下下面的问题:


  • net.ipv4.tcp_tw_reuse 配置如何通过时间戳保证重用 TCP 连接的相对安全?

  • net.ipv4.tcp_tw_recycle 配置为什么被 Linux 从协议栈中移除?


本文转载 Draveness 技术网站。


原文链接:https://draveness.me/whys-the-design-tcp-time-wait


2020-03-20 21:291054

评论

发布
暂无评论
  • 贾斯特里尼 & 布鲁克斯葡萄酒,佳节送礼首选

    时间飞逝,眨眼间,2022年已近尾声,2023年元旦近在咫尺。过去的三年,注定是载入史册的年份,我们经历了无声的战争,最终赢得了胜利。2023年即将到来,我们又可以恢复到三年前的样子,走亲访友,把酒言欢,想想都觉得无比惬意。去见许久未曾谋面的亲朋好友,

    2022-12-21

  • Dubbo 延迟与粘滞连接

    大家好,今天开始给大家分享 — Dubbo 专题之 Dubbo 延迟和粘滞连接。在前一个章节中我们介绍了 Dubbo 并发控制,Dubbo 为我们提供两大类的配置:消费端的配置和服务提供端配置,我们分别可以对服务提供端和服务消费端进行并发数量的控制。同时我们也例举了常

    2021-05-20

  • 设计有意义的选择——再谈心流

    上篇谈到心流源自有意义的选择。设计师应该在游戏中应该提供给玩家什么样的选择机会,让玩家在游戏中保持心流状态,是游戏成败的关键因素。今天让我们从理解游戏中的选择入手,去讨论设计选择的方法,以及和现实生活的联系。

    2021-03-19

  • 状态机的概念与设计

    一般情况下,状态触发器的数量是有限的,其状态数也是有限的,故称为有限状态机(Finite State Machine,简称为FSM)。状态机中所有触发器的时钟输入端被连接到一个公共时钟脉冲源上,其状态的转换是在同一时钟源的同一脉冲边沿同步进行的,所以它也被称作时

    2023-02-09

  • 40000 美元之后,比特币高位震荡加剧,是买?是卖?还是持有?

    在2020年年末比特币正如人们所预料的那样突破了28000美元。然后让人没想到的是,比特币的价格涨势并没有停止,而是不断突破一个又一个里程碑,最终一举突破了41910美元历史新高,距离4.2万美元只差“临门一脚”。而创下了这一历史新高,仅仅用了8天时间。

    2021-01-19

  • Python 分支结构

    2022-12-29

  • 12|如何自动检查业务真实的健康状态?

    这节课,我会首先带你学习 Pod 的状态机制,然后通过示例应用,进一步介绍怎么为工作负载配置健康检查。

    2023-01-04

  • 【面试专题】2021 年字节,面试安卓工程师会问到那些问题

    nextPollTimeoutMillis决定了堵塞与否,以及堵塞的时间,三种情况:

    2021-11-05

  • DeFi 2.0 的 LaaS 协议,重振 DeFi 赛道发展的关键

    2021年的年12月,DeFi赛道整体的TVL达到了顶峰约为2530亿美元,这代表着彼时DeFi市场发展到了一个新的高度。但进入到2022年以来,DeFi赛道逐渐的进入到发展下行阶段,尤其是在2022年4月开始,市场因UST脱锚而导致的系列连锁反应,DeFi的TVL数据下降速率加快,

    2022-07-19

  • 有状态流处理简介 (一)

    写在前面: 大家好,我是强哥,一个热爱分享的技术狂。目前已有 12 年大数据与AI相关项目经验, 10 年推荐系统研究及实践经验。平时喜欢读书、暴走和写作。

    2021-08-06

  • TCP 面试相关总结

    1)主机A发送标志syn=1,随机产生seq =1234567的数据包到服务器,主机B由syn=1知道,A要求建立连接; 此时状态A为SYN_SENT,B为LISTEN

    2021-11-11

  • 五月,开篇

    时间已到五月三日,确刚刚准备做这个月的开篇。过去的一个多月估计会是难忘的经历。职业生涯依赖脱离工作状态最久的一次,期间每天从早到晚的反思、整理、深入、探究,很煎熬,却也收获了不少。作为开篇,整理如下。

    2021-05-03

  • OVS 设计与实现阅读笔记,五年前的这篇论文里这些问题已经明了

    这篇论文其实很早就看到了,但是当时很多概念都理解不了。一年后再翻开,发现已经能明白一些了。

    2021-01-08

  • kubernetes 灰度发布

    在生产环境中,如何保证在服务升级的时候,不影响用户的体验,这个是一个非常重要的问题。如果在我们升级服务的时候,会造成一段时间内的服务不可用,这就是不够优雅的。那什么是优雅的呢?主要就是指在服务升级的时候,不中断整个服务,让用户无感知,进而不

    2022-08-06

  • 31|行业案例分析

    2022-10-26

  • 基于 Python 和 R 的数据科学

    2023-01-12

  • 详解 HTTP Keep-Alive 选项说明及注意事项

    keep-Alive首部只是请求将连接保持在活跃状态。发出keep-alive请求之后,客户端和服务器并不一定会同意进行keep-alive会话。它们可以在任意时刻关闭空闲的keep-alive连接,并可随意限制keep-alive连接所处理事务的数量。

    2023-04-20

  • 启发式搜索的实现、特性和题解

    2022-09-30

  • 42|自己成功到助人成功

    2022-10-26

  • netty 系列之: 从零到壹, 搭建一个 SOCKS 代理服务器

    上一篇文章,我们讲到了netty对SOCKS消息提供了SocksMessage对象的封装,并且区分SOSCKS4和SOCKS5,同时提供了连接和响应的各种状态。

    2021-12-17

发现更多内容

2024 年入局大模型:是否为时已晚?

快乐非自愿限量之名

人工智能 AI技术 GPT

Python实现科学式占卜

梦笔生花

Python 编程 科学卜卦

源码交付:定制软件开发的重要保障

SoFlu软件机器人

【教程】如何在苹果手机上查看系统文件?

雪奈椰子

WorkPlus开启高效沟通新时代,领先的IM工具之选

WorkPlus

目标主力能源:华为智能光伏的时代指南针

脑极体

AI 能源

探究HTTP代理爬虫的反爬虫策略

百度搜索:蓝易云

云计算 Linux 运维 HTTP 云服务器

Ubuntu系统通用镜像加速配置教程

百度搜索:蓝易云

云计算 Linux ubuntu 运维 云服务器

vue-office文档预览跨域问题

麦兜

独立站的客户支持体系:提高客户满意度的秘密武器

技术冰糖葫芦

API

解密三维CAD选择难题,谁是企业研发设计的「最佳拍档」?

ToB行业头条

《设计模式之美》PDF

程序员李木子

谈谈 RocketMQ 5.0 分级存储背后一些有挑战的技术优化

阿里巴巴云原生

阿里云 RocketMQ 云原生

centos7系统逻辑分区磁盘扩展教程

百度搜索:蓝易云

云计算 Linux centos 运维 云服务器

Atlassian 停服 Bamboo,CI/CD 用不了了?教你快速迁移到极狐GitLab CI

极狐GitLab

ClickHouse分析效率翻倍提升,揭秘奇点云对归因分析场景的优化实践

奇点云

奇点云 clickhosue DataKun

演讲回顾 | 清晖资深讲师分析敏捷实践的最新趋势,帮助大型企业落地敏捷开发

龙智—DevSecOps解决方案

敏捷 Atlassian

存内计算技术打破常规算力局限性

小明Java问道之路

如何降低微服务复杂度丨云栖大会微服务主题分享实录

阿里巴巴云原生

阿里云 微服务 云原生

《2024年金融业生成式AI应用报告》:已有6家上市银行发布大模型技术应用进展

科技热闻

Atlassian为Jira、Confluence引入AI新功能,进一步释放团队潜能

龙智—DevSecOps解决方案

AI Atlassian

2023 年度技术盘点:从13个企业关心的问题看懂用云范式的改变

阿里巴巴云原生

阿里云 容器 云原生

uniapp vuecli项目融合[小记]:将多个项目融合,打包成一个小程序/App,拆分多个H5应用

达摩

小程序 uni-app vue cli

🛠 Open Source Instant Messaging (IM) Project OpenIM Source Code

Geek_1ef48b

C# 方法详解:定义、调用、参数、默认值、返回值、命名参数、方法重载全解析

小万哥

C# 程序人生 编程语言 软件工程 后端开发

《设计模式:可复用面向对象软件的基础》PDF

程序员李木子

reptimeAI + Xinference 联合方案:高效部署并监控你的 LLM 应用

Greptime 格睿科技

监控 openai LLM模型 Greptime GreptimeDB

Go 定时器:Timer 和 Ticker

陈明勇

Go golang Go定时器

WorkPlus打造高效协作的即时通讯工具,提升工作效率

WorkPlus

构建端到端可观测全景丨云栖大会可观测分享实录

阿里巴巴云原生

阿里云 Serverless Kubernetes 云原生

2024 年最新版Java面试题及答案整理(纯干货,超详细)

架构师之道

程序员 java面试

为什么 TCP 协议有 TIME_WAIT 状态_文化 & 方法_Draveness_InfoQ精选文章