TCP连接和 time_wait、close_waite

tags:time_wait close_waite RST TCP


引言:前两天朋友公司的服务器垮掉了,最后查出的原因是发现大量的time_wait网络状态。被问起来time_wait是什么,当时就简单的给解释了两句,后来想想正好博客没有特别好的话题,拿来写一下也很不错。

简单的描述产生原因

因为本文较长,如果没有耐心的可以简单了解一下,有耐心的请阅读全文。

TCP是面向连接的,即使不知道具体的过程,也都知道TCP的三次握手,四次挥手。挥手也就是关闭连接,关闭连接的时候,主动关闭的一方在接收到被动关闭方的回应前,处于time_wait状态,并保持一段时间。close_wait是被动关闭方接收到关闭链接请求后所处的状态。

查看状态的代码:

$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

>结果
$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
CLOSE_WAIT 134
ESTABLISHED 245 //已经建立连接,正在传输状态
TIME_WAIT 3

TCP的三次握手和四次挥手的状态转换

要说清楚这个状态,需要了解一定的TCP知识,我们在这里简单介绍一下

三次握手建立连接

TCP必须经过三次握手来建立可靠连接才能彼此传输数数据。假设请求方为客户端与服务端建立TCP连接

在握手前,TCP服务器进程已经创建传输控制块TCB(传输控制模块,记录TCP

运行过程中的变量),准备接受客户连接请求,此时服务器进入LISTEN(监听)状态;

第一次握手:TCP客户端先创建传输控制块TCB,然后向服务器发出连接请求报文,这时报文首部中的同步位SYN=1,初始序列号 seq=x ,TCP客户端进程进入了SYN_SENT(同步已发送)状态。TCP协议中SYN报文段(SYN=1的报文段)不携带数据,但需要消耗掉一个序号(序号每次报文要递增)。

第二次握手:服务端在收到客户端发过来的SYN请求报文后,如果同意连接,则发出确认报文。确认报文ACK=1,SYN=1,确认号是ack=x+1(这个ack是确认号,大写的ACK是标志位,文章最末尾有各个标志位的简单介绍),同时自己也初始化一个序列号 seq=y,此时,服务器TCP进程进入了SYN_RCVD(收到同步)状态。这个SYN同样不携带数据,且消耗一个序号。

第三次握手:客户端口收到服务端发过来的SYN和ACK确认报文后,还要向服务器给出确认,确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,客户端状态由原来的SYN_SENT状态变为ESTABLISHED(连接已确认建立)。TCP协议中ACK报文可以携带数据,携带数据的时候消耗序号

服务端收到报文后,由原来的SYN_RCVD变为ESTABLISHED,双方开始通信

三次握手有一个很形象的比喻

//客户端和服务端打电话
客户端:服务端你能听见吗
服务端:哎,能听见,你能听见我说话吗
客户端:能听见,有点事找你(如果没有这一步,服务端并不知道客户端是否能听见)

四次挥手关闭连接

先来解释一个TCP连接到底是谁关闭的,一般来说,谁断开都可以,对于我们常用的http来说,一般情况下是这样的

  • 对于http1.0协议

    1. 如果响应头中有content-length头,则以content-length的长度就可以知道body的长度了,客户端在接收body时,就可以依照这个长度来接收数据,接收完后,就表示这个请求完成了,客户端请求断开。
    2. 如果没有content-length头,则客户端会一直接收数据,直到服务端主动断开连接,才表示body接收完了。
  • 对于http1.1协议

    1. 如果响应头中的Transfer-encoding为chunked传输,则表示body是流式输出,body会被分成多个块,每块的开始会标识出当前块的长度,此时,body不需要通过长度来指定,客户端主动请求断开。
    2. 如果是非chunked传输,而且有content-length,则按照content-length来接收数据,接收完成后客户端请求断开连接。
    3. 如果是非chunked,并且没有content-length,则客户端接收数据,直到服务端主动断开连接。

下面说四次挥手的过程

我们假设客户端是请求关闭连接的一方,服务端为被动关闭

第一次挥手:客户端进程发请求关闭(或称释放)连接报文,并且停止发送数据。数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN_WAIT_1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。

第二次挥手:服务器收到请求关闭报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE_WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE_WAIT状态持续的时间。

客户端收到服务器的确认请求后,此时,客户端就进入FIN_WAIT_2(终止等待2)状态,等待服务器发送关闭连接报文(在这之前还需要接受服务器发送的最后的数据)。

第三次挥手:服务器将最后的数据发送完毕后(或者没有需要发送的数据),就向客户端发送关闭连接报文,FIN=1,ack=u+1。由于服务器很可能又发送了一些数据,所以假定此时的序列号为seq=w,此时,服务器就进入了LAST_ACK(最后确认)状态,等待客户端的确认。

第四次挥手:客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME_WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2MSL(最大报文段寿命)的时间,当客户端撤销相应的TCB后,才进入CLOSED状态。

服务器只要收到了客户端发出的确认,立即进入CLOSED状态。撤销TCB后,就结束了这次的TCP连接。可以看到,服务器(被动断开方)结束TCP连接的时间要比客户端(主动断开方)早一些。

//客户端和服务端打电话
客户端:我的事说完了,有事情你说我听着,没什么事挂了哈
服务端:好的知道了,(好的,我还要和你说个事,你妈叫你回家吃饭)
服务端:行了挂了吧
客户端:哦,知道了,那我挂了

关于为什么要等待2MSL:

MSL(Maximum Segment Lifetime),这是TCP 对TCP Segment 生存时间的限制。

客户端发送最后一个ACK后,不能确保服务端一定能收到,假如ACK没有被服务端收到,超时后服务端重新进行第三次挥手,这时候如果A还在等待,又收到第三次挥手的FIN消息,证明ACK没有成功到达,这个时间至少是:服务端的超时时间 + FIN的传输时间,为了保证可靠,采用更加保守的等待时间2MSL。

如果客户端此时没有在等待状态直接CLOSED,服务端超时后发送FIN消息到客户端,客户端表示并不知道这数据包是干什么的,所以响应一个RST(用来异常的关闭连接,请自行了解),如果客户端有一个和服务端的新连接在这个端口上建立。这将可能导致后面建立的连接受到影响,TCP是可靠的连接,所以是不希望这种不靠谱的事情出现的。这种错误可以比喻为

//客户端和服务端打电话
客户端:我的事说完了,有事情你说我听着,没什么事挂了哈
服务端:好的知道了,(好的,我还要和你说个事,你妈叫你回家吃饭)
服务端:行了挂了吧
客户端:哦,知道了,那我挂了
(上面这一句因为信号不好等原因服务端没收到客户端就挂了电话)后面有事情又拨通了服务端电话(建立了新连接)
服务端:挂了吧
客户端:怎么回事,蛇精病啊,刚通了就让我挂

TCP报文首部标志位

关于报文首部格式,网上有很多,而且大部分都是以正确的,下面只介绍和本文有关的,

  • 标志位:

    1. SYN(synchronous建立联机)

      在连接建立时用来同步序号,当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意时,则应在响应的报文段中使SYN=1和ACK=1,因此,SYN置1就表示这是一个连接请求或连接接受报文。
    2. ACK(acknowledgement 确认)

      仅当ACK=1时确认号字段才有效,TCP规定,连接建立后所有传送的报文段都必须把ACK置1.
    3. PSH(push传送)

      当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送操作。
    4. FIN(finish结束)

      用来释放一个连接,当FIN=1时,表示此报文段的发送方的数据已发送完毕,并要求释放运输连接。
    5. RST(reset重置)

      当RST=1时,表明TCP连接中出现严重错误,必须释放连接,然后再重新建立运输连接。
    6. URG(urgent紧急)

      当URG=1时,表明紧急字段有效,告诉系统此报文中有紧急数据,应尽快传送。
  • 其他:

    1. Sequence number(顺序号码)

      seq是发送的数据包本身的序列号;
    2. Acknowledge number(确认号码)

      ack是对收到的数据包的确认,值是等待接收的数据包的序列号。即期望对方继续发送的那个数据包的序列号。

TCP连接和 time_wait、close_waite的更多相关文章

  1. TCP连接的TIME_WAIT和CLOSE_WAIT 状态解说【转】

    相信很多运维工程师遇到过这样一个情形: 用户反馈网站访问巨慢, 网络延迟等问题, 然后就迫切地登录服务器,终端输入命令"netstat -anp | grep TIME_WAIT | wc ...

  2. 经典!服务端 TCP 连接的 TIME_WAIT 过多问题的分析与解决

    开源Linux 专注分享开源技术知识 本文给出一个 TIME_WAIT 状态的 TCP 连接过多的问题的解决思路,非常典型,大家可以好好看看,以后遇到这个问题就不会束手无策了. 问题描述 模拟高并发的 ...

  3. TCP连接(Time_Wait、Close_Wait)说明

    修改Time_Wait和CLOSE_WAIT时间 修改Time_Wait参数的方法 (在服务端修改)Windows下在HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlS ...

  4. TCP连接中time_wait在开发中的影响-搜人以鱼不如授之以渔

    根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方socket将进入TIME_WAIT状态,TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),T ...

  5. TCP/IP详解--TCP连接中TIME_WAIT状态过多

    TIMEWAIT状态本身和应用层的客户端或者服务器是没有关系的.仅仅是主动关闭的一方,在使用FIN|ACK|FIN|ACK四分组正常关闭TCP连接的时候会出现这个TIMEWAIT.服务器在处理客户端请 ...

  6. TCP连接的TIME_WAIT和CLOSE_WAIT 状态解说

    相信很多运维工程师遇到过这样一个情形: 用户反馈网站访问巨慢, 网络延迟等问题, 然后就迫切地登录服务器,终端输入命令"netstat -anp | grep TIME_WAIT | wc ...

  7. mysql服务器,大量tcp连接状态TIME_WAIT

    今天早上,java应用中发现too many open files,检查了下使用的连接数发现基本上在两三百左右,mysql打开的文件数也就几百左右,再看所有tcp连接,发现3306的连接有4000多, ...

  8. 服务器TCP连接中 TIME_WAIT 状态过多

    今天查看服务器的TCP连接数,发现其中 TIME_WAIT 状态的太多了: # netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a ...

  9. TCP 连接的 TIME_WAIT 过多 导致 Tomcat 假死

    最近系统二次开发之后,发现使用的 Tomcat 7 会经常假死.前端点击页面无任何反应,打开firebug,很多链接一直在等待服务器的反应.查看服务器的状态,CPU占用很少,最多不超过10%,一般只有 ...

随机推荐

  1. [SqlServer]如何向数据库插入带有单引号(')的字符串

    今天在做一个复制功能的时候,发现存在单引号字符串与INSERT INTO 语句的' '产生冲突. 在网络上找到了一个这样功能 如何向数据库插入带有单引号(')的字符串 用SQL语句往数据库某字段(字符 ...

  2. 更新Cocos2D支持Xcode 7

    原文链接(有节选简写) Apple已经释放出Xcode7,给我们带来了Swift2和每个平台的最新版本支持.Cocos2D却还在等待更新去兼容Apple的改变.不幸的是,SpriteBuilder还未 ...

  3. Java进阶(二十一)java 空字符串与null区别

    java 空字符串与null区别 1.类型 null表示的是一个对象的值,而并不是一个字符串.例如声明一个对象的引用,String a = null ; ""表示的是一个空字符串, ...

  4. 基于ARM-contexA9蜂鸣器驱动开发

    上次,我们写了一个LED的驱动程序,这一节,我们只需稍微改动一下就可以实现蜂鸣器的驱动,让我们来看看吧. 还是跟之前一样,先找电路图,找到电路板上对应的引脚和相关联的寄存器. 1.看电路图 (1)蜂鸣 ...

  5. 【Java编程】Java基本数据类型

    在较前面的一篇博文<C/C++基本数据类型>中,我主要介绍了c/c++的基本数据类型.我们知道C语言没有具体规定各类数据类型所占内存的字节数,只要求long型数据长度不小于int型,sho ...

  6. Cocos2d-swift V3.x 中的update方法

    在cocos2d V3.x中update方法如果实现,则会被自动调用;不用向早期的版本那样要显式schedule. 但是你还是要显式schedule其他方法或blocks使用node的schedule ...

  7. 漫谈jdbc

    本文可作为北京尚学堂jdbc课程的学习笔记; 简介 jdbc是什么东西? jdbc全称(Java Database Connectivity java数据库连接) 它是干什么的? 至于它是干什么的,那 ...

  8. PS 滤镜——旋转模糊

    这里给出灰度图像的模糊算法,彩色图像只要分别对三个通道做模糊即可. %%  spin blur % 旋转模糊 clc; clear all; close all; I=imread('4.jpg'); ...

  9. 64位ubuntu安装N64模拟器mupen64

    我们知道在windows平台下模拟器多如牛毛,N64的模拟器也不例外.而linux下对于想玩N64的童鞋们有啥好办法呢?我通过度娘找到一款开源的N64模拟器mupen64,其官方网址为:http:// ...

  10. 修改win7系统sid

    百度百科定义: Windows使用SID来表示所有的安全对象(security principals).安全对象包括主机,域计算机账户,用户和安全组.名字Name是用来代表SID的一个方法,可以允许用 ...