TCP状态装换图知识详解(图)

TCP状态装换图

状态图

TCP状态装换图.jpg

tcp 11种状态含义.jpg

状态解释

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(重要、共详细的请看下图的2MSL): 表示收到了对方的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可用状态了。

tcp连接的建立3次握手

服务器端通常处于监听状态即LISTEN,accept()处于阻塞状态 当客户端连接服务器端时此时客户端的connect()刚刚调用并处于阻塞状态。
将会触发以下事件: 
    首先客户端的应用程序将会使tcp进程发送SYN,MSS(最大报文段长度),此时客户端将会处于SYN_SENT 网络传输给服务器端后;
    服务器端tcp接收到后服务器端将会由LISTEN状态变为SYN_RCVD 然后服务器端也会发送一个SYN,MSS还有一个ACK,注意这个ACK是客户端发送的SYN值加1;
    客户端在接收到服务器端的SYN,MSS,ACK核对无误后将会由SYN_SENT状态变为ESTABLILSHED;
    此时客户端的connect()函数将会返回不再处于阻塞状态,同时客户端发送ACK,此ACK是服务器端发送的SYN值加1,服务器端在接收到客户端的ACK核对无误后,accept()将从阻塞状态返回,同时read()处于阻塞状态。此时连接已经建立。

tcp 11种状态含义.jpg

建立连接协议(三次握手)
(1)客户端发送一个带SYN标志的TCP报文到服务器。这是三次握手过程中的报文1。 
(2) 服务器端回应客户端的,这是三次握手中的第2个报文,这个报文同时带ACK标志和SYN标志。因此它表示对刚才客户端SYN报文的回应;同时又标志SYN给客户端,询问客户端是否准备好进行数据通讯。 
(3) 客户必须再次回应服务段一个ACK报文,这是报文段3。

tcp断开连接4次挥手

当一端数据已经发送完了,就会将本端的tcp断开掉通常是客户端主动断开,这种情况同时是客户端应用程序调用close(fd)关闭套接字,这将触发tcp进程发送 FIN,此时客户端将会处于FIN_WAIT_1,服务器端在接收到这个FIN后将会处于close_wait()状态,同时read()return 0,然后服务器端将会发送ACK 值为客户端发送的FIN值加1,客户端在接收到 服务器端发送给它的ACK后将会处于FIN_WAIT2然后服务器端将 客户端的文件描述符读端关闭,此时服务器端可能还会有未发送的数据,通常会悄悄丢弃掉,然后 关闭客户端描述符close(),然后服务器端tcp进程将会发送FIN此时服务器端将会处于LAST_ACK状态客户端在接收到服务器端发送的FIN后将会由FIN_WAIT2状态变TIME_WAIT状态,同时发送ACK值为 客户端发送的FIN值加1,服务器端在接收到客户端发送的ACK后核对无误后将由LAST_ACK状态变为 CLOSED状态。注意客户端在处于TIME_WAIT状态时要经历2个MSL时间才会将状态变为CLOSED通常这个 等待的时间为60秒。
连接终止协议(四次握手) 
  由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。 
(1) TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送(报文段4)。 
(2) 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。 
(3) 服务器关闭客户端的连接,发送一个FIN给客户端(报文段6)。 
(4) 客户段发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。 

TCP正常连接建立和终止所对用的状态

TCP正常连接建立和终止所对用的状态.jpg

同时打开期间报文段的交换![]

同时打开期间报文段的交换.jpg

同时关闭期间的报文段交换

同时关闭期间的报文段交换.jpg

总结

TCP是一个面向连接的协议,所以在连接双方发送数据之前,都需要首先建立一条连接。

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

服务器调⽤socket()、 bind()、 listen() 完成初始化后,调⽤accept()阻塞等待,处于监听端口的状态,客户端调⽤socket()初始化后,调⽤connect()发出SYN段并阻塞等待服务器应答,服务器应答⼀个SYN-ACK段,客户端收到后从connect()返回,同时应答⼀个ACK段,服务器收到后 从accept()返回。

数据传输的过程: 建立连接后,TCP协议提供全双⼯的通信服务,但是⼀般的客户端/服务器程序的流程是由客户端主 动发起请求,服务器被动处理请求,⼀问⼀答的⽅式。因此,服务器从accept()返回后立刻调 ⽤read(),读socket就像读管道⼀样,如果没有数据到达就阻塞等待,这时客户端调⽤write()发送 请求给服务器,服务器收到后从read()返回,对客户端的请求进⾏处理,在此期间客户端调 ⽤read()阻塞等待服务器的应答,服务器调⽤write()将处理结果发回给客户端,再次调⽤read()阻塞 等待下⼀条请求,客户端收到后从read()返回,发送下⼀条请求,如此循环下去。如果客户端没有更多的请求了,就调⽤close() 关闭连接,就像写端关闭的管道⼀样,服务器 的read()返回0,这样服务器就知道客户端关闭了连接,也调⽤close()关闭连接。注意,任何⼀⽅调⽤close() 后,连接的两个传输⽅向都关闭,不能再发送数据了。如果⼀⽅调⽤shutdown() 则连接处 于半关闭状态,仍可接收对⽅发来的数据。
详情:基于TCP协议的服务器/客户端程序

TCP连接的建立可以简单的称为三次握手,而连接的中止则可以叫做四次握手。

1.连接的建立

在建立连接的时候,客户端首先向服务器申请打开某一个端口(用SYN段等于1的TCP报文),然后服务器端发回一个ACK报文通知客户端请求报文收到,客户端收到确认报文以后再次发出确认报文确认刚才服务器端发出的确认报文(绕口么),至此,连接的建立完成。这就叫做三次握手。如果打算让双方都做好准备的话,一定要发送三次报文,而且只需要三次报文就可以了。

可以想见,如果再加上TCP的超时重传机制,那么TCP就完全可以保证一个数据包被送到目的地。

2.结束连接

TCP有一个特别的概念叫做half-close,这个概念是说,TCP的连接是全双工(可以同时发送和接收)连接,因此在关闭连接的时候,必须关闭传和送两个方向上的连接。客户机给服务器一个FIN为1的TCP报文,然后服务器返回给客户端一个确认ACK报文,并且发送一个FIN报文,当客户机回复ACK报文后(四次握手),连接就结束了。

3.最大报文长度

在建立连接的时候,通信的双方要互相确认对方的最大报文长度(MSS),以便通信。一般这个SYN长度是MTU减去固定IP首部和TCP首部长度。对于一个以太网,一般可以达到1460字节。当然如果对于非本地的IP,这个MSS可能就只有536字节,而且,如果中间的传输网络的MSS更佳的小的话,这个值还会变得更小。

4.TCP的状态迁移图

如上图所示:服务器的状态迁移和客户端的状态迁移,如果从某一个角度出发来看这个图,就会清晰许多,这里面的服务器和客户端都不是绝对的,发送数据的就是客户端,接受数据的就是服务器。

4.1.客户端应用程序的状态迁移图

客户端的状态可以用如下的流程来表示:

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

以上流程是在程序正常的情况下应该有的流程,从书中的图中可以看到,在建立连接时,当客户端收到SYN报文的ACK以后,客户端就打开了数据交互地连接。而结束连接则通常是客户端主动结束的,客户端结束应用程序以后,需要经历FIN_WAIT_1,FIN_WAIT_2等状态,这些状态的迁移就是前面提到的结束连接的四次握手。

4.2.服务器的状态迁移图

服务器的状态可以用如下的流程来表示:

CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED

在建立连接的时候,服务器端是在第三次握手之后才进入数据交互状态,而关闭连接则是在关闭连接的第二次握手以后(注意不是第四次)。而关闭以后还要等待客户端给出最后的ACK包才能进入初始的状态。

4.3.其他状态迁移

书中的图还有一些其他的状态迁移,这些状态迁移针对服务器和客户端两方面的总结如下

LISTEN->SYN_SENT,对于这个解释就很简单了,服务器有时候也要打开连接的嘛。

SYN_SENT->SYN收到,服务器和客户端在SYN_SENT状态下如果收到SYN数据报,则都需要发送SYN的ACK数据报并把自己的状态调整到SYN收到状态,准备进入ESTABLISHED

SYN_SENT->CLOSED,在发送超时的情况下,会返回到CLOSED状态。

SYN_收到->LISTEN,如果受到RST包,会返回到LISTEN状态。

SYN_收到->FIN_WAIT_1,这个迁移是说,可以不用到ESTABLISHED状态,而可以直接跳转到FIN_WAIT_1状态并等待关闭。

4.4.2MSL等待状态

书中给的图里面,有一个TIME_WAIT等待状态,这个状态又叫做2MSL状态,说的是在TIME_WAIT2发送了最后一个ACK数据报以后,要进入TIME_WAIT状态,这个状态是防止最后一次握手的数据报没有传送到对方那里而准备的(注意这不是四次握手,这是第四次握手的保险状态)。这个状态在很大程度上保证了双方都可以正常结束,但是,问题也来了。

由于插口的2MSL状态(插口是IP和端口对的意思,socket),使得应用程序在2MSL时间内是无法再次使用同一个插口的,对于客户程序还好一些,但是对于服务程序,例如httpd,它总是要使用同一个端口来进行服务,而在2MSL时间内,启动httpd就会出现错误(插口被使用)。为了避免这个错误,服务器给出了一个平静时间的概念,这是说在2MSL时间内,虽然可以重新启动服务器,但是这个服务器还是要平静的等待2MSL时间的过去才能进行下一次连接。

4.5.FIN_WAIT_2状态

这就是著名的半关闭的状态了,这是在关闭连接时,客户端和服务器两次握手之后的状态。在这个状态下,应用程序还有接受数据的能力,但是已经无法发送数据,但是也有一种可能是,客户端一直处于FIN_WAIT_2状态,而服务器则一直处于WAIT_CLOSE状态,而直到应用层来决定关闭这个状态。

5.RST,同时打开和同时关闭

RST是另一种关闭连接的方式,应用程序应该可以判断RST包的真实性,即是否为异常中止。而同时打开和同时关闭则是两种特殊的TCP状态,发生的概率很小。

6.TCP服务器设计

前面曾经讲述过UDP的服务器设计,可以发现UDP的服务器完全不需要所谓的并发机制,它只要建立一个数据输入队列就可以。但是TCP不同,TCP服务器对于每一个连接都需要建立一个独立的进程(或者是轻量级的,线程),来保证对话的独立性。所以TCP服务器是并发的。而且TCP还需要配备一个呼入连接请求队列(UDP服务器也同样不需要),来为每一个连接请求建立对话进程,这也就是为什么各种TCP服务器都有一个最大连接数的原因。而根据源主机的IP和端口号码,服务器可以很轻松的区别出不同的会话,来进行数据的分发。

备注:

TCP指的是传输控制协议。它是一种面向连接导向的、可靠地及基于字节流的运输层通信协议。而在接触TCP中还有UDP,UDP也是一项重要的传输协议。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。

TCP流量控制(滑动窗口)

介绍UDP时我们描述了这样的问题:如果发送端发送的速度较快,接收端接收到数据后处理的速度较慢,而接收缓冲区的大小是固定的,就会丢失数据。TCP协议通过“滑动窗口(Sliding Window)”机制(防止出现丢包)解决这一问题。具体请看(http://www.cnblogs.com/invisible2/p/6650289.html)

TCP协议作为一个可靠的面向流的传输协议,其可靠性和流量控制由滑动窗口协议保证,而拥塞控制则由控制窗口结合一系列的控制算法实现。

流量控制:端到端的控制方式,接收方传递信息给发送方,使其不要发送数据太快。

主要的方式就是返回的ACK中会包含自己的接收窗口的大小,并且利用大小来控制发送方的数据发送

拥塞控制:就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。

常用的方法就是:

  1. 慢开始、拥塞控制(发送试探报文)
  2. 快重传、快恢复

问题

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报文。

参考资料:

1:tcp 11种状态转换图

2:TCP状态转换图、滑动窗口、半连接状态、2MSL

3:TCP状态知识总结(图解)

4:TCP状态转换图

本文转自:https://www.jianshu.com/p/0f93c17eba10

猜你喜欢

转载自blog.csdn.net/mediatec/article/details/87892491
今日推荐