注:主动、被动与服务器、客户端没有明确的对应关系。
每个连接均开始于CLOSED状态。当一方执行了被动的连接原语(LISTEN)或主动的连接原语(CONNECT)时,它便会脱离CLOSED状态。如果此时另一方执行了相对应的连接原语,连接便建立了,并且状态变为ESTABLISHED。任何一方均可以首先请求释放连接,当连接被释放后,状态又回到了CLOSED。
CLOSED: 表示初始状态。
LISTEN: 表示服务器端的某个SOCKET处于监听状态,可以接受连接了。
SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat很难看到这种状态的,除非特意写一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此该状态下收到客户端的ACK报文后,它会进入到ESTABLISHED状态。
SYN_SENT: 表示客户端已发送SYN报文,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。
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: 表示双方都正在关闭SOCKET连接,属于一种比较罕见的例外状态。正常情况下,当发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示发送FIN报文后,并没有收到对方的ACK报文,反而却收到了对方的FIN报文。如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,即出现CLOSING状态。
CLOSE_WAIT: 表示在等待关闭。当对方close一个SOCKET后发送FIN报文给自己,你毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来真正需要考虑的事情是查看是否还有数据发要送给对方,如果没有的话,那么就可以close这个SOCKET,发送FIN报文给对方,即关闭连接。所以在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。
关闭的状态转换:假设通信双方是A,B;A主动发起关闭
(1)主动发起关闭方
A首先主动发起FIN报文,准备关闭TCP连接,然后进入FIN_WAIT1状态;
如果A收到了ACK报文,进入FIN_WAIT2状态,说明B还有数据发给A。不久之后,B发送FIN给A,然后,A发送ACK,并进入TIME_WAIT状态。
如果A收到到了ACK + FIN,A直接进入TIME_WAIT状态。
经过2个MSL,没有收到FIN信号,那么TIME_WAIT就自动转化为CLOESD。
(2)被动接收方
B在收到A的FIN报文后,知道A准备关闭 TCP连接了(注意只是A单方面关闭发数据的连接,也就是说A还可以接收数据)。B将发送ACK给A,进入CLOSED_WAIT状态。如果此时B也有数据发送给A,那么就一直发送好了,反正A不会发数据了。此时A处于FIN_WAIT2状态。当B的数据发送完毕之后,那么B发送FIN给 A,B进入LAST_ACK状态,当收到A发过来的ACK信号后,A进入CLOSED状态。
为什么TIME_WAIT状态要进过2个MSL(Maximum Segment Lifetime.)才能进入CLOSED状态?
由于网路存在不可靠的因素,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,重发可能丢失的ACK报文的过程最多需要2MSL,所以如果过了2MSL,没有再次收到B的FIN,那么说明之间A发出的ACK被B收到了,因此可以可靠关闭连接。
同时打开
两个应用程序同时执行主动打开的情况是可能的,虽然发生的可能性较低。
每一端都发送一个SYN,并传递给对方,且每一端都使用对端所知的端口作为本地端口。
例如:主机A中一应用程序使用77作为本地端口,并连接到主机B 99端口做主动打开。
主机B中一应用程序使用99作为本地端口,并连接到主机A 77端口做主动打开。
TCP协议在遇到这种情况时,只会打开一条连接。
这个连接的建立过程需要4次数据交换,而一个典型的连接建立只需要3次交换(即3次握手)
但多数伯克利版的TCP/IP实现并不支持同时打开。
SYN_RCVD与SYN_SEND都是转换为ESTABLISHED的中间状态,目标是两端均转换到ESTABLISHED状态。
同时关闭
如果应用程序同时发送FIN,则在发送后会首先进入FIN_WAIT_1状态。在收到对端的FIN后,回复一个ACK,会进入CLOSING状态。在收到对端的ACK后,进入TIME_WAIT状态。这种情况称为同时关闭。
同时关闭也需要有4次报文交换,与典型的关闭相同。
TCP三次握手建立连接&&四次握手关闭连接
1、建立连接协议(三次握手)
(1)建立连接时,客户端A发送SYN包(seq=J)到服务器B,并进入SYN_SENT状态,等待服务器B确认。
(2) 服务器B收到SYN包,必须确认客户A的SYN(ACK=J+1),同时自己也发送一个SYN包(seq=k),即SYN+ACK包,此时服务器B进入SYN_RCVD状态。
(3) 第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。
至此3次握手结束,TCP连接就建立起来了,然后客户端与服务器开始交互数据。上面描述过程中,SYN包表示TCP数据包的标志位syn=1,同理,ACK表示TCP报文中标志位ack=1,SYN+ACK表示标志位syn=1和ack=1同时成立。
2、终止连接协议(四次握手)
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接,
收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
(1) TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送。
(2) 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1,和SYN一样,一个FIN将占用一个序号。
(3) 服务器关闭客户端的连接,发送一个FIN给客户端。
(4) 客户段发回ACK报文确认,并将确认序号设置为收到序号加1。
为什么建立连接协议是三次握手,而关闭连接却是四次握手?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你未必会马上会关闭SOCKET,即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
为什么三次握手能可靠地建立连接?
在建立连接的时候,发起方发出请求(即发出SYN报文),接收方收到后必须给发起方一个回应(ACK+SYN)
(a)如果发起方没有收到接收方的ACK+SYN报文,说明报文丢失,必须重发请求;
如果发起方收到ACK+SYN报文
(b)不向接收方发出ACK信号(也就是只有两次握手,此时开始传递报文),那么从接收方的角度来看,它认为自己发出的ACK+SYN报文丢失了,发起方没有收到,所以它会再次重发。
(c) 向接收方发出ACK信号,没有收到接收方再次发来的ACK+SYN报文,那么说明接收方收到了自己发出的ACK报文,所以可以建立连接了。
举个例子保障可靠通信:
现在A和B两支军队准备在10:00同时发起进攻,如何才能保证这两支军队同时呢?
现在A发出电报,“要求在10:00同时发起进攻”;B收到后必须向A回复【收到,可以】
A在收到【收到,可以】的消息后,知道B已经收到了自己发出的消息。
站在B的角度去思考:我发出了【收到,可以】的消息,A必须要给我一个回应。如果我没有收到回应,那么就说明我的这个消息没有送到A那里去,那么就得再传一次【收到,可以】的消息给A。
所以A必须再次发出【OK】消息给B,此时,如果A没有再次收到B发来的【收到,可以】的消息,就可以认为自己的【OK】已经送给了B;
而如果B收到A发来的【OK】消息,说明自己此前发出的【收到,可以】已经被A收到。这样可以保证可靠通信。