网络编程:TIME_WAITE
一、TIME_WAIT
1、TIME_WAIT发生的场景
故障:一次升级线上应用服务后,发现该服务的可用性时好时坏,一段时间可以对外提供服务,一段时间突然又不可以了。使用netstat命令发现主机有成千上万处于TIME_WAIT状态的连接。
为啥?该应用服务需要通过发起TCP连接对外提供服务。每个连接会占用一个本地端口,当在高并发的情况下,TIME_WAIT状态的连接过多,多到本机可用的端口耗尽,应用服务对外表现的症状,就是不能正常工作,但当TIME_WAIT的连接被系统收回并关闭,就有可用了
TCP连接的四次挥手:

TCP 连接终止时,主机 1 先发送 FIN 报文,主机 2 进入 CLOSE_WAIT 状态,并发送一个 ACK 应答,同时,主机 2 通过 read 调用获得 EOF,并将此结果通知应用程序进行主动关闭操作,发送 FIN 报文。主机 1 在接收到 FIN 报文后发送 ACK 应答,此时主机 1 进入 TIME_WAIT 状态。主机 1 在 TIME_WAIT 停留持续时间是固定的,是最长分节生命期 MSL(maximum segment lifetime)的两倍,一般称之为 2MSL。和大多数 BSD 派生的系统一样,Linux 系统里有一个硬编码的字段,名称为TCP_TIMEWAIT_LEN,其值为 60 秒。也就是说,Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒。
#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT state, about 60 seconds */
注:只有发起连接终止的一方会进入TIME_WAIT状态
2、TIME_WAIT的作用
为什么不直接进入 CLOSED 状态,而要停留在 TIME_WAIT 这个状态?
两个方面:
1)首先这样做是为了确保最后的ACK能让被动关闭方接收,从而帮助其正常关闭
2)为了让旧连接的重复分节在网络中自然消失。
考虑这样一个场景,在原连接中断后,又重新创建了一个原连接的“化身”,说是化身其实是因为这个连接和原先的连接四元组完全相同,如果迷失报文经过一段时间也到达,那么这个报文会被误认为是连接“化身”的一个 TCP 分节,这样就会对 TCP 通信产生影响。
所以,TCP 就设计出了这么一个机制,经过 2MSL 这个时间,足以让两个方向上的分组都被丢弃,使得原来连接的分组在网络中都自然消失,再出现的分组一定都是新化身所产生的。
重点:2MSL 的时间是从主机 1 接收到 FIN 后发送 ACK 开始计时的;如果在 TIME_WAIT 时间内,因为主机 1 的 ACK 没有传输到主机 2,主机 1 又接收到了主机 2 重发的 FIN 报文,那么 2MSL 时间将重新计时。道理很简单,因为 2MSL 的时间,目的是为了让旧连接的所有报文都能自然消亡,现在主机 1 重新发送了 ACK 报文,自然需要重新计时,以便防止这个 ACK 报文对新可能的连接化身造成干扰。
3、TIME_WAIT的危害
主要危害有两种:
1)第一是内存资源占用
2)第二是对端口资源的占用,一个TCP连接至少消耗一个本地端口,端口资源又是有限的,一般可以开启的端口为32768~61000 ,也可以通过net.ipv4.ip_local_port_range指定,如果 TIME_WAIT 状态过多,会导致无法创建新连接。
4、如何优化TIME_WAIT?
net.ipv4.tcp_max_tw_buckets
暴力的方法:通过syctl命令,将系统值调小,这个值默认为18000,当系统中处于TIME_WAIT的连接一旦超过这个值时,系统就会将所有的TIME_WAIT连接状态重置,并且只打印警告信息,但该方法治标不治本,带来的问题远多于解决的问题,不推荐使用
降低TCP_TIMEWAIT_LEN,重新编译系统
方法不错,但缺点是需要内核方面的知识,能够重新编译内核。
SO_LINGER的设置
通过设置套接字选项,来设置调用close或者shutdown关闭连接时的行为
int setsockopt(int sockfd, int level, int optname, const void *optval,
socklen_t optlen);
struct linger {
int l_onoff; /* 0=off, nonzero=on */
int l_linger; /* linger time, POSIX specifies units as seconds */
}
设置linger参数的几种可能:
- 如果l_onoff为 0,那么关闭本选项。l_linger的值被忽略,这对应了默认行为,close 或 shutdown 立即返回。如果在套接字发送缓冲区中有数据残留,系统会将试着把这些数据发送出去。
- 如果l_onoff为非 0, 且l_linger值也为 0,那么调用 close 后,会立该发送一个 RST 标志给对端,该 TCP 连接将跳过四次挥手,也就跳过了 TIME_WAIT 状态,直接关闭。这种关闭的方式称为“强行关闭”。 在这种情况下,排队数据不会被发送,被动关闭方也不知道对端已经彻底断开。只有当被动关闭方正阻塞在recv()调用上时,接受到 RST 时,会立刻得到一个“connet reset by peer”的异常。
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
setsockopt(s,SOL_SOCKET,SO_LINGER, &so_linger,sizeof(so_linger));
- 如果l_onoff为非 0, 且l_linger的值也非 0,那么调用 close 后,调用 close 的线程就将阻塞,直到数据被发送出去,或者设置的l_linger计时时间到。
第二种可能为跨越TIME_WAIT状态提供了一个可能,是一个非常危险的行为,不值得提倡。
net.ipv4.tcp_tw_reuse:更安全的设置
Linux 系统对于net.ipv4.tcp_tw_reuse的解释如下:
Allow to reuse TIME-WAIT sockets for new connections when it is safe from protocol viewpoint. Default value is 0.It should not be changed without advice/request of technical experts.
大致意思就是从协议角度理解如果是安全可控的,可以复用处于TIME_WAIT的套接字为新的连接所用。
什么是协议角度理解的安全可控呢?
主要两点:1、只适用连接发起方(C/S模型中的客户端)
2、对应的TIME_WAIT状态的连接创建时间超过1s才可以被复用
小结:
TIME_WAIT 的引入是为了让 TCP 报文得以自然消失,同时为了让被动关闭方能够正常关闭;
不要试图使用SO_LINGER设置套接字选项,跳过 TIME_WAIT;
现代 Linux 系统引入了更安全可控的方案,可以帮助我们尽可能地复用 TIME_WAIT 状态的连接。
网络编程:TIME_WAITE的更多相关文章
- 猫哥网络编程系列:HTTP PEM 万能调试法
注:本文内容较长且细节较多,建议先收藏再阅读,原文将在 Github 上维护与更新. 在 HTTP 接口开发与调试过程中,我们经常遇到以下类似的问题: 为什么本地环境接口可以调用成功,但放到手机上就跑 ...
- python select网络编程详细介绍
刚看了反应堆模式的原理,特意复习了socket编程,本文主要介绍python的基本socket使用和select使用,主要用于了解socket通信过程 一.socket模块 socket - Low- ...
- Linux Socket 网络编程
Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后 ...
- 猫哥网络编程系列:详解 BAT 面试题
从产品上线前的接口开发和调试,到上线后的 bug 定位.性能优化,网络编程知识贯穿着一个互联网产品的整个生命周期.不论你是前后端的开发岗位,还是 SQA.运维等其他技术岗位,掌握网络编程知识均是岗位的 ...
- 浅谈C#网络编程(一)
阅读目录: 基础 Socket编程 多线程并发 阻塞式同步IO 基础 在现今软件开发中,网络编程是非常重要的一部分,本文简要介绍下网络编程的概念和实践. Socket是一种网络编程接口,它是对传输层T ...
- C++11网络编程
Handy是一个简洁优雅的C++11网络库,适用于linux与Mac平台.十行代码即可完成一个完整的网络服务器. 下面是echo服务器的代码: #include <handy/handy.h&g ...
- Java - 网络编程
Java的网络编程学习,关于计算机基础的学习参考:计算机网络基础学习 - sqh. 参考:
- Linux网络编程-IO复用技术
IO复用是Linux中的IO模型之一,IO复用就是进程预先告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程进程处理,从而不会在单个IO上阻塞了.Linux中,提 ...
- Python Socket 网络编程
Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...
- iOS网络编程
今天的重点是UIWebView.NSURLSession.JSon. 网络编程联网准备:1.在Info.plist中添加AppTransportSecurity类型Dictionary:2.在AppT ...
随机推荐
- MybatisPlus - [01] 概述
MybatisPlus可以节省我们大量工作时间,所有的CURD代码它都可以自动化完成! 一.是什么 MyBatis-Plus(简称MP)是一个基于MyBatis的增强工具,其设计目的是在不改变My ...
- 补充:基于项目的协同过滤推荐算法(Item-Based Collaborative Filtering Recommendation Algorithms)
前言 继续上篇博客,继续读论文. 想看上篇论文的同学可以点击这里 相关工作 In this section we briefly present some of the research litera ...
- python 二级 语言基本元素笔记-字符串
l='12345' 1.递增顺序: 正向递增从0开始 负向从[-1]开始,l[1]=2,l[-1]=5 2.切片操作:左开右闭,l[2:5]=3,4 3.导入库,引入 库名.函数名 input函数 输 ...
- 实现Windows之间(win10)的桌面连接的三步走方法
实现Windows之间(win10)的远程桌面连接的三步走方法 目录 目录 实现Windows之间(win10)的远程桌面连接的三步走方法 目录 环境 step1:打开两台Windows电脑的 ...
- 如何编写正确高效的Dockerfile
Dockerfile是什么 Dockerfile 非常普通,它就是一个纯文本,里面记录了一系列的构建指令,比如选择基础镜像.拷贝文件.运行脚本等等,RUN, COPY, ADD指令都会生成一个 Lay ...
- Django实战项目-学习任务系统-用户管理
接着上期代码框架,开发第6个功能,用户管理,查看用户信息和学生用户属性值,尤其是总积分值,还可以查看积分流水明细,完成任务奖励积分,兑换物品消耗积分. 第一步:编写第6个功能-用户管理 1,编辑模型文 ...
- docker也一直发展
docker也一直发展.但本答案仅仅作为向新人介绍docker的入门文章的话,在大方向上还是没问题的.本文仅仅是个docker入门介绍文章,用比较宏大的叙事来描述docker的面貌,如果想了解更细节或 ...
- Delphi 判断字符是否是汉字
function IsHZ(ch: WideChar): boolean; var i: Integer; begin i := Ord(ch); if (i < 19968) or (i &g ...
- WIN2012域用户添加和批量添加工具
WIN2012域用户添加和批量添加,不需要进行复杂的进电脑管理去添加 直接在软件上就可单个用户添加,可批量添加,并把指定的用户加入组 可以自定义组织单位,使用起来比较简单方便. 链接:https:// ...
- BUUCTF---Cipher1(playfair)
playfair Playfair密码原理以及该题解题步骤 Playfair密码(Playfair cipher 或 Playfair square)一种替换密码,1854年由查尔斯·惠斯通(Char ...