Alamofire前奏之TCP三握四挥

1,293 阅读6分钟

建立TCP需要三次握手才能建立,而断开连接则需要四次挥手。

一、TCP三次握手🤝

TCP有6种标识:SYN(建立联机)、ACK(确认)、PSH(传送)、FIN(结束)、RST(重置)、URG(紧急)

上图所示即为TCP三次握手的请求示意图。

1、第1次握手

客户端向服务端发送连接请求报文。这时,报文首部中的同步序号SYN=1,同时随机生成初始序列号seq=x。此时,TCP客户端进程进入了SYN_SENT(同步已发送状态)状态。

TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。这是三次握手中的开始,表示客户端想和服务端建立连接。

2、第2次握手

服务端收到请求报文后,如果同意建立连接,则发出确认报文。确认报文中SYN=1ACK=1,确认序号ack=x+1,同时也为服务端随机初始化一个序列号seq=y。此时,服务端进程进入了SYN_RCVD(同步收到)状态。

这个报文也不携带数据,并且也要消耗掉一个序号。这个报文带有SYN(建立连接)和ACK(确认)标识。表示服务端询问客户端是否准备好连接。

3、第3次握手

客户端收到服务器发送的确认后,还要给服务端发送确认报文。确认报文中ACK=1ack=y+1。此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。

至此,客服端与服务端连接建立成功,可以互相通信了。

二、TCP数据的传输过程

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

1、主机A初始seq为1200滑动窗体为100,向主机B传递数据。

2、假设主机B在完全成功接收数据的基础上,主机B为了确认这数据是否接收完全,向主机A发送ACK包,并将Ack号设置为1301。

确认Ack号公式:Ack号 = Seq号 + 传递的字节数 + 1

3、主机A获得B传来的Ack(1301)后,开始发送Seq为1301,滑动窗体为100的数据。

如此循环。。。直到数据全部发送完成。但这只是数据完全发送正确的情况,假如某个数据包因为某种原因丢失了,那又该如何呢?如下图所示

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

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

三、TCP四次挥手👋

上图所示即为TCP四次挥手示意图。

1、第1次挥手

客户端发送一个FIN(结束),用来关闭客户端与服务端之间的连接。

客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部中FIN=1,其序列号seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1)。此时,客户端进入FIN-WAIT-1(终止等待1)状态。

2、第2次挥手

服务端收到这个FIN,服务端发回一个ACK(确认)报文,ACK=1ack=u+1,并且带上自己的序列号seq=v。此时,服务端进入CLOSE-WAIT(关闭等待)状态。

服务端通知高层的应用进程,客户端向服务端的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据发送了,但是服务器若发送数据,客户端依然要接收。这个状态还要持续一段时间,这段时间就是CLOSE-WAIT状态持续的时间。

客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文。

3、第3次挥手

服务端发送一个FIN(结束)到客户端,服务端关闭客户端的连接。

服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1ack=w+1。由于在半关闭状态,服务器很可能又发送数据,假定此时的序列号为seq=w。此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。

4、第4次挥手

客户端发送ACK(确认)报文确认,并将确认的序号+1,关闭连接完成。

客户端收到服务器的连接释放报文后,必须发出确认,ACK=1ack=w+1,自己的序列号seq=u+1。此时,客户端就进入了TIME-WAIT(时间等待)状态。

注意:此时TCP连接还没有释放,必须经过2MSL(最后报文段寿命)的时间后,没有收到服务器端的ACK,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入CLOSED状态。

服务端在收到ACK确认报文后,就立即进入CLOSED状态。

四、关于三握四挥的思考

1、为什么要三次握手?而不是两次握手?

假如客户端发送了第一个连接的请求报文,但是由于网络不好,这个请求没有立即到达服务端,而是在某个网络节点中滞留了,直到某个时间才到达服务端。

本来这已经是一个失效的报文,但是服务端接收到这个请求报文后,还是会向客户端发出确认的报文,表示同意连接。

假如不采用三次握手,那么只要服务端发出确认,新的连接就建立了,但其实这个请求失效的,客户端并不会再向服务端发送确认信息。但是服务端认为连接已经建立,会一直等待客户端发送数据。这样,服务端的很多资源就白白浪费了,采用三次握手就是为了防止这种情况的发生。服务端会因为收不到客户端的确认报文,就知道客户端并没有建立连接。

为了保证有效连接的建立,这就是三次握手的作用。

2、为什么要四次挥手?

当关闭连接时,当收到客户端的FIN报文时,仅表示对方没有数据发送了,但是并不代表服务端自身的所有数据都传输完成了,所以不一定会马上关闭连接。只有当服务端的数据发送完毕后,服务端再次发送FIN报文给客户端,来通知客户端同意关闭连接。

所以服务端在收到客户端的FIN报文后,会发送一个ACK报文,表示确认收到关闭连接报文了,直到服务端报文数据传输完毕后,再发送FIN报文给客户端表示服务端同意关闭了。因此会有四次挥手。