最近学习Sokect编程时,突然想到计算机网络中学到的TCP的十一种状态图,因此决定好好复习一下TCP协议中比较容易朗朗上口却又不容易搞得清楚地三次握手和四次挥手的过程。
先给大家拿出来教科书上经常展示的TCP十一种状态图,接下来一步一步说清楚这十一种状态的实际场景和如何转化。
1.三次握手
TCP协议是基于IP协议进行封装的,也就是说TCP包是基于IP报文进行了封装,这点很像Java下IO包,基于装饰者模式,每次都加一些同一层软件能理解的信息进去,熟悉计算机网络应该明白我的意思,因为IP报文还是基于以太帧进行封装的,而TCP上层应用也会有自己的协议进行封装。
总之在传输层使用TCP协议建立连接,那么这个连接的建立和结束就是靠TCP这一套体系进行控制的(其实所谓的协议就是一种控制规范)。
而TCP连接的建立,就是依靠三次握手来确保两端建立了可靠地连接。
第一次握手:建立连接时,客户端发送
syn
包(syn=j)到服务器,并进入SYN_SENT
状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。第二次握手:服务器收到
syn
包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN
包(syn=k),即SYN+ACK
包,此时服务器进入SYN_RECV
状态;第三次握手:客户端收到服务器的
SYN+ACK
包,向服务器发送确认包ACK
(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED
(TCP连接成功)状态,完成三次握手。
HOST A此时作为客户端,主动发送SYN
到服务器,从CLOSED
状态转换为SYN_SENT
的过程被称为主动打开。
HOST B此时作为服务器端,从CLOSED
状态进入LISTEN
的状态被称为被动打开。
三次握手阶段涉及到的TCP状态有:
服务器端:CLOSED(关闭)->LISTEN(监听)->SYN_RCVD(收到客户端SYN包,回复SYN+AKC)->ESTABLISHED(收到客户端ACK确认以后进入连接状态,可以和客户端进行通信)
客户端:CLOSED(关闭)->SYN_SENT(主动打开,发送SYN包,等待服务端相应)->ESTABLISHED(收到SYN+ACK并回复ACK以后进入连接状态)
2.四次挥手
TCP连接结束的时候,需要进行四次挥手(四次收发包)确认后才会关闭连接。
第一次挥手:客户端向服务端发送
FIN
包,表示客户端请求断开连接,此时客户端由ESTABLISHED
状态进入FIN_WAIT_1
状态。第二次挥手:服务端收到客户端的
FIN
包,回复ACK
包,此时服务端也由ESTABLISHED
状态进入CLOSED_WAIT
状态。第二次挥手和第三次挥手之间:因为双方已经都知道了对方要离开的心意,所以要交代完全部的工作,也就是说,服务器要将客户端的数据全部传输到客户端以后,才会进行下一次挥手,客户端收到服务器端的
ACK
回复以后,与服务器进行最后的数据传输,而这段数据传输的时间,在客户端处于FIN_WAIT_2
状态。第三次挥手:服务器端传输完数据以后,发送
FIN+ACK
包,同时自己进入LAST_ACK
状态。第四次挥手:客户端收到服务器的
FIN+ACK
包,回复ACK
包确定连接的结束。而后自己进入一个TIME_WAIT
的等待时间,等待结束后客户端关闭连接,而服务器端收到客户端回复的ACK
包以后就会关闭这个连接。
可以看到四次挥手比三次握手多了一个流程,因为虽然双方都收到来自对方的关闭消息以后,却并不能直接结束该连接,因为实际应用中有些数据(比如在缓冲区中)还没有由操作系统安排进网络传输中,所以TCP会安排一次数据传输的间隔(FIN_WAIT_2
或者CLOSED_WAIT
,实际上两者是一个时间段),这种机制也是为了确保TCP传输的可靠性。
同时客户端有一个自己的等待时间(TIME_WAIT
状态),这个等待时间是2MSL(两倍的最大报文段生存时间),原因是因为由于网络环境原因,客户端发送的ACK包可能会丢失,在TIME_WAIT
状态下可重发ACK
包。