理解Time-Wait
What is it?
TIME-WAIT状态的主要作用在于TCP连接的拆除阶段。拆除一个TCP连接通常需要交换4个segment,如下图所示:
Host1上的应用程序关闭了自己这一端的连接,使得TCP向Host 2发送了一个FIN。Host 2对这个FIN进行ACK,并将这个FIN作为一个EOF传送给应用程序(假设Host 2上的应用程序有一个挂起的read操作)。经过一段时间后,Host 2上的应用程序close掉自己那一端的连接,这样TCP会向Host 2发送一个FIN,Host 1会以一个ACK应答。
此时,Host 2关闭连接,并释放资源。从Host 2的角度来看,连接已经不复存在了。但是,Host 1还没有关闭连接,而是会进入TIME-WAIT状态,并在这个状态停留2MSL(2 Maximum Segment Lifetime)。
MSL(最大分段寿命)是segment被丢弃之前在网络中可以存活的最大时长。
等待了2MSL之后,Host 1也将TCP连接关闭,并释放资源。
注意
关于TIME-WAIT状态,需要注意以下3点:
1. 通常,只有主动关闭TCP连接的那一端会进入TIME-WAIT状态。
主动关闭表示发送第一条FIN的那一端。另一端称为被动关闭。有一种可能是同时关闭,即两端同时关闭连接,并向网络发送FIN,在这种情况下,就认为两个应用程序都进行了主动关闭,两端都会进入TIME-WAIT状态。
2. RFC将MSL定义为2分钟,根据这个定义,连接会在TIME-WAIT状态停留4分钟。但实际上,各实现均有不同,一般TIME-WAIT状态持续时间在1分钟到4分钟之间。
3. 如果连接处于TIME-WAIT状态时有segment到达,就重启2MSL的定时器。
为什么需要TIME-WAIT?
使用TIME-WAIT状态主要有两个目的:
1. 维护连接状态
It maintains the connection state in case the final ACK, which is sent by the side doing the active close, is lost, causing the other side to resend its FIN.
如果主动关闭连接的那端发送的最后一条ACK丢失,那么另一端会重新发送FIN(注意,这两者一来一回最长的时间正好是2MSL),如果此时主动关闭方已经关闭了连接的话(也就是说没有TIME-WAIT的话),那么主动方收到对方重送的FIN后,由于TCP已经没有这条连接的记录了,会发送RST(重置)给对端,对端会将此标记为error,因而无法正常关闭连接。但是,如果主动方处于TIME-WAIT状态,那么它就仍然有这条TCP连接的记录,就可以对对端重送的FIN进行正确的相应。
这也就解释了为什么连接在TIME-WAIT状态收到一个segment时,需要重启2MSL的定时器。如果最后一条ACK丢失,对端重传FIN,处于TIME-WAIT状态的这一端会再次对FIN进行ACK,并且重启定时器以防这一条ACK也丢失。
2. 为了耗尽网络中所有基于此连接的“走失的segment”提供时间。
It provides time for any ‘stray segments’ from the connection to drain from the network.
IP数据报在网络上传输时,难免会发生丢失或延迟,当然,TCP会有一些机制来保证对没有被即时ACK的segment进行重传。
如果没有TIME-WAIT状态,并且,这些延迟的或者被重传的segment在连接被关闭后到达,TCP只是将这些segment丢弃并以RST回应,当RST到达另一端时,由于这台Host中也没有这条TCP连接的记录了,所以RST会被丢弃,在这种情况下不会产生问题。但是,如果在这两台主机上用同样的端口号建立了一条新连接,那么之前那个TCP连接中延迟的或者被重传的segment的sequence number可能正好落在新连接的接收窗口中(receive sliding window),那么这部分的数据就会被接收,从而对新连接造成破坏。
TIME-WAIT状态确保了在原有连接的所有segment从网络中消失之前,不会再次使用原来用过的socket对(两个IP地址以及相应的端口号),因此TIME-WAIT状态在提供TCP可靠性方面发挥了重要的作用。
TIME-WAIT 暗杀
不幸的是,TIME-WAIT状态可以被提前终止,这被称为TIME-WAIT暗杀。有以下两种情况:
1. RFC 793指出,当一条连接处于TIME-WAIT状态并收到一个RST时,应该立即将连接关闭。
假设,现在有一条连接处于TIME-WAIT状态,并且之前一个延迟的或者被retransmission的segment到达,而这个segmentTCP无法接收(例如序列号在接受窗口之外),TCP会以一个ACK相应,说明它所期待的序列号(就是对等实体的FIN之后的序列号)。但对等实体中已经没有这个TCP连接的记录了,所以会以一个RST来进行ACK。当这个RST回到处于TIME-WAIT状态的那一端时,会使连接立即关闭——TIME-WAIT状态被暗杀了。
幸运的是,对TCP进行修改,使其忽略TIME-WAIT状态下的RST即可,某些协议栈进行了这样的修改。
2. 另一种TIME-WAIT暗杀的方式是有意为之的。
即使应用程序是TCP连接的主动关闭方,程序员也可以使用套接字选项 SO_LINGER 迫使连接立即关闭,而不经过TIME-WAIT状态。有时,会推荐使用这种可疑的方式使得服务器跳出TIME-WAIT状态,这样就可以在服务器重启或者崩溃后快速重启。但是,健壮的应用程序永远都不应该干扰TIME-WAIT状态,因为这是TCP可靠机制的重要组成部分。
通常,应用程序关闭一条连接时,即使TCP发送缓冲区中仍然有数据要发送,close调用也会立即返回,虽然,TCP还是会尝试着发送未发送出去的数据,但是应用程序并不知道是否发送成功了。为了防止这个问题的发生,可以设置套接字选项SO_LINGER(通过填写linger结构体,并调用setsockopt来设置套接字的SO_LINGER选项)。
如果成员l_onoff为零,那么linger选项将被关闭,其行为与默认行为相同——close调用立即返回,内核继续尝试发送所有未发送的数据。
如果l_onoff非零,其行为取决于l_linger的值。如果l_linger非零,就将l_linger作为内核等待挂起的数据被发送出去并被确认(ACK)而逗留的时间间隔。也就是说,close在所有数据都发送完毕,或者时间间隔到期之前是不会返回的。
如果l_linger时间到了,仍然有未发送的数据,close就返回EWOULDBLOCK,所有未发送的数据都可能丢失。如果数据都发送出去了,close就返回0。
当然,以这种方式使用SO_LINGER,能保证的也只是数据到达了对方的TCP接收缓冲区中,不能保证数据被对面的应用程序读走。
最后如果l_onoff非零,l_linger为零,TCP连接会被丢弃。也就是说,向对等实体发送一个RST(表明连接已经不存在),不经过TIME-WAIT状态就立即关闭连接。这就是所谓的有意的TIME-WAIT暗杀。
理解Time-Wait的更多相关文章
- 理解CSS视觉格式化
前面的话 CSS视觉格式化这个词可能比较陌生,但说起盒模型可能就恍然大悟了.实际上,盒模型只是CSS视觉格式化的一部分.视觉格式化分为块级和行内两种处理方式.理解视觉格式化,可以确定得到的效果是应 ...
- 彻底理解AC多模式匹配算法
(本文尤其适合遍览网上的讲解而仍百思不得姐的同学) 一.原理 AC自动机首先将模式组记录为Trie字典树的形式,以节点表示不同状态,边上标以字母表中的字符,表示状态的转移.根节点状态记为0状态,表示起 ...
- 理解加密算法(三)——创建CA机构,签发证书并开始TLS通信
接理解加密算法(一)--加密算法分类.理解加密算法(二)--TLS/SSL 1 不安全的TCP通信 普通的TCP通信数据是明文传输的,所以存在数据泄露和被篡改的风险,我们可以写一段测试代码试验一下. ...
- node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理
一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...
- 如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念
一.前言 DDD(领域驱动设计)的一些介绍网上资料很多,这里就不继续描述了.自己使用领域驱动设计摸滚打爬也有2年多的时间,出于对知识的总结和分享,也是对自我理解的一个公开检验,介于博客园这个平 ...
- 学习AOP之透过Spring的Ioc理解Advisor
花了几天时间来学习Spring,突然明白一个问题,就是看书不能让人理解Spring,一方面要结合使用场景,另一方面要阅读源代码,这种方式理解起来事半功倍.那看书有什么用呢?主要还是扩展视野,毕竟书是别 ...
- ThreadLocal简单理解
在java开源项目的代码中看到一个类里ThreadLocal的属性: private static ThreadLocal<Boolean> clientMode = new Thread ...
- JS核心系列:理解 new 的运行机制
和其他高级语言一样 javascript 中也有 new 运算符,我们知道 new 运算符是用来实例化一个类,从而在内存中分配一个实例对象. 但在 javascript 中,万物皆对象,为什么还要通过 ...
- 深入理解JS 执行细节
javascript从定义到执行,JS引擎在实现层做了很多初始化工作,因此在学习JS引擎工作机制之前,我们需要引入几个相关的概念:执行环境栈.全局对象.执行环境.变量对象.活动对象.作用域和作用域链等 ...
- 浅谈我对DDD领域驱动设计的理解
从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...
随机推荐
- PyCharm下的pywin32安装及使用
转载http://www.mamicode.com/info-detail-2145088.html
- Halcon除法
今天,用到了Halcon 的除法.求出两个region的面积,area1,area2.我想求出它们的比值area1/area2.但是发现比值是整数,没有保留小数.应该改为这样area1/real(ar ...
- spring 大会的启示
1.事件驱动的微服务编程 2.无服务架构的编程模型 3.微服务缓存
- __enter__ 和 __exit__上下文管理协议
class F: def __init__(self,n): self.n = n def __enter__(self): print('执行enter') return self def __ex ...
- tomcat8做成windows服务
- Ubuntu16下用virtualbox 安装Windows虚拟机
平时要用Windows系统,但是现在工作都是在Linux下面开发,所以都没装Windows,之前用vm虚拟机比较麻烦, 所以就用virtualbox搞搞: 1.sed -i '$adeb http:/ ...
- aio,nio ,io 心得
1.nio 流的过程有几个,连接,可读,读 ,返回 :连接了不一定可读,等待浪费时间,这些时间可以去读其他的连接,selector是管理,管理全部测一下可不可读,只对可读的连接进行读取.同时,nio有 ...
- SVG绘制太极图
思路:先画一整个圆,填充颜色为黑色,再用一个边框和填充颜色均为白色的长方形覆盖右半边的半圆,再以同一个圆心,相同半径绘制一整个圆,该圆的边线颜色为黑色,没有填充颜色,最后常规操作再画四个小圆 源代码: ...
- Pushlet实现后台信息推送(一)
Pushlet是使用较多的后台向前台推送信息的工具.前台订阅某个感兴趣的事件joinListen,触发后台的Pushlet的servlet,为该请求会话建立session,默认这个sessionID是 ...
- gitlab 常用维护命令
GitLab简介 GitLab 是一个用于仓库管理系统的开源项目.使用Git作为代码管理工具,并在此基础上搭建起来的web服务.Github是公共的git仓库,而Gitlab适合于搭建企业内部私有gi ...