三次握手:
第一次
A发送连接请求报文段。首部中的同步位SYN=1,同时选择一个初始序号seq=x。此时A进入SYN_SENT状态。第二次
B收到连接请求报文段后,如果同意建立连接,则向A发送确认。
在确认报文段中把SYN和ACK都置1,确认号ack为x+1,同时自己也要喧杂一个初始序号seq = y。B进入到SYN_RCVD状态;第三次
A收到B的SYN+ACK包,还要向B发出确认,ACK置1,确认号ack=y+1,自己的序号seq为x+1。
此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
为什么要三次握手?
也就是为什么Client还要发出一次确认呢?主要是防止已失效的连接请求突然又传送到了Server。比如说Client发出了一个连接请求,但是这个请求报文在某些网络节点滞留了,这时Client会再发出一个连接请求,从而建立连接、数据传输然后释放连接。但是那个滞留的请求报文可能在某个时间到达了Server,Server误以为这时Client又发出一次新的连接请求,于是就向Client发出了确认报文,同意建立连接。
假设不采用三次握手,那么只要Server发出确认就会建立新的连接。但是由于Client此时并没有真的发出建立连接的请求,因此不会理睬Server的确认也不会向其发送数据,但Server却认为新的连接已经建立了并一直等待Client发来数据,此时Server的很多数据就这么浪费了。
采用三次握手的话可以防止上面现象的发生,例如在刚才的情况下Client不会向Server发出确认,Server由于收不到确认就知道Client没有要求建立连接,就不会傻傻等着了。
四次挥手:
A首先发出连接释放报文段,并停止发送数据,主动关闭TCP连接。
A将FIN置1,其序号seq = u,等于前面已传送过的数据的最后一个字节的序号加1,此时A进入FIN_WAIT_1状态。B收到连接释放报文后即发出确认,ACK置1,确认号ack=u+1,而这个报文段自己的序号是v,等于前面已传送过的数据的最后一个字节的序号加1。然后B进入到CLOSE_WAIT状态。
这时从A到B这个方向的连接就释放了,TCP连接处于半关闭状态,即A已经没有数据要发送了,但若B还要发送数据,A仍要接收。
A在收到B的确认后,就进入到FIN_WAIT_2状态。
若B已经没有要发送的数据了,就发送连接释放报文。FIN置1,序号seq为w,B还必须要重复上次已发送过的确认号ack=u+1。此时B进入LAST_ACK状态。
A收到B的连接释放报文后,要对此发出确认,ACK置1,确认号ack = w+1,自己的序号为seq = u+1。
然后会进入TIME_WAIT状态。现在TCP连接还没有被释放掉,必须经过时间等待计时器设置的时间2MSL后A才进入CLOSED状态。MSL为最长报文段寿命,一般为2分钟。
为什么Client必须要等到2MSL时间呢?有两个原因:
为了保证Client最后发出的ACK报文能够到达Server。
这个ACK报文段可能丢失,此时Server收不到确认就会超时重传之前的FIN报文段,Client就可以在2MSL时间内收到这个重传的FIN报文,从而重传一次ACK确认,重新启动2MSL。
如果Client不在TIME_WAIT状态等待一段时间而是在发送完ACK后立即关闭连接,那么就无法收到Server重传的FIN报文段,Server就不会按正常步骤进入CLOSED状态。防止“已失效的连接请求报文段”出现。
和三次握手中防止的已失效的连接请求报文段的情况相同,Client在发送完最后一个ACK后,再经过2MSL时间,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。