TCP协议三次握手、数据收发、四次挥手,及抓包分析

以下分析,均在linux环境下进行抓包;
抓包命令:tcpdump -i any port 端口 -s 0 -w 文件,使用root用户进行抓包;
并用wireshark进行包分析。
以下为博主机器下的抓包信息:
在这里插入图片描述

1.三次握手

1.1建立握手

TCP协议三次握手、数据收发、四次挥手,及抓包分析_第1张图片

三次握手连接的步骤:

  1. 首先,客户端先对服务端发起SYN包(请求建立连接)(seq = 0),发送完毕之后,客户端将自己状态设置为SYN_SENT状态;
  2. 服务端收到客户端的连接请求,将自己状态设置为SYN_RCVD,并且给客户端发送SYN包(请求建立连接),ACK包(该包有两层含义:①当前服务端收到了客户端发来的SYN包(计算该包的大小),②下一次客户端给服务端发送数据包的时候,包序为该ack的值)(seq = 0, ack = 1);
  3. 客户端收到服务端发来的SYN + ACK数据包之后,表明建立连接成功了,将自身状态设置为ESTABLISHED状态,并给服务端发送ACK数据包(该数据包中,①包含客户端下一次发送的包序,②服务端下一次给客户端发送数据时的包序)(seq = 1, ack = 1),服务端将状态设置为ESTABLISHED。

包序:在客户端和服务端中,各存在着一套包序。建立连接时,SYN(seq = 0),表明当前客户端(服务端)的包序是从0开始的,则后面发送数据包时,其包序应该大于当前的包序(seq = 0),包序也不一定是从0开始的。

ACK数据包不消耗包序,是用来确认发送的数据包是否被接收到,如果在一段时间内发送数据方没有收到ACK数据包,则会对数据进行重发。

为什么客户端还要发送一次ACK确认数据包呢?
主要是为了防止已失效的连接请求数据包(SYN)突然又传到了服务端;

  1. 正常情况:客户端发起连接请求,但因连接请求数据包(SYN)丢失而没有收到确定数据包(ACK),于是客户端再重传一次连接请求(SYN),后续收到了确认数据包(ACK),建立了连接。数据传输完毕之后就释放了连接,客户端发送了两次连接请求数据包,第一个丢失,第二个抵达了服务端,没有“已失效的连接请求数据包”。

  2. 异常情况:客户端发出的第一个连接请求数据包(SYN)并没有丢失,而是在某些网络结点长时间滞留了,以致延误到连接释放以后的某个时间才到达服务端,这是一个已经失效的数据包(SYN),但服务端收到此失效的连接请求数据包(SYN)后,误认为客户端又发起了一次新的连接,于是向客户端发出了确认数据包(ACK),同意建立连接。如果不采用三次握手,那么服务端发出确认数据包(ACK)后,新的连接就建立了;

  3. 但是目前客户端并没有发起连接请求(SYN),则不会对服务端的确定数据包(ACK)进行确认,也不会对服务端发送数据。但服务端却以为新的连接已经建立,并一直等待客户端发送数据,服务端的资源就被白白浪费了;

  4. 三次握手,就可解决上面的问题,客户端不会向服务端发送确认数据包(ACK),服务端由于收不到确认,就知道客户端并没有请求建立连接。

1.2 抓包分析

在这里插入图片描述
客户端对服务端发起连接。

				源端口			目的端口			发送数据包					数据长度
客户端			37828			9999			  SYN	seq = 0  			len = 0
服务端			9999			37828			SYN ACK seq = 0 ack = 1 	len = 0
客户端			37828			9999			ACK seq = 1 ack = 1			len = 0

连接请求数据包SYN不发送数据,但消耗一个包序。

2.数据收发

TCP协议三次握手、数据收发、四次挥手,及抓包分析_第2张图片
三次握手连接建立后,就可以正常的数据收发。
上图中,客户端先给服务端发送数据(nihaoa),该数据长度为6个字节;自三次握手建立之后,客户端维护的seq序列为1,则服务端给客户端确认应答时,ack = 1 + 6 = 7;

服务端再给客户端发送数据(wohenhao),该数据长度为8个字节;自三次握手建立之后,服务端维护的seq序列为1,则客户端给服务端确认应答时,ack = 1 + 8 = 9;

在这里插入图片描述

				源端口			目的端口			发送数据包					数据长度
客户端			37828			9999			PSH ACK	seq = 1 ack = 1  	len = 6
服务端			9999			37828			ACK seq = 1 ack = 7 		len = 0
服务端			9999			37828			PSH ACK	seq = 1 ack = 7  	len = 8
客户端			37828			9999			ACK seq = 7 ack = 9			len = 0

3.四次挥手

TCP协议三次握手、数据收发、四次挥手,及抓包分析_第3张图片
FIN数据包不发送数据,但消耗一个包序。
四次挥手步骤:

  1. 客户端先发起断开连接,客户端状态变为FIN_WAIT_1,序列号为前面数据收发之后的序列号;

  2. 服务端收到连接释放请求,将状态设置为CLOSE_WAIT,并对释放请求进行应答,ACK(seq = 9, ack = 8);此时TCP连接处于半关闭状态,即客户端已经没有要发送的数据了,但若是服务端要发送数据,客户端仍需要接收,服务端到客户端这个方向的连接并未关闭,可能会持续一段时间。

  3. 客户端收到服务端的ACK应答后,就进入FIN_WAIT_2状态,等待服务端发出的连接释放请求;

  4. 若服务端没有向客户端发送的数据了,则服务端发送FIN数据包,并将状态设置为LAST_ACK,等待客户端的确定,然后断开连接;

  5. 客户端收到服务端的释放请求后,必须对该请求进行应答,发送ACK数据包,并且进入到TIME_WAIT状态。在TIME_WAIT状态下,TCP连接还没有释放掉,必须经过时间等待计时器(TIME-WAIT timer)设置的时间2MSL后,客户端才进入到CLOSED状态;

  6. MSL:最长报文段寿命,RFC793建议设置为2分钟,TCP允许根据实际情况设置更小的MSL值,因此从客户端进入到TIME_WAIT状态之后,要经过4分钟才能进入到CLOSED状态,才能开始建立下一个新的连接。

为什么客户端在TIME_WAIT必须等待2MSL的时间?

  1. 为了保证客户端最后一次发送的ACK数据包能够到达服务端,这个ACK数据包很可能会丢失,则服务端(LAST_ACK状态)接收不到对方的ACK应答,服务端就会超时重传这个FIN数据包,而客户端在2MSL时间内能收到这个FIN数据包,接着客户端重传一个ACK应答,重新启动2MSL计时器,最后客户端和服务端都会进入到CLOSED状态。如果不这样,服务端就无法按照正常步骤进入CLOSED状态;
  2. 防止“已失效的连接请求数据包”出现在本连接中,客户端在发送完最后一个ACK应答后,再经过2MSL时间,就可以使本连接持续时间内产生的所有数据包都消失,这样就可以使下一个新的连接种不会出现这种旧的连接请求数据包。

客户端先断开连接,但是客户端要等待2MSL时间,所以一般情况下,都是客户端先断开连接,服务端作为被断开方,可节约服务端的资源。

3.1 抓包分析

在这里插入图片描述
这是博主机器上抓的,只有三次挥手。

分析为什么只有三次挥手:

  1. ACK作为TCP协议的头部,则无论是否有数据发送,ACK都是存在的;
  2. 客户端先发送一个FIN ACK数据包,代表此时客户端没有数据要发送了,需要断开连接了;
  3. 服务端此时,也没有数据需要发送,则不单独发送一个ACK应答,服务端也直接进行断开连接请求(FIN) + ACK ,这里的ACK也就直接应答了客户端的FIN请求;
  4. 客户端对服务端的断开连接请求进行应答(ACK)。
				源端口			目的端口			发送数据包					数据长度
客户端			37828			9999			FIN ACK	seq = 7 ack = 9  	len = 0
服务端			9999			37828			FIN ACK	seq = 9 ack = 8  	len = 0
客户端			37828			9999			ACK seq = 8 ack = 10		len = 0

你可能感兴趣的:(linux学习,网络,linux,socket,wireshark,C++)