一开始,客户端和服务端都处于 CLOSE 状态。先是服务端主动监听某个端口,处于 LISTEN 状态
三次握手:
- 客户端会随机初始化序号(client_isn),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN 标志位置为 1,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态
- 服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序列号(server_isn),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1, 接着把 SYN 和 ACK 标志位置为 1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态
- 客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入 server_isn + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务端的数据
为什么是三次握手?不是一次、两次、四次?
首先,一次是不可能的,因为TCP要双方建立连接,所以肯定至少要一问一答来保证双方是正常能通讯的,不然就变UDP了
然后,三次都已经能建立稳定连接了,所以四次就多余了撒,接下来只需要说明为什么两次不行即可
- 三次握手才让双方确认对方的接收、发信息的能力
- 三次握手才可以同步双方的初始序列号
- 三次握手才可以避免历史连接混乱
场景描述:
- 客户端首先发送一个 SYN 报文(序列号为 90)。
- 客户端宕机,且该 SYN 报文被网络阻塞,服务器未收到。
- 客户端重启后,发送新的 SYN 报文(序列号为 100)
所以此时大体有2种情况:新的SYN先到(一切正常)、旧的SYN先到(就需要RST)
如果在收到RST之前收到新的SYN,也就是第二次握手之后又来新的握手请求 or 如果是第三次握手之后又来新的握手请求,服务端也是回Challenge ACK,客户端收到后发现不是预期的ACK号,会发送RST中止连接
所以服务端在第二次握手后,如果收到新的SYN请求,就会回一个Challenge ACK,客户端收到后,一般会回个RST,于是双方断开连接
为什么不能用两次握手?
- 客户端首先发送一个 SYN 报文,随即客户端宕机,且该 SYN 报文被网络阻塞,服务器未收到
- 客户端重启后,发送新的 SYN 报文
当出现这种情况时,两次握手分不清当前这个连接是否是历史连接
超时重传,重传次数(都是参数控制)
第一次握手丢失: 客户端发送 SYN 报文,但服务器未收到。
- 客户端反应:
- 客户端在超时后重传 SYN 报文。
- 重传的 SYN 报文序列号与之前相同。
- 超时时间由操作系统内核控制,可能为 1 秒、3 秒等。
- 默认重传次数为 /proc/sys/net/ipv4/tcp_syn_retries 的值,通常为 5。
- 每次重传的超时时间呈指数增长,导致总耗时可能长达 1+2+4+8+16+32= 63 秒。
- 最终结果:如果服务器仍然未响应,客户端将放弃连接。
第二次握手丢失: 服务器发送 SYN-ACK 报文,但客户端未收到。
- 客户端反应:
- 客户端认为自己的 SYN 报文可能丢失,超时后重传 SYN 报文。
- 服务器反应:
- 服务器也未收到第三次握手,超时后重传 SYN-ACK 报文。
- 最终结果:如果双方都未收到对方的响应,最终都会放弃连接。
第三次握手丢失: 客户端发送 ACK 报文,但服务器未收到。
- 服务器反应:
- 服务器未收到第三次握手,认为其 SYN-ACK 报文丢失,超时后重传 SYN-ACK 报文。
- 客户端反应:
- 如果在此阶段客户端发送数据,服务器会返回 RST 报文,表示连接尚未建立。
- 客户端收到 RST 后,会重新开始第一次握手。
- 最终结果:连接将无法建立,客户端将重新尝试连接。