TCP协议之三次握手与四次挥手
TCP协议是TCP/IP体系中核心一个协议,该协议比起IP协议,ICMP协议,UDP协议都更复杂,因此这篇文章主要分析TCP协议在建立连接和断开连接的时候,状态转移以及报文段的内容。
下面,先放一张TCP的状态转移图:
TCP协议之三次握手
三次握手的过程是TCP在客户端和服务端建立连接的过程。简单的来说三次握手过程,就是客户端先发送一个连接请求给服务端,这是第一次握手。服务端接收到客户端发来的链接请求,然后在将确认的消息发给客户端,这是第二次握手。客户端对服务端发来的确认消息进行确认,然后将确认的消息发给服务端,这就是三次握手。三次握手之后,链接建立。
为什么一定是三次握手才能建立一个可靠地链接?如果不是三次握手,那么在客户端发送了链接请求之后,服务端对这个请求进行确认,就认定这次的链接已经成功建立,俗称的二次握手。这样的方式的弊端在哪里?
考虑这么一种情况,当客户端进行第一次握手时,发送了一个报文段,但是这个报文段因为网络的问题,迟迟没有到,这时,客户端又会再一次发送一个连接请求的报文段给服务端,这次成功接收,两者建立连接,并通信结束,关闭连接。这之后,因为网络延迟的那个报文段传到了服务端那里,服务端又以为客户端要建立新的连接,于是就同意了,向客户端发送确认。因为是二次握手,所以服务端后续要做的事情,就是等待客户端发送的消息,但是客户端是不会理会服务端传来的确认,所以服务端就会一直在等待客户端的数据,白白浪费了资源。
抓包分析TCP三次握手的报文段
现在,详细说一下三次握手具体是做了什么。
第一次握手,客户端发送一个报文段给服务端,该报文段中标志有SYN标志,该标志表示建立连接,以及一个初始的序列号。
第二次握手时,服务端发送一个报文段给客户端,该报文段中标志有SYN标志,和ACK标志。ACK标志的值是客户端发来的初始序号值+ 1,表示对客户端进行确认,报文段中还有服务端自己的初始序号。
第三次握手时,客户端发送一个报文段给服务端,该报文段中标志只有ACK标志,该ACK值是服务端的初始序列化的值 + 1,表示对服务端进行确认,以及还有一个序号值,该序号值为客户端第一次握手时的序号值 + 1。
三次握手建立连接结束。
图片上共有三行,每一行代表一次握手。我们先看第一行
可以看出,第一次握手时55732端口的程序主动发送一个建立链接的报文段给8888端口。这个55732端口是Java程序写的一个Socket客户端,8888端口是Socket程序写的服务端。建立连接的报文段,Flags中,只有SYN是为1的,这表明这是一个建立连接的报文段,该报文段中初始的序列号为0,ACK的number也为0。
第二次握手,是服务端发送给客户端一个报文段,表示确认收到了客户端的链接请求。该报文段中标志位有两个一个是ACK,一个SYN。表示收到链接请求,端口开放。该报文段中也会发送一个服务端自己的初始序列号。注意,这里ACK的值变成了1,是客户端初始序列号 + 1才有的。
第三次握手,是客户端发送给服务端一个报文段,表示确认收到了服务端的确认。因为双方端口都已经打开,所以客户端在发,就不会再有SYN标志了,这里只有ACK标志,该标志的值也是1,是因为服务端的初始序列号 + 1造成的。这里的序列号为1,是因为第一次握手,发送了一个SYN标志,该标志会占用1个序号值,但是ACK不会。
对应到上面的状态图中,就是客户端是主动打开,从起始点发送SYN报文段,进入SYN_SENT状态,然后接受SYN,ACK,走黑粗线的路径进入到数据传输状态,也就是ESTABLISHED,对于服务端而言,就是从起始点走虚线的部分,被动打开后,接受客户端的SYN,进入SYN_RCVD,最后,接受客户端第三次握手的ACK,进入数据传输状态,也就是ESTABLISHED。
TCP协议之四次挥手
TCP协议是一种全双工协议,拥有半关闭的特性。
全双工的意思是A可以往B发送数据,B同时也可以给A发送数据。
半双工的意思是A可以往B发送数据,B也可以给A发送数据,但是两者不能同时。
单工的意思是只能A给B发送数据,或者B给A发送数据。
半关闭的意思是将全双工,变成单工,也就是如果B关闭,是指B不能给A发数据了,但是A可以给B发
所以TCP协议如果要正常的关闭客户端与服务端的链接,需要四次报文段,也就是4次挥手。
首先,客户端因为应用程序的执行完毕,会主动开始断开链接,这时会发送一个含有FIN标志位的报文段。这表明客户端不会再发送数据给服务端。这时第一次挥手。
然后,服务端接收到这个报文段,就会发送一个含有ACK标志的报文段给客户端,表示确认收到了关闭的报文段。这是第二次挥手。
然后,服务端在处理完服务端的事情后,也会发送一个含有FIN的报文段给客户端,表示服务端不会再发送数据给客户端。这是第三次挥手。
最后,客户端收到这个报文段后,就会发送一个含有ACK的报文段给服务端,表示确认收到了关闭的报文段。这是第四次挥手。至此,链接全部关闭。
抓包分析TCP四次挥手的报文段
不用看黑色报文,4行代表关闭的4次挥手的过程,每一行是一次挥手。
第一行,是客户端主动关闭链接,发送一个含有FIN的报文段,这是第一次挥手
第二行,是服务端确认客户端的FIN报文,发送一个ACK的确认报文,该ACK的值是9,而第一行的序列号的8,因为与SYN一样,FIN也占一个序列号。
忽略黑的后第三行,服务端发送一个含有FIN的报文段,这是第三次挥手。
第四行,客户端会这个FIN报文段进行确认,发送了一个ACK报文段。该ACK的值是第三行的序列号 + 1。
对应到上面的状态图中,先说服务端的状态转移,因为收到了客户端的FIN,所以发送一个ACK给客户端,同时自己进入到CLOSE_WAIT状态,等待服务端应用程序结束,发送FIN给客户端,自己进入LAST_ACK状态,等待最后的ACK到来,接收到ACK,结束状态。
客户端因为先发起关闭,状态比较复杂,他先发送一个FIN给服务端,自己进入了FIN_WAIT_1状态,这时他等待接收服务端的报文,该报文会有三种可能:
- 只有服务端的ACK
- 只有服务端的FIN
- 基于服务端的ACK,又有FIN
对于第一种,该ACK是服务端确认了客户端的FIN而发的,这时客户端会进入FIN_WAIT_2状态,这是当他收到服务端的FIN来时,发送了一个ACK,会进入到TIME_WAIT状态,他要在这个状态等待2MSL的时间,1个MSL是报文段在网络的最长时间,客户端等待2MSL,是为了当最后一个ACK丢失时,可以在发送一次,因为这时,服务端在等待超时后在发送一个FIN给客户端,所以客户端也知道了ACK丢失了。
对于第二种,只有服务端的FIN的时,会发送一个ACK给服务端,客户端进入CLOSING状态,然后接收到服务端的ACK时,也会进入TIME_WAIT状态。
对于第三种,同时都收到了,就省略了进入CLOSING状态,直接进入TIME_WAIT状态。抓包分析的截图,就是这种情况。