一文搞懂TCP数据报结构、三次握手、数据传输过程、丢包与解决方法、四次挥手

目录

1、TCP数据报结构

2、三次握手

3、TCP数据的传输过程

4、传输过程中数据包丢失与解决方法

4.1、丢包的情况

4.2、重传超时时间(RTO,Retransmission Time Out)

4.3、重传次数

5、四次挥手(TCP断开)


TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的通信协议,数据在传输前要建立连接,传输完毕后还要断开连接。

客户端在收发数据前要使用connect()函数和服务器建立连接。建立连接的目的是保护IP地址、端口、物理链路等正确无误,为数据的传输开辟通道。

TCP建立连接时要传输三个数据包,俗称三次握手(Three-way Handshaking)。可以很形象的比如为下面的对话:

  • [Shake 1] 套接字A:“你好,套接字B,我这里有数据要传送给你,建立连接吧。”
  • [Shake 2] 套接字B:“好的,我这边已准备就绪。”
  • [Shake 3] 套接字A:“谢谢你受理我的请求。

1、TCP数据报结构

一文搞懂TCP数据报结构、三次握手、数据传输过程、丢包与解决方法、四次挥手_第1张图片

带阴影的几个字段需要重点说明一下:

1) 序号:Seq(Sequence Number)序号占32位,用来标识从计算机A发送到计算机B的数据包的序号,计算机发送数据时对此进行标记

2) 确认号:Ack(Acknowledge Number)确认号占32位,客户端鸡儿服务端都可以发送,Ack = Seq + 1。

3) 标志位:每个指针占用一个Bit,共有6个,分别为URG、ACK、PSH、RST、SYN、FIN,具体含义如下

  •     URG:紧急指针(ungent pointer)有效
  •     ACK:确认序号有效(Acknowledge,确认)。
  •     PSH:接收方应该尽快将这个报文交给应用层。
  •     RST:重置连接。
  •     SYN:建立一个新连接(建立一个新的同步连接)。
  •     FIN:断开一个连接 (Finish,表示完成)

2、三次握手

一文搞懂TCP数据报结构、三次握手、数据传输过程、丢包与解决方法、四次挥手_第2张图片

客户端调用 socket() 函数创建套接字后,因为没有建立连接,所以套接字处于CLOSED状态;服务端调用 listen() 函数后,套接字进入LISTEN状态,开始监听客户端请求。

这个时候,客户端开始发起请求:

1) 当客户端调用connect()函数后,TCP协议会在客户端组建一个数据包,并设置SYN标志位,表示该数据包是用来建立新的同步连接的,同时生成一个随机数1000,填充“序号(Seq)”字段,表示该数据包的序号。完成这些工作,开始向服务端发送数据包,客户端就进入了SYN-SEND状态。

2) 服务器接收到数据包,检测到已经设置了SYN标志位,就知道这是客户端发来的建立连接的“请求包”。服务器端也会组建一个数据包,并设置SYN和ACK标志位,SYN表示该数据包用来建立连接,ACK用来确认刚才客户端发送的数据包。

服务器生成一个随机数2000,填充“序号(Seq)”字段。2000和客户端数据包没有关系。

服务器将客户端包的“SYN位”数据 加1,得到1001,并用这个数字填充“确认号(Ack)”字段。

服务器将数据包发出,进入SYN-RECV状态。

3)客户端收到数据包,检测到已经设置了SYN和ACK标志位,就知道这是客户端发来 的“确认包”。客户端会检测“确认号(Ack)”字段,看它的值是否为1000+1,如果是则说明创建新连接的请求对方接收成功。

接下来服务器会继续组建数据包,并设置ACK标志位,表示客户端正确接收了来自服务器发来的“确认包”。同时,将刚才服务器发来的数据包(2000)加1,得到2001,并用这个数字来填充“确认号(Ack)”字段。

客户端将数据包发出,进入ESTABLISHED状态,表示连接已经成功建立。

4) 服务器端收到数据包,检测到只设置了ACK标志位,就知道这是客户端发来的“确认包”,服务器会检测“确认号(Ack)”字段,看它的值是否为2000+1,如果是就说明客户端那边已经进入连接成功状态,服务器也随之进入EATABLISED状态。

至此,客户端和服务器都进入EATABLISED状态,连接建立成功,接下来就可以收发数据了。

三次握手的关键是要确认对方收到了自己的“序号(Seq)”字段。

3、TCP数据的传输过程

建立连接后,两台主机就可以相互传输数据了。如下图所示:

一文搞懂TCP数据报结构、三次握手、数据传输过程、丢包与解决方法、四次挥手_第3张图片

传输过程为:首先,主机A通过1个数据包发送100个字节的数据,数据包的“Seq号”字段设置为1200。主机B为了确认自己收到,想主机A发送ACK包,并将“确认号(Ack)”字段设置为1301.

为了确保数据准确到达,目标机器在收到数据包后必须立即回传ACK包,这样发送方才能确认数据传输成功。

此时Ack号为什么不是1200+1,而是1200+100,原因在于Ack号的增量为传输的数据字节数。假设每次Ack号不加传输的字节数,这样虽然可以确认数据包的传输,但是无法明确100字节全部正确传递还是丢失了一部分。

Ack号 = Seq号 + 传递的字节数 + 1

与三次握手协议相同,最后加1是为了告诉对方要传递的Seq号。

4、传输过程中数据包丢失与解决方法

4.1、丢包的情况

下面分析传输过程中数据包丢失的情况,如下图所示:

一文搞懂TCP数据报结构、三次握手、数据传输过程、丢包与解决方法、四次挥手_第4张图片

 

上图表示通过Seq1301数据包向主机B传递100字节的数据,但中间发生了错误,主机B未收到。经过一段时间后,主机A仍未收到对于Seq1301的ACK确认,因此尝试重传数据。

为了完成数据包的重传,TCP套接字每次发送数据包都会启动定时器,如果在一定时间内没有收到目标机器传回的ACK包,那么定时器超时,数据包会重传。

注意一下,上图演示的是数据包丢失的情况,也会有ACK包丢失的情况,一样会重传。

4.2、重传超时时间(RTO,Retransmission Time Out)

这个值太大会导致不必要的等待,太小会导致不必要的重传,理论上最好是网络RTT时间,但又受制于网络距离与瞬态时延变化,所以实际上使用自适应的动态算法(Jacobson算法和Karn算法)来确定超时时间。

RTT(Round-Trip Time)表示从发送端发送数据开始,到发送端收到来自接收端的ACK确认包,总共经历的时延。

4.3、重传次数

TCP数据包重传次数根据系统设置的不同而有所区别。有些系统,一个数据包只会被要求重传3次,如果重传3次后还未收到该数据包的ACK确认,就不再尝试重传。但有些要求很高的业务系统,会不断地重传丢失的数据包,以尽最大可能保证业务数据的正常交互。

最后需要说明的是,发送短只有在收到对方的ACK确认包之后,才会清空输出缓冲区中的数据。

5、四次挥手(TCP断开)

建立连接需要三次握手,彼此都要回一个ACK确认包,而断开连接需要四次挥手,可以形象的比喻为下面的对话:

  • [Shake 1] 套接字A:“任务处理完毕,我希望断开连接。”
  • [Shake 2] 套接字B:“哦,是吗?请稍等,我准备一下。”
  • 等待片刻后……
  • [Shake 3] 套接字B:“我准备好了,可以断开连接了。”
  • [Shake 4] 套接字A:“好的,谢谢合作。”

一文搞懂TCP数据报结构、三次握手、数据传输过程、丢包与解决方法、四次挥手_第5张图片

建立连接后,客户端和服务器都处于ESTABLISED状态。这时,客户端发起断开连接的请求:

1)客户端调用close()函数后,向服务器发送FIN数据包,进入FIN_WAIT_1状态。FIN是Finish的缩写,表示完成任务需要断开连接。

2)服务器收到数据包后,检测到设置的FIN标志位,知道要断开连接,于是向客户端发送“确认包”,进入CLOSE_WAIT状态。

注意:服务器收到请求后并不是立即断开连接,而是先向客户端发送“确认包”,告诉它我知道了,我需要准备一下才能断开连接。

3)客户端收到“确认包”后进入FIN_WAIT_2状态,等待服务器准备完毕后再次发送数据包。

4)等待片刻后,服务器准备完毕,可以断开连接,于是再主动向客户端发送FIN包,告诉它我准备好了,断开连接吧,然后进入LAST_ACK状态。

5)客户端收到服务器的FIN包后,再向服务器发送ACK包,告诉它你断开吧。然后进去TIME_WAIT状态。

6)服务器收到客户端的ACK包后,就断开连接,关闭套接字,进入CLOSED状态。

关于TIME_WAIT状态的说明

客户端最后一次发送ACK包后进入TIME_WAIT状态,而不是CLOSED状态关闭连接,这是为什么呢?

TCP是面向连接的传输方式,必须保证数据能够正确到达目标机器,不能丢失也不能出错,而网络是不稳定的,随时可能会毁坏数据,所以机器A每次向机器B发送数据包后,都要求机器B“确认”,回传ACK包,告诉机器我收到了,这样机器A才能知道数据传送成功了。如果机器B没有回传ACK包,机器A会重新发送,直到机器B回传ACK包。

客户端最后一次向服务器回传ACK包时,有可能因为网络问题导致服务器接收不到,服务器会再次发送FIN包,如果这时候客户端完全关闭了连接,那么服务器无论如何也收不到ACK包了,所以客户端需要等待片刻、确认对方收到ACK包后才能进入CLOSED状态。那么,新的问题来了,要等多久呢?

数据包在网络中是有生存时间的,超过这个时间还未到达目标主机就会被丢弃,并通知源主机。这称为报文最大生存时间(MSL,Maximum Segment LifeTime)。TIME_WAIT需要等待两个2MSL才会进入CLOSED状态。ACK包达到服务器需要MSL,服务器重传FIN包需要MSL时间,2MSL是数据包往返的最大时间,如果2MSL后还未收到服务器重传的FIN包,就说明服务器已经收到了ACK包,客户端就可以进入CLOSED状态啦。

你可能感兴趣的