TCP状态转换图  

  在《UNIX网络编程 卷1》一书中,作者给出了TCP状态转换图(如下)。本文也将围绕此图进行阐释。

  

  注:上图红框表示比较特殊的地方。

TCP状态转换两条主线  

  图2-4中的两条主线当然就是客户端和服务器端的状态迁移:

  对客户端(也可以是服务器端,这里的例子是客户端主动打开连接,服务器端被动打开):

  CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED

  注:若客户端到达FIN_WAIT_1时,它同时接收到了服务器的SYN、ACK,则它会直接跳过FIN_WAIT_2而到达TIME_WAIT状态。

  对服务器端(也可以是客户端)

  CLOSED->LISTEN->SYN_RECEIVED->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED

  另外还需要注意的是,客户端和服务器端可能会同时打开连接或同时关闭连接(很少出现),这时处理过程会稍微跟原来的不一样,具体参考图2-4。

各状态解释  

  CLOSED - 初始状态,表示TCP连接是“关闭着的”或“未打开的”。

  LISTEN - 表示服务器端的某个SOCKET处于监听状态,可以接受客户端的连接。

  SYN-SENT - 这个状态与SYN_RCVD 状态相呼应,当客户端SOCKET执行connect()进行连接时,它首先发送SYN报文,然后随即进入到SYN_SENT 状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT 状态表示客户端已发送SYN报文。

  SYN-RCVD - 表示接收到了SYN报文。在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat很难看到这种状态,除非故意写一个监测程序,将三次TCP握手过程中最后一个ACK报文不予发送。当TCP连接处于此状态时,再收到客户端的ACK报文,它就会进入到ESTABLISHED 状态。

  ESTABLISHED - 表示TCP连接已经成功建立,数据可以传送给用户;

  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()主动要求关闭连接。注意:FIN_WAIT_2 是没有超时的(不像TIME_WAIT 状态),这种状态下如果对方不关闭(不配合完成4次挥手过程),那这个FIN_WAIT_2 状态将一直保持到系统重启,越来越多的FIN_WAIT_2 状态会导致内核崩溃。

  CLOSE-WAIT -  表示正在等待关闭。怎么理解呢?当对方close()一个SOCKET后发送FIN报文给自己,你的系统毫无疑问地将会回应一个ACK报文给对方,此时TCP连接则进入到CLOSE_WAIT状态。接下来呢,你需要检查自己是否还有数据要发送给对方,如果没有的话,那你也就可以close()这个SOCKET并发送FIN报文给对方,即关闭自己到对方这个方向的连接。有数据的话则看程序的策略,继续发送或丢弃。简单地说,当你处于CLOSE_WAIT 状态下,需要完成的事情是等待你去关闭连接。

  CLOSING - 这种状态在实际情况中应该很少见,属于一种比较罕见的例外状态。正常情况下,当一方发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING 状态表示一方发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?那就是当双方几乎在同时close()一个SOCKET的话,就出现了双方同时发送FIN报文的情况,这时就会出现CLOSING 状态,表示双方都正在关闭SOCKET连接。

  LAST-ACK - 当被动关闭的一方在发送FIN报文后,等待对方的ACK报文的时候,就处于LAST_ACK 状态。当收到对方的ACK报文后,也就可以进入到CLOSED 可用状态了。

  TIME-WAIT - 等待足够的时间以确保远程TCP接收到连接中断请求的确认; 表示收到了对方的FIN报文,并发送出了ACK报文。TIME_WAIT状态下的TCP连接会等待2*MSL(Max Segment Lifetime,最大分段生存期,指一个TCP报文在Internet上的最长生存时间。每个具体的TCP协议实现都必须选择一个确定的MSL值,RFC 1122建议是2分钟,但BSD传统实现采用了30秒,Linux可以cat /proc/sys/net/ipv4/tcp_fin_timeout看到本机的这个值),然后即可回到CLOSED 可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。

几个问题  

  1)为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

  这是因为,服务端的LISTEN状态下的SOCKET当收到SYN报文的连接请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。

  2)为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?

  A. 可靠地实现TCP全双工连接的终止

  TCP协议在关闭连接的四次握手过程中,最终的ACK是由主动关闭连接的一端(后面统称A端)发出的,如果这个ACK丢失,对方(后面统称B端)将重发出最终的FIN,因此A端必须维护状态信息(TIME_WAIT)允许它重发最终的ACK。如果A端不维持TIME_WAIT状态,而是处于CLOSED 状态,那么A端将响应RST分节,B端收到后将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。

因而,要实现TCP全双工连接的正常终止,必须处理终止过程中四个分节任何一个分节的丢失情况,主动关闭连接的A端必须维持TIME_WAIT状态 。

  B. 允许老的重复分节在网络中消逝(实际也就是避免同一端口对应多个套接字

  TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个迟到的迷途分节到达时可能会引起问题。在关闭“前一个连接”之后,马上又重新建立起一个相同的IP和端口之间的“新连接”,“前一个连接”的迷途重复分组在“前一个连接”终止后到达,而被“新连接”收到了。为了避免这个情况,TCP协议不允许处于TIME_WAIT状态的连接启动一个新的可用连接,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个新TCP连接的时候,来自旧连接重复分组已经在网络中消逝。

  3)关闭TCP连接一定需要4次挥手吗?

  不一定,4次挥手关闭TCP连接是最安全的做法。但在有些时候,我们不喜欢TIME_WAIT状态(如当MSL数值设置过大导致服务器端有太多TIME_WAIT状态的TCP连接,减少这些条目数可以更快地关闭连接,为新连接释放更多资源),这时我们可以通过设置SOCKET变量的SO_LINGER标志来避免SOCKET在close()之后进入TIME_WAIT状态,这时将通过发送RST强制终止TCP连接(取代正常的TCP四次握手的终止方式)。但这并不是一个很好的主意,TIME_WAIT 对于我们来说往往是有利的。

参考资料

  《UNIX网络编程 卷1》

  TCP的三次握手/四次挥手以及状态变迁图 

传输控制协议(TCP) -- TCP状态转换图的更多相关文章

  1. 传输控制协议(TCP)

    传输控制协议(TCP)[来自Unix网络编程(卷一)第2章] 1.TCP是一个面向连接.可靠性的传输协议: 2.TCP含有用于动态估算客户与服务器之间往返时间(RTT)的算法,以便它知道等待一个确认需 ...

  2. 传输控制协议(TCP) -- 连接建立及终止过程

    TCP简介 相对于不可靠.无连接的用户数据报协议(User Datagram Protocol, UDP),传输控制协议(Transmission Control Protocol, TCP)是可靠的 ...

  3. tcp - 传输控制协议 (TCP)

    总缆 SYNOPSIS #include <sys/socket.h> #include <netinet/in.h> tcp_socket = socket(PF_INET, ...

  4. java基础 UDP通信 user datagram protocol 用户数据豆协议 TCP transmission control protocol 传输控制协议 多线程TCP

    无连接通信 UDP 客户端 package com.swift.test; import java.io.IOException; import java.net.DatagramPacket; im ...

  5. TCP/IP状态转换图

  6. TCP/IP协议族-----15、传输控制协议(TCP)

  7. TCP/IP 中文译名为传输控制协议/因特网互联协议,又叫网络通讯协议

    原文地址:http://hi.baidu.com/albyuyrgqgbbhoq/item/65006d2d002ab33195f62ba1 TCP/IP(Transmission Control P ...

  8. TCP的三次握手以及TCP状态转换图详解

    今天来讨论一下TCP的三次握手以及TCP的状态转换图.首先发一个三次握手的流程图如下: 圖 2.4-3.三向交握之封包连接模式A:封包发起当用戶端想要对服务器端发起连接时,就必須要送出一個要求连线的封 ...

  9. TCP的状态转换

    TCP的状态转换图 手写的状态转换图 一.服务端状态变迁:​ 服务端创建套接字之后调用listen函数将套接字有一个未连接的主动套接字转换为被动套接字,指示内核应接受指向该套接字的连接请求,套接字状态 ...

随机推荐

  1. 设置 NSZombieEnabled 定位 EXC_BAD_ACCESS 错误

    我们做 iOS 程序开发时经常用遇到 EXC_BAD_ACCESS 错误导致 Crash,出现这种错误时一般 Xcode 不会给我们太多的信息来定位错误来源,只是在应用 Delegate 上留下像 T ...

  2. 带你深入理解STL之RBTree

    最近一直忙于校招的笔试,STL的深入理解系列也耽搁了好几天,再加上!红黑树真的是超级超级难理解,超级超级复杂,参考了好多博客上的大神的理解才稍微明白一点,勉强入个门,下面请以一个菜鸟的角度跟着我一起学 ...

  3. GitHub无法访问或访问缓慢解决办法

    缘由 由于众所周知的原因,Github最近无法访问或访问很慢.由于Github支持https,因此此次屏蔽Github采用的方法是dns污染,用户访问github会返回一个错误的IPFQ当然是一种解决 ...

  4. 安卓6.0新特性在Fragment申请运行时权限

    今天在Fragment申请权限时代码如下: public void getContacts(){ int flag = ActivityCompat.checkSelfPermission(getAc ...

  5. 【Netty源码学习】ChannelPipeline(一)

    ChannelPipeline类似于一个管道,管道中存放的是一系列对读取数据进行业务操作的ChannelHandler. 1.ChannelPipeline的结构图: 在之前的博客[Netty源码学习 ...

  6. [ExtJS5学习笔记]第十八节 Extjs5的panel的dockeditems属性配置toolbar

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/39156321 官方例子:http://docs.sencha.com/extjs/5. ...

  7. 详解EBS接口开发之采购接收&退货处理之关键API--(补充)

    PROCEDURE process_rcv_online(p_api_version IN NUMBER, p_init_msg_list IN VARCHAR2 DEFAULT fnd_api.g_ ...

  8. Cocos2D v3.4.9粒子效果不能显示的原因分析及解决办法

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 在游戏App中为了衬托气氛我们往往使用一些特殊的图形效果,粒子 ...

  9. 【一天一道LeetCode】#292. Nim Game

    一天一道LeetCode 从今天开始,调整规律,不按顺序做,从easy开始! 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 ...

  10. 安卓ListView的性能优化

    在安卓APP中LIstView这个控件可以说基本上是个APP就会用到,但是关于ListView除了需要了解其最基本的用法外,作为一个要做出高性能APP的程序员还需了解一些关于LIstView控件性能优 ...