TCP三次握手与四次挥手
TCP三次握手与四次挥手:深入理解网络连接的建立与终止
在网络通信中,TCP(传输控制协议)是确保数据可靠传输的核心协议。作为一名网络工程师或开发者,深入理解TCP的三次握手和四次挥手过程是必备技能。本文将带您详细了解这两个关键过程,以及它们背后的设计原理。
TCP连接的本质
在深入握手和挥手过程之前,我们需要先理解TCP连接的本质。TCP连接用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合包括Socket、序列号和窗口大小。
TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。与UDP不同,TCP在通信前需要先建立连接,通信结束后还需要释放连接,这正是通过"三次握手"和"四次挥手"来完成的
TCP三次握手:连接的建立
三次握手的基本过程
TCP三次握手是指在建立TCP连接时,客户端和服务器之间需要交换三个报文段才能成功建立连接的过程。这个过程不仅确保了通信双方的发送和接收能力,还协商了一些连接参数,如初始序列号等。
第一次握手:客户端发送SYN包
客户端向服务器发送一个SYN(同步)报文段,表明客户端请求建立连接
这个报文段中,SYN标志位被设置为1,序列号(seq)被设置为一个随机值x(客户端的初始序列号ISN)
此时,客户端进入
SYN_SENT
状态,等待服务器的确认
第二次握手:服务器响应SYN-ACK包
服务器收到客户端的SYN报文段后,如果同意建立连接,会向客户端发送一个SYN+ACK报文段
在这个报文段中,SYN标志位和ACK标志位都被设置为1,确认号(ack)被设置为客户端的序列号加1(即x+1),序列号(seq)被设置为一个随机值y(服务器的初始序列号ISN)
此时,服务器进入
SYN_RECEIVED
状态,等待客户端的确认
第三次握手:客户端发送ACK包
客户端收到服务器的SYN+ACK报文段后,会向服务器发送一个ACK报文段,表明客户端已收到服务器的SYN+ACK报文段
在这个报文段中,ACK标志位被设置为1,确认号(ack)被设置为服务器的序列号加1(即y+1),序列号(seq)被设置为x+1
此时,客户端进入
ESTABLISHED
状态服务器收到客户端的ACK报文段后,也进入
ESTABLISHED
状态,表示连接已成功建立
为什么是三次握手?不是两次或四次?
这是面试中最常见的问题之一。三次握手的设计是为了解决几个关键问题:
1. 防止历史重复连接初始化造成的混乱
这是三次握手的主要原因。考虑一个场景:客户端先发送了SYN(seq=90)报文,然后客户端宕机了,而且这个SYN报文还被网络阻塞了,服务端并没有收到。接着客户端重启后,又重新向服务端建立连接,发送了SYN(seq=100)报文。
在网络拥堵情况下,一个「旧SYN报文」可能比「最新的SYN」报文早到达服务端。此时服务端就会回一个SYN+ACK
报文给客户端,此报文中的确认号是91(90+1)。客户端收到后,发现自己期望收到的确认号应该是100+1,而不是90+1,于是就会回RST报文。服务端收到RST报文后,就会释放连接。
2. 同步双方的初始序列号
TCP协议的通信双方都必须维护一个序列号,序列号是可靠传输的关键因素。三次握手过程可以确保双方的初始序列号都被正确同步。
3. 避免资源浪费
如果只有两次握手,当客户端发送的SYN报文在网络中阻塞,客户端没有接收到ACK报文,就会重新发送SYN。由于没有第三次握手,服务端不清楚客户端是否收到了自己回复的ACK报文,所以服务端每收到一个SYN就只能先主动建立一个连接,这会造成多个冗余的无效链接,造成不必要的资源浪费。
TCP四次挥手:连接的优雅终止
四次挥手的基本过程
TCP四次挥手是TCP协议中用于断开连接的机制,确保数据传输的可靠性。由于TCP连接是全双工的,每个方向都必须单独进行关闭。
第一次挥手:主动方发送FIN报文
当客户端决定关闭连接时,会向服务器发送一个FIN(结束)报文段,表明客户端不再发送数据
在这个报文段中,FIN标志位被设置为1,序列号(seq)被设置为客户端当前的序列号x
此时,客户端进入
FIN_WAIT_1
状态,等待服务器的确认需要注意的是,虽然客户端不再发送数据,但仍然可以接收服务器发送的数据
第二次挥手:被动方发送ACK确认
服务器收到客户端的FIN报文段后,会向客户端发送一个ACK报文段,确认收到了客户端的关闭请求
在这个报文段中,ACK标志位被设置为1,确认号(ack)被设置为客户端的序列号加1(即x+1)
此时,服务器进入
CLOSE_WAIT
状态,客户端收到这个ACK报文段后,进入FIN_WAIT_2
状态此时,客户端到服务器的连接已经关闭,但服务器到客户端的连接仍然开放,服务器可以继续向客户端发送数据
第三次挥手:被动方发送FIN报文
当服务器也决定关闭连接时(可能是立即关闭,也可能是处理完剩余数据后关闭),会向客户端发送一个FIN报文段,表明服务器也不再发送数据
在这个报文段中,FIN标志位被设置为1,序列号(seq)被设置为服务器当前的序列号y
此时,服务器进入
LAST_ACK
状态,等待客户端的最终确认
第四次挥手:主动方发送最终ACK
客户端收到服务器的FIN报文段后,会向服务器发送一个ACK报文段,确认收到了服务器的关闭请求
在这个报文段中,ACK标志位被设置为1,确认号(ack)被设置为服务器的序列号加1(即y+1)
此时,客户端进入
TIME_WAIT
状态,服务器收到这个ACK报文段后,进入CLOSED
状态客户端在
TIME_WAIT
状态等待2MSL(Maximum Segment Lifetime,报文最大生存时间)时间后,才最终进入CLOSED
状态
为什么是四次挥手?不是三次?
这是另一个常见的面试问题。四次挥手的设计是为了解决全双工通信中安全关闭连接的问题:
1. 全双工通信的特性 TCP连接是全双工的,即连接的两端都可以同时发送和接收数据。因此,关闭连接时,每个方向都需要单独关闭。第一次和第二次挥手关闭了客户端到服务器的连接,第三次和第四次挥手关闭了服务器到客户端的连接。
2. 数据传输的完整性 当客户端发送FIN报文段时,表示客户端不再发送数据,但服务器可能还有数据需要发送给客户端。服务器需要先确认客户端的FIN报文段(第二次挥手),然后继续发送剩余的数据,最后才能发送自己的FIN报文段(第三次挥手)。这样可以确保所有数据都被完整传输。
3. TIME_WAIT状态的重要性 客户端在发送最后一个ACK后进入TIME_WAIT状态,等待2MSL时间。这个设计有两个主要目的:
确保最后一个ACK报文能够到达服务器。如果这个ACK报文丢失,服务器会重传FIN报文,客户端可以再次发送ACK
防止"已失效的连接请求报文段"出现在新连接中。经过2MSL时间后,所有旧的报文都会从网络中消失
实际应用与最佳实践
在网络编程中的应用
理解TCP的三次握手和四次挥手机制对于开发高效、可靠的网络应用程序至关重要:
Socket编程中的连接管理
服务器端:服务器通过
socket()
、bind()
、listen()
和accept()
函数来创建套接字、绑定地址、监听连接请求和接受连接。当客户端发起连接请求时,三次握手在accept()
函数调用期间完成客户端:客户端通过
socket()
和connect()
函数来创建套接字和发起连接请求。三次握手在connect()
函数调用期间完成连接释放:双方通过
close()
或shutdown()
函数来关闭连接,触发四次挥手过程
长连接与短连接的选择
长连接:适用于频繁通信的场景,如数据库连接、消息队列等。可以减少连接建立和释放的开销
短连接:适用于偶尔通信的场景,如简单的HTTP请求。可以避免长时间占用服务器资源
常见问题排查
连接建立失败
检查服务器是否在监听指定端口
检查防火墙设置是否阻止了连接
使用
tcpdump
或wireshark
抓包分析握手过程
连接异常断开
检查网络是否稳定
查看是否有超时设置不合理
分析四次挥手过程是否完整
总结
TCP的三次握手和四次挥手机制是TCP协议可靠性的重要保障,它们确保了TCP连接的安全建立和释放。掌握这些原理不仅有助于我们更好地理解网络通信的本质,还能帮助我们在实际开发中设计出更加高效、可靠的网络应用。
三次握手通过精心设计的报文交换过程,解决了历史连接、序列号同步和资源浪费等问题;四次挥手则通过双向关闭机制,确保了全双工连接的优雅终止。理解这些机制,将使我们成为更好的网络工程师和开发者。