TCP/IP三次握手四次挥手分析
流程图
全部11种状态
客户端独有的:(1)SYN_SENT (2)FIN_WAIT1 (3)FIN_WAIT2 (4)CLOSING (5)TIME_WAIT
服务器独有的:(1)LISTEN (2)SYN_RCVD (3)CLOSE_WAIT (4)LAST_ACK
共有的:(1)CLOSED (2)ESTABLISHED
TCP状态迁移
大家很明白TCP初始化连接三次握手吧:发SYN包,然后返回SYN/ACK包,再发ACK包,连接正式建立。但是这里有点出入,当请求者收到SYS /ACK包后,就开始建立连接了,而被请求者第三次握手结束后才建立连接。但是大家明白关闭连接的工作原理吗?关闭连接要四次握手:发FIN包,ACK 包,FIN包,ACK包,四次握手!!为什么呢,因为TCP连接是全双工,我关了你的连接,并不等于你关了我的连接。
客户端TCP状态迁移:
CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
服务器TCP状态迁移
CLOSED->LISTEN->SYN收到 ->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED
三次握手
最开始的时候客户端和服务器都是处于CLOSED状态。主动打开连接的为客户端,被动打开连接的是服务器。
- TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态;
- TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文,这是报文首部中的同部位SYN=1,同时选择一个初始序列号 seq=x ,此时,TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
- TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。
- TCP客户进程收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。
- 当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了。
四次挥手
数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。
- 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,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连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
- 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
由于Server的Socket在客户端已经关闭时而没有调用关闭,
造成服务器端的连接处在“挂起”状态,而客户端则处在等待应答的状态上。
此问题的典型特征是:
一端处于FIN_WAIT2 ,而另一端处于CLOSE_WAIT.
不过,根本问题还是程序写的不好,有待提高
-------------------------------------------------------------------------
CLOSE_WAIT,TCP的癌症,TCP的朋友。
CLOSE_WAIT状态的生成原因
首先我们知道,如果我们的服务器程序APACHE处于CLOSE_WAIT状态的话,说明套接字是被动关闭的!
因为如果是CLIENT端主动断掉当前连接的话,那么双方关闭这个TCP连接共需要四个packet:
Client ---> FIN ---> Server
Client <--- ACK <--- Server
这时候Client端处于FIN_WAIT_2状态;而Server 程序处于CLOSE_WAIT状态。
Client <--- FIN <--- Server
这时Server 发送FIN给Client,Server 就置为LAST_ACK状态。
Client ---> ACK ---> Server
Client回应了ACK,那么Server 的套接字才会真正置为CLOSED状态。
Server 程序处于CLOSE_WAIT状态,而不是LAST_ACK状态,说明还没有发FIN给Client,那么可能是在关闭连接之前还有许多数据要发送或者其 他事要做,导致没有发这个FIN packet。
通常来说,一个CLOSE_WAIT会维持至少2个小时的时间。如果有个流氓特地写了个程序,给你造成一堆的 CLOSE_WAIT,消耗你的资源,那么通常是等不到释放那一刻,系统就已经解决崩溃了。
只能通过修改一下TCP/IP的参数,来缩短这个时间:修改tcp_keepalive_*系列参数有助于解决这个 问题。
解决这个问题的方法是修改系统的参数,系统默认超时时间的是7200秒,也就是2小时, 这个太大了,可以修改如下几个参数:
|
然后,执行sysctl命令使修改生效。
连接进程是通过一系列状态表示的,这些状态有:
|
各个状态的意义如下:
LISTEN - 侦听来自远方TCP端口的连接请求;
SYN-SENT -在发送连接请求后等待匹配的连接请求;
SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认;
ESTABLISHED- 代表一个打开的连接,数据可以传送给用户;
FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;
FIN-WAIT-2 - 从远程TCP等待连接中断请求;
CLOSE-WAIT - 等待从本地用户发来的连接中断请求;
CLOSING -等待远程TCP对连接中断的确认;
LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认;
TIME-WAIT -等待足够的时间以确保远程TCP接收到连接中断请求的确认;
CLOSED - 没有任何连接状态;
TCP连接过程是状态的转换,促使发生状态转换的是用户调用:
|
传送过来的数据段,特别那些包括以下标记的数据段SYN,ACK,RST和FIN;
还有超时,上面所说的都会时TCP状态发生变化。
这个图n多人都 知道,它对排除和定 位网络或系统故障时大有帮助,但是怎样牢牢地将这张图刻在脑中呢?那么你就一定要对 这张图的每一个状态,及转换的过程有深刻地认识,不能只停留在一知半解之中。下面对这张图的11种状 态详细解释一下,以便加强记忆!不过在这之前,先回顾一下TCP建立连接的三次握手过程,以及关闭连接的四次握手过程。
11种状态详解
CLOSED: 这个没什么好说的了,表示初始状态。
LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处 于监听状态,可以接受连接了。
SYN_RCVD: 这个状态表示接受到了SYN报 文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手 过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文 后,它会进入到ESTABLISHED状态。
SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。
ESTABLISHED:这个容易理解了,表示连接已经建立了。
FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报 文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况 下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。
FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点 数据需要传送给你,稍后再关闭连接。
TIME_WAIT: 表示收到了对方的FIN报 文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标 志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发 送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报 文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报 文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一 个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一 个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文 给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话, 那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报 文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。
最后有2个问题 的回答,我自己分析后的结论(不一定保证100%正确)
1、 为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起 应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文 通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文 和FIN报文多数情况下都是分开发送的。
2、 为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状 态?
这是因为:虽然双方 都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状 态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报 文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报 文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报 文,并保证于此。
断开连接的时候, 当发起主动关闭的左边这方发送一个FIN过去后,
右边被动关闭的这方要回应一个ACK,这个ACK是TCP回应的,而不是应用程序发送的,
此时,被动关闭的一方就处于CLOSE_WAIT状态了。
如果此时被动关闭的这一方不再继续调用closesocket,那么他就不会发送接下来的FIN,导致自己老是处于CLOSE_WAIT。
只有被动关闭的这一方调用了 closesocket,才会发送一个FIN给主动关闭的这一方,同时也使得自己的状态变迁为LAST_ACK。
比如被动关闭的是客户端
当对方调用closesocket的时候,你的程序正在
|
很多人就是忘记了那句closesocket,这种代码太常见了。
我的理解,
当主动关闭的一方发送FIN到被动关闭这边后,被动关闭这边的TCP马上回应一个ACK过去,同时向上面应用程序提交一个ERROR,
导致上面的SOCKET的send或者recv返回SOCKET_ERROR.
正常情况下,如果上面在返回SOCKET_ERROR后调用了closesocket, 那么被动关闭的者一方的TCP就会发送一个FIN过去,自己的状态就变迁到LAST_ACK.
服务器上出现大量的close_wait的例子和解决方法(例子从网上找的,基本差不多)
oracle 22725 oracle9i 3u IPv4 18621468 TCP RHEL3:6800 (LISTEN) oracle 22725 oracle9i 4u IPv4 18621469 TCP RHEL3:6800->RHEL3:2174 (CLOSE_WAIT) oracle 22725 oracle9i 8u IPv4 18621568 TCP RHEL3:6800->RHEL3:2175 (CLOSE_WAIT) oracle 22725 oracle9i 9u IPv4 18621578 TCP RHEL3:6800->RHEL3:2176 (CLOSE_WAIT) oracle 22726 oracle9i 3u IPv4 18621468 TCP RHEL3:6800 (LISTEN) oracle 22726 oracle9i 4u IPv4 18621469 TCP RHEL3:6800->RHEL3:2174 (CLOSE_WAIT) oracle 22726 oracle9i 8u IPv4 18621568 TCP RHEL3:6800->RHEL3:2175 (CLOSE_WAIT) oracle 22726 oracle9i 9u IPv4 18621578 TCP RHEL3:6800->RHEL3:2176 (CLOSE_WAIT) $ kill -9 22725 |
进程被kill时,会释放占用的所有链接句柄。
该问题的出现原因网上到处都是,也就是Socket的Client端出现异常没有Close就退出了。
补充
在第一次消息发送中,A随机选取一个序列号作为自己的初始序号发送给B;第二次消息B使用ack对A的数据包进行确认,因为已经收到了序列号为x的数据包,准备接收序列号为x+1的包,所以ack=x+1,同时B告诉A自己的初始序列号,就是seq=y;第三条消息A告诉B收到了B的确认消息并准备建立连接,A自己此条消息的序列号是x+1,所以seq=x+1,而ack=y+1是表示A正准备接收B序列号为y+1的数据包。
seq是数据包本身的序列号;ack是期望对方继续发送的那个数据包的序列号。
TCP/IP三次握手四次挥手分析的更多相关文章
- 在深谈TCP/IP三步握手&四步挥手原理及衍生问题—长文解剖IP
如果对网络工程基础不牢,建议通读<细说OSI七层协议模型及OSI参考模型中的数据封装过程?> 下面就是TCP/IP(Transmission Control Protoco/Interne ...
- TCP/IP三次握手四次挥手
本文通过图来梳理TCP-IP协议相关知识.TCP通信过程包括三个步骤:建立TCP连接通道,传输数据,断开TCP连接通道.如图所示,给出了TCP通信过程的示意图. TCP 三次握手四次挥手 主要包括三部 ...
- 通俗了解TCP/IP三次握手四次挥手
前言: tcp/ip通信机制是计算机中很重要的一个知识点,不是一句两句就能解释清楚的,需要反复推敲其中的玄妙. 通俗理解: 但是为什么一定要进行三次握手来保证连接是双工的呢,一次不行么?两次不行么?我 ...
- TCP/IP 三次握手-四次挥手
TCP的建立需要三次握手,断开需要四次挥手. 首先三次握手: 首先,客户机向服务器发送请求报文,服务器回复ACK,并分配资源,而客户端接受到ACK后回复确认报文,并分配资源,此时三次握手完成. 四次挥 ...
- TCP/IP 三次握手四次挥手
TCP运输连接 TCP连接建立过程中要解决以下三个问题: (1)要使每一方能够确知双方的存在. (2)要允许双方协商一些参数(如最大窗口值.是否使用窗口扩大选项和时间戳选项以及服务质量等). (3)能 ...
- [na]TCP的三次握手四次挥手/SYN泛洪
1.TCP报文格式 上图中有几个字段需要重点介绍下: (1)序号:Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记. (2)确认序号:Ack序号,占32位, ...
- TCP协议—三次握手四次挥手的原理<转>
三次握手四次挥手的原理 TCP是面向连接的,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接.在TCP/IP协议中,TCP 协议提供可靠的连接服务,连接是通过三次握手进行初始化的.三 ...
- 救救孩子吧,到现在还搞不懂TCP的三次握手四次挥手
本文在个人技术博客同步发布,详情可用力戳 亦可扫描屏幕右侧二维码关注个人公众号,公众号内有个人联系方式,等你来撩... 前几天发了一个朋友圈,发现暗恋已久的女生给我点了个赞,于是我当晚辗转反侧.彻 ...
- TCP的三次握手四次挥手理解及面试题
一.TCP概述 每一条TCP连接都有两个端点,这种端点我们叫作套接字(socket),它的定义为端口号拼接到IP地址即构成了套接字, 例如,若IP地址为192.0.0.1 而端口号为8000,那么得到 ...
随机推荐
- Dubbo实践(十)代理
Invoker调用 代理有几种方式:普通代理.JDK.Javassist库动态代理.Javassist库动态字节码代理. 生成代理的目的是你调用invoker的相关函数后,就等同于是调用DubboIn ...
- PAT——1041. 考试座位号
每个PAT考生在参加考试时都会被分配两个座位号,一个是试机座位,一个是考试座位.正常情况下,考生在入场时先得到试机座位号码,入座进入试机状态后,系统会显示该考生的考试座位号码,考试时考生需要换到考试座 ...
- 字符型设备驱动程序-first-printf以及点亮LED灯(一)
学习使用 Linux 的 字符型设备驱动 来 进行 . 学习地址:http://edu.51cto.com/lesson/id-25710.html 第一步: 首先写 三个函数 ,2017年5月17 ...
- 删除 center os7 openjdk
卸载CentOS7-x64自带的OpenJDK并安装Sun的JDK7的方法 第一步:查看并卸载CentOS自带的OpenJDK 安装好的CentOS会自带OpenJdk,用命令 java -ver ...
- C# WinForm开发系列 - ListBox/ListView/Panel【zz】
原文传送:http://www.cnblogs.com/peterzb/archive/2009/06/18/1505424.html 1.ColorListBox ColorListBox.zi ...
- Xcode 提交APP时遇到 “has one iOS Distribution certificate but its private key is not installed”
解决办法:登录Apple开发证书后台,把发布版证书.cer文件下载到本地,双击安装即可.若还没有设置发布证书文件,则创建一个后下载. Ref: https://blog.csdn.net/dingqk ...
- 非空校验在oracle和mysql中的用法
oracle判断是否为null nvl(参数1,参数2) :如果参数1为null则返回参数2,否则返回参数1 mysql判断是否为null ifnull(参数1,参数2) :如果参数1为null则返回 ...
- 基于visual studio 2017 以及cubemx 搭建stm32的开发环境(1)
参考如下文档: 传送门:http://www.stm32cube.com/article/128 如果链接不存在的话,下载我截屏好的图: 传送门:https://pan.baidu.com/s/1NC ...
- java int 与 Integer之间的区别
int与integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而integer是对象,用一个引用指向这个对象 1.Java 中的数据类型分为基本数据类型 ...
- R语言学习笔记(二十四):plyr包的用法
plyr 这个包,提供了一组规范的数据结构转换形式. Input/Output list data frame array list llply() ldply() laply() data fram ...