三次握手和四次挥手是什么
TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而建立连接是通过三次握手来进行的,断开连接是通过四次挥手来进行的。
建立连接:三次握手
关于下方用到的SYN ACK标志位,请点击此处了解
关于下方用到的客户端和服务器用到的各种状态,请点击此处了解
三次握手是用来建立连接的过程,确保通信的双方都准备好数据传输。以下是三次握手的步骤:
- 一开始,客户端和服务端都处于 CLOSE 状态(即未建立连接),但服务端开启了监听,处于 LISTEN 状态
-
第一次握手(SYN):
- 客户端结束 CLOSE 状态,向服务器发送一个连接请求报文,其中标志位设置为 SYN(同步),并且初始化了一个随机的序列号用于数据传输。之后客户端为 SYN_SENT 状态。
-
第二次握手(SYN + ACK):
- 服务器收到客户端的连接请求,回复一个确认报文,其中设置自己的标志位为 SYN 和 ACK(确认),并且初始化了一个随机的序列号,并将客户端发来的的序列号加一,用于后续数据传输。服务器此时处于 SYN_RCVD 状态。
-
第三次握手(ACK):
- 客户端收到服务器的确认报文,向服务器发送一个确认报文,其中标志位设置为 ACK。客户端确认收到服务器的连接确认,同时确认序列号。客户端进入 ESTABUSHED 状态。
- 服务端收到客户端的应答报文后,也进入 ESTABLISHED 状态。
连接已经建立,双方可以开始进行数据传输
终止连接:四次挥手
在数据传输完毕后,TCP 通过四次挥手来终止连接,确保数据传输的完整性。双方都可以主动断开连接,断开连接后主机中的资源将被释放。以下是四次挥手的步骤:
- 第一次挥手:主动关闭方(一般是客户端)发送一个FIN(Finish)报文,表示不再发送数据。此时,主动关闭方进入FIN_WAIT_1状态。
- 第二次挥手:被动关闭方(一般是服务器)收到第一个FIN后,发送一个ACK(Acknowledgment)报文,确认收到了关闭请求。被动关闭方进入CLOSE_WAIT状态。客户端收到ACK后进入FIN_WAIT_2状态。
- 第三次挥手:被动关闭方发送自己的关闭请求,也就是发送一个FIN报文。此时,被动关闭方进入LAST_ACK状态。
- 第四次挥手:主动关闭方收到被动关闭方的关闭请求后,发送一个ACK报文作为确认。当被动关闭方收到这个ACK后,连接正式关闭,主动关闭方进入TIME_WAIT状态。被动关闭方在收到ACK后也会进入CLOSED状态。主动关闭方2MSL后进入CLOSED状态。
关于3次握手的疑问
为什么是3次握手呢?2,4,5可以吗?
首先我们要清楚,建立连接意味着OS需要把这些已经建立好的连接管理起来。要管理!就要先描述,在组织! OS内为了管理连接就要创建数据结构 struct tcp_linki{...}
所以说,创建维护连接是有成本的! ! !即 内存+CPU
2次为什么不可以
- 2次握手—SYN洪水,非常容易收到攻击,大量的SYN请求,导致服务器OS只要收到SYN就开始维护链接,导致内存和CPU的高负荷。
- 如果仅 SYN - SYN/ACK,那就不能判断服务器到客户端的SYN/ACK是否丢失,从而导致服务器处于一直处于ESTABLISHED状态,但客户端没有成功建立连接。因为此时服务器已经认为自己建立连接了,就不会有所谓的重传了。
- 3次- 保证双发连接建立的可靠性
- 避免引入不必要的延迟和开销
- 对网络实时性需求进行平衡考虑
- 4次
- 四次握手可以增加可靠性,但需要额外的RTT(从发送方发送数据到接收方,再从接收方发送确认到发送方,所花费的时间)。考虑到网络实时性需求,引入额外延迟是不可取的。三次握手已经可以满足需求。
- 5次
- 五次握手更加冗余,会引入更多不必要的延迟和开销。并且没有明显的技术优势。
- 3次握手的本质
- 本质就是在赌最后一次握手报文不会丢失,这种情况是小概率事件,如果丢失,客户端此时是认为已经连接成功的,当直接发送数据给服务器时,服务器此时是没有正常建立连接的,所以服务器会设置RST控制位发送给客户端,进行重连。
- 总而言之
- 奇数次握手必然是客户端最后发送ACK,肯定是客户端先开始维护连接,而不是服务器
- 偶数次握手必然是服务器发送ACK,肯定是服务器先开始维护连接,这会导致安全性的降低,容易被SYN攻击
- 如果偶数次发送ACK,则服务器可能承担大量的维护连接的成本,因为服务器最后一次发送ACK时,已经确认连接成功,OS必然会创建数据结构维护连接,与2次握手类似会发生SYN洪水,而客户端则不需要成本,因为不需要维护连接,因为客户端此时并没有确认连接。
- 再多次了没有必要,因为TCP握手并不是用来解决安全问题的,只是为了自己不要有明显的漏洞,所以SYN洪水肯定是会存在的,越多次的握手效率越低,3次就够了,没有明显的漏洞,可以缓解SYN洪水问题,使得攻击方成本变高,因为想要建立连接必然需要客户端先维护连接。
- 进一步总结
- 3次握手没有明显的设计漏洞,一旦建立连接出现异常,成本嫁接到client,server端成本较低
- 验证双方通信信道的通畅情况——三次握手是验证全双工通信信道通畅的最小成本!
关于四次挥手的疑问
四次挥手的设计是为了确保双方都有足够的时间来处理未完全传输的数据,以及确认对方收到了关闭请求和确认。这样可以保证数据的可靠传输和连接的正常关闭。
为什么握手要三次,挥手却要四次呢?
那是因为握手的时候并没有数据传输,所以服务端的 SYN 和 ACK 报文可以合并一起发送,但是挥手的时候有数据在传输,所以 ACK 和 FIN 报文不能同时发送,需要分两步,所以会比握手多一步。
为什么客户端在第四次挥手后还会等待 2MSL?
等待 2MSL 是因为保证服务端接收到了 ACK 报文,因为网络复杂了,很有可能 ACK 报文丢失了,如果服务端没接收到 ACK 报文的话,会重新发送 FIN 报文,只有当客户端等待了 2MSL 都没有收到重发的 FIN 报文时就表示服务端是正常收到了 ACK 报文,那么这个时候客户端就可以关闭了。
TCP规定,MSL应大于最大报文段在网络中存在的时间,以确保网络中不会存在旧的重复报文段。
2MSL是多少?
MSL 是 IP 协议中的一个时间间隔,表示一个数据包在网络中的最大生存时间。在 IPv4 中,MSL 通常被设置为 2 分钟(120 秒),而在 IPv6 中可能会有所不同。其具体时间取决于实现和配置。
因此,2MSL 通常等于 4 分钟(240 秒)。
什么情况会出现三次挥手?
当被动关闭方(上图的服务端)在 TCP 挥手过程中,没有数据要发送并且开启了 TCP 延迟应答机制,那么第二和第三次挥手就会合并传输,这样就出现了三次挥手。
异常情况
进程终止
程序不是正常退出,而是像使用了kill -9 命令一样被强行中断。在OS看来,无论是正常终止还是异常终止,都是终止,无论代码是否释放资源,只要程序终止,OS都会去释放资源,也代表着会发起四次挥手。
机器重启
和进程终止的情况相同,类似于电脑关机时,OS会提示有进程在执行是否需要关闭,关机之前就会终止进程。
机器掉电/网线断开
接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset. 即使没有写入操作, TCP自己也内置了一个保活定时器(时间比较长,可能是几十分钟,也可能是几小时), 会定期询问对方是否还在. 如果对方不在, 也会把连接释放.
另外, 应用层的某些协议, 也有一些这样的检测机制. 例如HTTP长连接中, 也会定期检测对方的状态. 例如QQ, 在QQ断线之后, 也会定期尝试重新连接。
在HTTP长连接中定期检测对方状态的意义如下:
-
检测连接是否断开:HTTP长连接如果一直处于闲置状态,无法确定对端是否断开,需要定期检测来确认连接状态。
-
防止假连接:定期检测可以判断连接是否是假连接,避免无效数据传输。
-
异常处理:如果检测发现连接已断开,可以进行重新连接或其他异常处理。
-
保活机制(keepalive-内核态,属于TCP,与HTTP的keep-alive不同,HTTP的属于用户态,是长连接机制):定期发送保活数据包,防止中间设备判断为空闲连接而断开。
-
探测网络:检测包可以用来探测网络质量,RTT延迟,丢包率等。
-
节省资源:可以关闭假连接,回收资源。
-
重建连接:发现断开后可以立即rebuild连接,提高可靠性。
-
避免冗余连接:关闭不再使用的连接,避免资源占用。
扩展
以下是一位大佬 [小林coding] 的文章
没有listen,是否能建立连接呢?
总结以下就是可以。不过是客户端自己连自己。
没有accept,是否能建立连接呢?
总结一下就是可以,但是没有它就无法创建套接字,进行数据通信。