一、建立TCP连接 —— 三次握手
(1)客户端向服务端发送一个携带初始序列号的SYN包。
(2)服务端收到后将其加入到半连接队列,然后向客户端回复携带初始序列号的SYN+ACK包。
(3)客户端收到后再向服务端发送一个ACK包,服务端收到后确认建立连接,放入到全连接队列。
问题1:为什么是三次握手?不是两次、四次?
- 防止历史重复连接:例如当客户端发了一个SYN包由于网络拥堵没有及时得到回复,于是发了一个新的SYN包,如果是两次握手会重复连接多次,造成资源浪费。
- 服务端回复的SYN+ACK包可合并一起发送,无需分成两次,因此不需要四次
问题2:第三次握手丢失了,会发生什么?
-
因为这个第三次握手的 ACK 是对第二次握手的 SYN 的确认报文,所以当第三次握手丢失了,如果服务端那一方迟迟收不到这个确认报文,就会触发超时重传机制,重传 SYN-ACK 报文,直到收到第三次握手,或者达到最大重传次数。
-
注意,ACK 报文是不会有重传的,当 ACK 丢失了,就由对方重传对应的报文。
二、数据传输过程
建立连接后双方就可以开始正式的数据通信了
1. TCP数据分片
TCP传输是面向字节流的,数据之间没有边界,当数据量过大时,在TCP层面就会按MSS大小(MTU-IP&TCP头部长度)对数据进行分片,每片数据再构成一个TCP报文交给IP层进行发送,以此避免超过MTU大小导致在IP层又对TCP报文进行分片,导致一片IP报文丢失时就要重传整个较大的TCP报文。
与TCP不同的是,UDP按包进行传输,不会对数据分片,这就是说应用层一次交给UDP多长的数据,UDP就将其组成一个UDP报文交给IP层,若报文过长,则IP层会进行分片,一片丢失则对于接收端整个UDP报文就丢失了,这也是UDP丢包率高的原因之一。
2. 确认应答机制
数据分片发送后那接收端如何重新将其排列为有序的数据流呢,发送端又是如何知道数据包安全抵达了呢?这就依靠TCP头部中的序列号和确认应答号了。
序列号:在建立连接时通信双方就会在SYN包中随机生成一个初始序列号发送给对方,之后发送数据包通信时就从上一次序列号+数据字节数长度作为序列号(连接和断开时的SYN包和FIN包数据长度视为1),这样接收端就可以按包的序号以及数据长度进行排列,解决网络包乱序问题。
确认应答号:接收端在收到数据后会发送一个ACK包,其中包含一个确认应答号,值为接收到的数据包序列号+数据长度,表示说这个序号前面的数据我都收到了,从这个序号开始发数据给我,这样发送端若没有收到对应的应答号,就可以对数据进行重传,解决丢包的问题。
为了保证传输的高效和可靠,TCP协议还做了很多复杂的机制,如重传机制、滑动窗口、流量控制、拥堵控制等,之后的文章再聊
三、关闭TCP连接 —— 四次挥手
(1)假设客户端要关闭连接,首先向服务端发送一个FIN包,自身进入终止等待1状态
(2)服务端收到后向客户端回复一个ACK包,自身进入关闭等待状态,此时仍可以向客户端发送数据。客户端收到ACK包后进入终止等待2状态
(3)服务端发送完毕后向客户端再发送一个FIN包,进入最后确认状态。
(4)客户端收到FIN包后向服务端发送一个ACK包,进入TIME-WAIT超时等待状态。服务端收到ACK包后就关闭连接,客户端超时完毕后也关闭连接。如果服务端再指定时间内没有收到ACK包会重发FIN包,客户端收到后会重发ACK包并刷新超时时间。
参考:
- https://www.bilibili.com/video/BV1kV411j7hA
- https://xiaolincoding.com/network/3_tcp/tcp_interview.html