一开始,客户端和服务端都处于 CLOSE 状态。先是服务端主动监听某个端口,处于 LISTEN 状态
1. 第一次握手
-
客户端会随机初始化序号(client_isn),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN 标志位置为 1,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态。
-
第一次握手的 SYN 包丢失:超时重传
2. 第二次握手
-
服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序号(server_isn),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1, 接着把 SYN 和 ACK 标志位置为 1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。
-
SYN+ACK 包丢失:服务端syn_ack 和客户端 ack 都超时重传
3. 第三次握手
-
客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入 server_isn + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务端的数据,之后客户端处于 ESTABLISHED 状态。
-
服务端收到客户端的应答报文后,也进入 ESTABLISHED 状态
-
ack 丢失(服务端屏蔽 ACK):客户端已经进入 ESTABLISHED 状态,服务端还在 SYN_RECV 状态,服务端没收到 ACK,重传 syn_ack,超过重传次数后就断开连接,但是客户端一直处于 ESTABLISHED 状态,直到下一次请求才发现服务端已经断开连接了
如果不是屏蔽 ACK(不接收直接丢弃),而是直接断开连接,服务端收到客户端的 ack 会回 RST 报文,客户端收到就会断开连接而不是不断重发 ACK
4. TCP 的保活机制
如果客户端一直不发数据,就会一直处于 ESTABLISHED 状态,但服务端已经断开了
-
在一段时间内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个「探测报文」,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序
5. 为什么要三次?
-
避免建立历史报文的连接:举例
-
同步双方的序列号
-
避免资源浪费
6. 两次可以吗?
-
有可能建立历史连接:两次握手没有中间状态让客户端阻止历史连接
-
只能确认客户端的序列号被服务端同步了,不能确认服务端的序号是否被客户端同步
-
两次握手就建立连接会造成服务端的资源浪费:没有第三次握手,服务端每次收到 SYN 报文就会建立一次连接,但是有可能有多个重复的 SYN 报文(历史报文被阻塞延后到达),冗余的 SYN 报文导致服务端建立多个连接,浪费资源
7. 四次可以吗?
-
第二次握手的作用有两个:ACK 确认收到客户端的第一次握手,同步客户端初始化序列号,SYN 发送服务端初始化序列号,这两次握手可以合并成同一次,所以四次握手没有必要