真实面经题目 · 原创解析
为什么 TCP 建立连接需要三次握手?
TCP 三次握手的目的不是单纯通知双方在线,而是让双方确认彼此的发送和接收能力、同步初始序列号、协商连接参数,并避免历史重复连接请求造成错误连接。两次握手无法可靠完成这些目标。
真实面经题目 · 原创解析
TCP 三次握手的目的不是单纯通知双方在线,而是让双方确认彼此的发送和接收能力、同步初始序列号、协商连接参数,并避免历史重复连接请求造成错误连接。两次握手无法可靠完成这些目标。
可以按三步解释。第一次握手,客户端发送 SYN 和自己的初始序列号,表示希望建立连接。第二次握手,服务端回复 SYN+ACK,既确认收到了客户端的 SYN,也发送自己的初始序列号。第三次握手,客户端回复 ACK,确认收到了服务端的 SYN。完成后,双方都知道自己的发送能到达对方、自己的接收能收到对方数据,并且双方序列号都已被确认。三次握手还能过滤网络中滞留的旧 SYN,避免服务端仅凭一个过期请求就建立连接并分配资源。握手过程中还会协商 MSS、窗口扩大、SACK、时间戳等 TCP 选项。测试时要覆盖丢包、重传、旧包、半连接队列、SYN flood 和握手超时等场景。
TCP 连接建立后是全双工通信,双方都要发送数据,也都要接收数据。因此建立连接时不仅要确认某一方能发出去,还要确认两个方向的序列号和收发能力都可用。三次握手就是为双向可靠传输建立初始状态。
客户端发送 SYN,并带上客户端选择的初始序列号。服务端收到后,可以确认客户端到服务端这个方向的报文能到达,也知道后续要从哪个序列号开始确认。但此时客户端还不知道服务端是否收到,也不知道服务端的初始序列号。
服务端回复 SYN+ACK,一方面确认客户端的 SYN,另一方面发送自己的初始序列号。客户端收到这一步后,可以确认客户端发送能力、服务端接收能力、服务端发送能力和客户端接收能力都具备。但服务端还不知道客户端是否收到了自己的 SYN。
客户端发送 ACK 确认服务端的 SYN。服务端收到后,才知道自己的发送方向也被对方确认,双方序列号同步完成,连接可以进入已建立状态。第三次握手也是避免服务端在不确定对端是否存在时过早确认连接的关键步骤。
如果只有两次握手,服务端可能在发送 SYN+ACK 后就认为连接建立。网络中的旧 SYN 延迟到达时,服务端可能误建连接并等待一个早已不存在的客户端,造成资源浪费和状态错误。三次握手通过最终 ACK 让服务端得到客户端仍然有效的确认。
两次握手后,客户端能确认服务端收到了自己并能回包,但服务端无法确认客户端收到了自己的 SYN。服务端可能因旧 SYN 或丢失的 SYN+ACK 误建连接。
第二次握手把服务端的 SYN 和对客户端 SYN 的 ACK 合并到一个报文中,已经能表达两个信息。建立连接时双方都还没有半关闭需求,因此没必要拆成四次。
服务端收不到第三次 ACK 会保留半连接状态并重传 SYN+ACK,直到超时放弃。客户端如果已经进入已建立状态,后续发送数据时也会携带确认信息,服务端可据此完成状态推进。
攻击者大量发送 SYN 后不完成第三次握手,使服务端半连接队列和资源被占用。防御通常包括 SYN cookies、队列调优、限速、清洗和前置负载均衡保护。