在说TCP/IP的三次握手和四次挥手时,先对TCP协议做一个简单的介绍。并对TCP报文段首部的6个控制(URG、ACK、PSH、RST、SYN、FIN)位进行一个详细的介绍。
# TCP协议
TCP协议是一种面向连接的, 稳定可靠的协议, 会负责做数据的检测, 分拆和重新按照顺序组装, 自动重发等
紧急URG(URGent)
当URG=1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据),而不要按原来的排队顺序来传送。
当URG置1时,发送应用进程就告诉发送方的TCP有紧急数据要传送。于是发送方TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍是普通数据。这时要与手不中紧急指针(Urgent Pointer)字段配合使用。
确认ACK(ACKnowledgment)
仅当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。
推送PSH(PuSH)
当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能够收到对方的响应。这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快的(即“推送”向前)交付接收应用进程,而不再等到整个缓存都填满了后再向上交付。
复位RST(ReSeT)
当RST=1时,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立运输连接。RST置1还用来拒绝一个非法的报文段或拒绝打开一个连接。RST也可称为重建位或重复位。
同步SYN(SYNchronization)
在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1。因此,SYN置1就表示这是一个连接请求或连接接受报文。
终止FIN(FINis)
用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。
# 三次握手
所谓三次握手(Three-WayHandshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包确认连接的建立。在Scoket,这一过程由客户端执行Connect来触发,整个流程如下:
第一次握手:
Client将标志位SYN设置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
第二次握手:
SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都设置为1,ack=J+1,随机产生一个seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
第三次握手:
Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK设置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED(连接)状态,完成三次握手,随后Client和Server之间可以开始传输数据了。
# 为什么是三次握手
首先,我们要知道TCP是全双工的,即客户端在给服务器端发送信息的同时,服务器端也可以给客户端发送信息。而半双工的意思是A可以给B发,B也可以给A发,但是A在给B发的时候,B不能给A发,即不同时,为半双工。 单工为只能A给B发,B不能给A发; 或者是只能B给A发,不能A给B发。
我们假设A和B是通信的双方。握手实际上就是通信,发一次信息就是进行一次握手。
1. 如果是2次握手:
2. 如果是4次握手:
# 四次挥手
所谓四次挥手(Four-Way- Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发4个包以确认连接的断开。在Socket编程中,这一过程由客户端或服务端任一方执行close来触发,整个流程如下图所示:
由于TCP连接时是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭
第一次挥手:
Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
第二次挥手:
Server收到FIN后,发送一个ACK给Client,确认序列号为收到序号+1(与SYN相同,一个FIN占用一个序列号),Server进入CLOSE_WAIT状态。
第三次挥手:
Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
第四次挥手:
Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序列号为收到序号+1,Server进入CLOSED状态,完成四次握手。
上面是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况,具体流程如下图
# 挥手为什么要四次
因为TCP连接是全双工的网络协议,允许同时通信的双方同时进行数据的收发。同样也允许收发两个方向的连接被独立关闭,以避免Client数据发送完毕,向Server发送FIN关闭连接,而Server还没有收到Client端数据的情况。所以关闭TCP连接需要四次挥手,每次关闭一个方向上的连接需要FIN和ACK两次握手
# TIME_WAIT状态的意义
在TCP连接中,主动关闭连接的一方发送的FIN报文到达时,被动关闭连接的一方会发送ACK确认报文,并且进入TIME_WAIT状态,并且等待2MSL时间段(MSL:maximum segment life)。这么做有一下两个原因
1. 被动关闭连接的一方(图中的Server)在一段时间内没有收到对方的ACK确认数据包,会重新发送FIN数据包,因而主动关闭连接的一方需要停留在等待状态以处理对方重新发送的FIN数据包。否则他会回应一个RST数据包给被动关闭连接的一方,使得对方莫名其妙。
2. 在TIME_WAIT状态下,不允许应用程序在当前IP和端口上以及之前通信的Client(这个Client的IP和端口号不变)建立一个连接。这样就能够避免新的连接收到之前的IP和端口号一致的连接残存在网络中的数据包。这也是TIME_WAIT状态的等待时间被设置为2MSL的原因,以确保网络上当前连接两个方向上尚未接收的TCP报文已经全部消失
总结:
在三次握手的过程中,SYN和ACK是一起发送的,但是在四次挥手的时候,FIN和ACK却不是一起发送的,而是分开发送的。为什么呢???那是因为啊,TCP连接是全双工的,也就是说接收到FIN只是说没有数据在发送过来了,但是还是可以发送数据的,也就是接收到一个FIN只是关闭一个方向的数据传输,另一个方向还可以继续发送数据。在四次挥手的时候也是这样。前两次挥手只是确认关闭了一个方向的数据,加上后面两次挥手才真正的关闭整个全双工连接。
当Socket在ESTABISHED(连接)状态时,他想主动关闭连接,于是向对方发送FIN请求,发送完FIN请求后,它处于FIN_WAIT_1状态,当对方确认ACK报文后则处于FIN_WAIT_2状态。FIN_WAIT_2表示半连接,也就是有一方要求关闭连接,另一方收到请求但是告诉它我还有一些数据要发送,稍后会关闭。TIME_WAIT状态表示收到对方的FIN并发送出ACK。如果是三次挥手,可能在关闭后还有一个方向没有关闭。