TCP/IP状态变迁图和TCP三次握手与四次挥手

网络 网络管理
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。原则是主动关闭的一方发送一个FIN报文来表示终止这个方向的连接,收到一个FIN意味着这个方向不再有数据流动,但另一个方向仍能继续发送数据,直到另一个方向也发送FIN报文。

TCP/IP状态变迁图:

TCP/IP状态变迁图:

各状态详细描述:

CLOSED:表示初始状态。对服务端和C客户端双方都一样。

LISTEN:表示监听状态。服务端调用了listen函数,可以开始accept连接了。

SYN_SENT:表示客户端已经发送了SYN报文。当客户端调用connect函数发起连接时,首先发SYN给服务端,然后自己进入SYN_SENT状态,并等待服务端发送ACK+SYN。

SYN_RCVD:表示服务端收到客户端发送SYN报文。服务端收到这个报文后,进入SYN_RCVD状态,然后发送ACK+SYN给客户端。

ESTABLISHED:表示连接已经建立成功了。服务端发送完ACK+SYN后进入该状态,客户端收到ACK后也进入该状态。

FIN_WAIT_1:表示主动关闭连接。无论哪方调用close函数发送FIN报文都会进入这个这个状态。

FIN_WAIT_2:表示被动关闭方同意关闭连接。主动关闭连接方收到被动关闭方返回的ACK后,会进入该状态。

TIME_WAIT:表示收到对方的FIN报文并发送了ACK报文,就等2MSL后即可回到CLOSED状态了。如果FIN_WAIT_1状态下,收到对方同时带FIN标志和ACK标志的报文时,可以直接进入TIME_WAIT状态,而无须经过FIN_WAIT_2状态。

CLOSING:表示双方同时关闭连接。如果双方几乎同时调用close函数,那么会出现双方同时发送FIN报文的情况,就会出现CLOSING状态,表示双方都在关闭连接。

CLOSE_WAIT:表示被动关闭方等待关闭。当收到对方调用close函数发送的FIN报文时,回应对方ACK报文,此时进入CLOSE_WAIT状态。

LAST_ACK:表示被动关闭方发送FIN报文后,等待对方的ACK报文状态,当收到ACK后进入CLOSED状态。

1. 连接建立

1) Client

当Client端调用socket函数调用时,相当于Client端产生了一个处于Closed状态的套接字。

Client端又调用connect函数调用,系统为Client随机分配一个端口,连同传入connect中的参数(Server的IP和端口),这就形成了一个连接四元组,connect调用让Client端的socket处于SYN_SENT状态。

当Server返回确认,并发送SYN,Client返回确认及SYN后,套接字处于ESTABLISHED阶段,此时双方的连接已经可以进行读写操作。

2)Server

当Server端调用socket函数调用时,相当于Server端产生了一个处于Closed状态的监听套接字,Server端调用bind操作,将监听套接字与指定的地址和端口关联,然后又调用listen函数,系统会为其分配未完成队列和完成队列,此时的监听套接字可以接受Client的连接,监听套接字状态处于LISTEN状态。

当Server端调用accept操作时,会从完成队列中取出一个已经完成的client连接,同时在server这端会产生一个会话套接字,用于和client端套接字的通信,这个会话套接字的状态是ESTABLISH。

2. 连接关闭

与连接建立分为server/client不同,连接关闭并没有绝对的server/client之分,连接关闭分为主动关闭和被动关闭。Server和client都可以担任这两个角色中的任意一个。如client可以关闭它与server的连接,同样的server一样也可以关闭一些长时间无读写事件发生的连接。既然这么说了,下面就会分成两个部分:client主动关闭,server主动关闭。

Client主动关闭,Server被动关闭:

Client主动关闭,Server被动关闭的情况还是蛮多的,比如说短连接中,当一次会话结束时,client就可以关闭它与server之间的连接。

我们这边直接说close而非shutdown。

当client想要关闭它与server之间的连接,首先client这边会首先调用close函数,client端会发送一个FIN到server端,client端处于FIN_WAIT1状态。当server端返回给client ACK后,client处于FIN_WAIT2状态,server处于CLOSE_WAIT状态。

当server端检测到client端的关闭操作(read返回为0),server端也需要调用close操作,server端会向client端发送一个FIN。此时server的状态为LAST_ACK,当 client收到来自server的FIN后,client端的套接字处于TIME_WAIT状态,它会向server端再发送一个ack确认,此时server端收到ack确认后,此套接字处于CLOSED状态。

Server端主动关闭的流程与Client端关闭类似,就不再多讲,下面还需要关注的是TIME_WAIT这个状态,分别按照client/server两个部分讲述。

首先说一下TCP/IP详解中描述的关于TIME_WAIT的描述及其存在的必要性:

主动关闭的socket当收到对端的FIN操作后,该socket就会处于TIME_WAIT状态,处于TIME_WAIT状态的socket会存活2MSL(Max Segment Lifetime),

之所以存活这么长时间是有理由的:

一方面是可靠的实现TCP全双工连接的终止,也就是当***的ACK丢失后,被动关闭端会重发FIN,因此主动关闭端需要维持状态信息,以允许它重新发送最终的ACK。

另一方面TCP在2MSL等待期间,定义这个连接(4元组)不能再使用,任何迟到的报文都会丢弃。设想如果没有2MSL的限制,恰好新到的连接正好满足原先的4元组,这时候连接就可能接收到网络上的延迟报文就可能干扰***建立的连接。重复的分节在网络中消逝。

#p#3. Server端的监听套接字与会话套接字的不同:

当server端的socket调用bind和listen之后,监听套接字的状态就会变为LISTEN状态,监听套接字的工作决定了它只是监听连接。

当server端调用accept,相当于从套接字的完成队列中取出一个client的连接,此时可以确定四元组,即:client的IP和port;server的IP和port,双方各有一个套接字进行交互,这里需要说一下server端的socket,我称server端accept后产生的套接字为会话套接字,这个套接字一出生的状态就是ESTABLISH。由server端得四元组我们可以看出,一个server从理论上可以接收的连接数量取决于文件描述符的个数。

4,状态为TIME_WAIT是不是所有执行主动关闭的socket都会进入TIME_WAIT状态呢?

有没有什么情况使主动关闭的socket直接进入CLOSED状态呢?

主动关闭的一方在发送***一个 ack 后就会进入 TIME_WAIT 状态 停留2MSL(max segment lifetime)时间,这个是TCP/IP必不可少的,也就是“解决”不了的。也就是TCP/IP设计者本来是这么设计的。

主要有两个原因:

1。防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)

2。可靠的关闭TCP连接。在主动关闭方发送的***一个 ack(fin) ,有可能丢失,这时被动方会重新发 fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以 主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。

特别提示的是:为什么TIME_WAIT状态还需要等待2MSL才能回到CLOSED状态?或者为什么TCP要引入TIME_WAIT状态?

《TCP/IP详解》中如此解释:当TCP执行一个主动关闭,并发回***一个ACK后,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL,这样可以让TCP再次发送***的ACK以防止这个ACK丢失(另一端超时重发***的FIN)。

附注:MSL(Maximum Segment Lifetime)即***生存时间,RFC 793中指出MSL为2分钟,但是实现中的常用值为30秒、1分钟或者2分钟。

建立TCP连接(三次握手)

由于TCP协议提供可靠的连接服务,于是采用有保障的三次握手方式来创建一个TCP连接。三次握手的具体过程如下:

客户端发送一个带SYN标志的TCP报文(报文1)到服务器端,表示希望建立一个TCP连接。

服务器发送一个带ACK标志和SYN标志的TCP报文(报文2)给客户端,ACK用于对报文1的回应,SYN用于询问客户端是否准备好进行数据传输。

客户端发送一个带ACK标志的TCP报文(报文3),作为报文2的回应。

至此,一个TCP连接就建立起来了。

终止TCP连接(四次挥手)

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。原则是主动关闭的一方(如已传输完所有数据等原因)发送一个FIN报文来表示终止这个方向的连接,收到一个FIN意味着这个方向不再有数据流动,但另一个方向仍能继续发送数据,直到另一个方向也发送FIN报文。

四次挥手的具体过程如下:

客户端发送一个FIN报文(报文4)给服务器,表示我将关闭客户端到服务器端这个方向的连接。

服务器收到报文4后,发送一个ACK报文(报文5)给客户端,序号为报文4的序号加1。

服务器发送一个FIN报文(报文6)给客户端,表示自己也将关闭服务器端到客户端这个方向的连接。

客户端收到报文6后,发回一个ACK报文(报文7)给服务器,序号为报文6的序号加1。

至此,一个TCP连接就关闭了。(4次挥手不是关闭TCP连接的唯一办法,见下文Q3疑问)

TCP三次握手,四次挥手的时序图:

TCP三次握手,四次挥手的时序图:

TCP相关疑问

几个常见的TCP/IP相关的疑问:

Q1 为什么在TCP协议里,建立连接是三次握手,而关闭连接却是四次握手呢?

A1因为当处于LISTEN 状态的服务器端SOCKET当收到SYN报文(客户端希望新建一个TCP连接)后,它可以把ACK(应答作用)和SYN(同步作用)放在同一个报文里来发送给客户端。但在关闭TCP连接时,当收到对方的FIN报文时,对方仅仅表示对方没有数据发送给你了,但未必你的所有数据都已经全部发送给了对方,所以你大可不必马上关闭SOCKET(发送一个FIN报文),等你发送完剩余的数据给对方之后,再发送FIN报文给对方来表示你同意现在关闭连接了,所以通常情况下,这里的ACK报文和FIN报文都是分开发送的。

Q2为什么TIME_WAIT 状态还需要等2*MSL秒之后才能返回到CLOSED 状态呢?

A2因为虽然双方都同意关闭连接了,而且握手的4个报文也都发送完毕,按理可以直接回到CLOSED 状态(就好比从SYN_SENT 状态到ESTABLISH 状态那样),但是我们必须假想网络是不可靠的,你无法保证你(客户端)***发送的ACK报文一定会被对方收到,就是说对方处于LAST_ACK 状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT 状态的作用就是用来重发可能丢失的ACK报文。

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

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

责任编辑:林琳 来源: ChinaUnix博客
相关推荐

2015-10-13 09:42:52

TCP网络协议

2020-06-29 14:50:47

TCP状态ACK

2021-01-29 06:11:08

TCP通信三次握手

2021-05-18 12:27:40

TCP控制协议

2019-06-12 11:26:37

TCP三次握手四次挥手

2020-02-17 10:10:43

TCP三次握手四次挥手

2017-09-25 21:27:07

TCP协议数据链

2021-07-03 17:47:25

TCP控制协议

2021-05-28 09:08:20

TCP连接序列号

2018-08-10 09:23:19

TCP考点数据

2022-11-17 10:20:49

TCP三次握手四次挥手

2021-08-04 08:01:28

Linux 三次握手Linux 系统

2014-10-30 09:58:19

tcp

2019-12-13 07:31:04

TCP三次握手四次挥手

2020-03-02 14:41:04

运维架构技术

2021-04-30 13:32:17

TCP三次握手网络协议

2022-08-28 20:35:52

三次握手四次挥手TCP

2021-08-26 05:04:38

TCP网络HTTP

2020-02-13 21:30:23

TCP三次握手四次挥手

2022-08-27 13:50:44

TCP服务端函数
点赞
收藏

51CTO技术栈公众号