三次握手 与 四次挥手
在正常情况下,TCP 要经过三次握手建立连接,四次挥手断开连接
1. 三次握手
服务端状态转化:
- [CLOSED -> LISTEN]
服务器端调用 listen 后进入 LISTEN 状态,等待客户端连接; - [LISTEN -> SYN_RCVD]
一旦监听到连接请求(同步报文段 SYN),就将该连接放入内核等待队列中,并向客户端发送 SYN + ACK 确认报文。 - [SYN_RCVD -> ESTABLISHED]
服务端一旦收到客户端的确认报文 ACK,就进入 ESTABLISHED 状态,可以进行读写数据了。
客户端状态转化:
- [CLOSED -> SYN_SENT]
客户端调用 connect,发送同步报文段 SYN; - [SYN_SENT -> ESTABLISHED]
connect 调用成功,收到服务器的 SYN + ACK 报文段则进入 ESTABLISHED 状态,开始读写数据;
三次握手有啥用 ? 和可靠性有什么关系 ?
- 三次握手相当于 “投石问路”, 检查一下当前这个网络的情况是否满足可靠传输的条件。
如果网络本身效果比较差,强行进行 TCP 传输,也会涉及到大量的数据丢包,更具体的说,三次握手是在检测通信双方的发送能力和接收能力是否都正常。 - 让通信双方能协商一些必要的信息,TCP 通信过程中,需要客户端和服务器之间有一些共同的信息,在三次握手过程中相互之间可以交互一些必要的内容。
举个栗子:
以打电话为例, 这个过程就是在检验通信双方的发送能力和接收能力:
假如说小明麦克风坏了,喂了几次没回应,就会重传,重试几次还没回应就放弃。
为啥是三次握手,两次行不行? 四次行不行 ?
- 四次?行,但是没必要,分开传输不如合在一起效率高。
- 两次 ?不行!两次,意味着缺少最后一次
- 由上面的两张图就可以知道,两次的话,此时客户端知道双方的发送和接收能力都是正常的
- 但是服务器这边是残缺的,不知道自己的发送能力和客户端的接收能力是否 OK
- 此时服务器对于当下能否满足可靠传输心里没底
- 这第三次交互就是为了给服务器吃一个定心丸。
2. 四次挥手
服务端状态转化:
- [ESTABLISHED -> CLOSE_WAIT]
当客户端主动关闭连接(调用 close),服务器会收到结束报文段 FIN ,服务器返回确认报文段 ACK 并进入 CLOSE_WAIT; - [CLOSE_WAIT -> LAST_ACK]
进入 CLOSE_WAIT 后说明服务器准备关闭连接(需要处理完之前的数据);当服务器真正调用 close (用户代码中执行了 socket.close) 关闭连接时,会向客户端发送 FIN,此时服务器进入 LAST_ACK 状态,等待最后一个 ACK 到来(这个 ACK 是客户端确认收到了 FIN ) - [LAST_ACK -> CLOSED]
服务器收到了对 FIN 的 ACK,彻底关闭连接。
客户端状态转化:
- [ESTABLISHED -> FIN_WAIT_1]
客户端主动调用 close 时,向服务器发送结束报文段,同时进入 FIN_WAIT_1; - [FIN_WAIT_1 -> FIN_WAIT_2]
客户端收到服务器对结束报文段的确认 ACK,则进入 FIN_WAIT_2,开始等待服务器的结束报文段; - [FIN_WAIT_2 -> TIME_WAIT]
客户端收到服务器发来的结束报文段 FIN,进入 TIME_WAIT,并发出 LAST_ACK; - [TIME_WAIT -> CLOSED]
客户端要等待一个 2MSL(Max Segment Life,报文最大生存时间)的时间,才会进入 CLOSED 状态。
为什么还要 TIME_WAIT ?
为的是给最后一次 ACK 提供重传机会,表面上 A 发送完 ACK 后就没有 A 的事了,按理说 A 可以销毁连接了,但是怕最后一个 ACK 丢包,若最后一个 ACK 丢了,那么 B 一定会因为没收到 ACK 重传 FIN,如果 A 已经销毁连接了,那么就无人能够处理这个 FIN 了,因此 A 不应该释放的太早,要等待一段时间,确保 B 不会重传 FIN 后再真正的销毁连接。
为什么是TIME_WAIT的时间是2MSL?
MSL 是 TCP 报文的最大生存时间,因此 TIME_WAIT 持续存在 2MSL 的话
-
就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失
(否则立刻客户端立即重新创建连接时,可能会收到来自上一个进程的迟到的数据(FIN),但是这种数据很可能是错误的);
-
同时也是在理论上保证最后一个报文可靠到达
(假设最后一个ACK丢失,那么服务器会再重发一个FIN。这时虽然客户端的进程不在了,但是 TCP 连接还在,仍然可以重发 LAST_ACK );
CLOSE_WAIT
一般而言,对于服务器上出现大量的 CLOSE_WAIT 状态,原因就是服务器没有正确的关闭 socket,没有执行到 socket.close 导致四次挥手没有正确完成。这是一个 BUG。只需要加上对应的 close 即可解决问题。
三次握手和四次挥手的区别
-
三次握手一定是客户端发起的(主动发起请求的一方叫做客户端)
四次握手可能是客户端发起的,也有可能是服务器主动发起的 -
三次握手,中间有两次合并和,
四次握手,中间两次合并不了,不能合并的原因在于 B (被动接收 FIN 的那一方)发送 ACK 和 FIN 的时机不同,
<1> 四次挥手中,B 发送给 A 的 ACK 是由操作系统内核负责的(除了应用层,TCP/IP … 本身就是属于操作系统层面),那么意味着,当内核收到 FIN 后会立即返回 ACK (我们是感知不到的)
<2> B 发送给 A 的 FIN,是由用户代码负责的,B 中代码调用了 socket.close() 方法时才触发 FIN 发送,所以要等到用户代码执行到 socket.close() 方法才触发,但是什么时候发送 取决于用户代码,若 <1> <2> 操作之间的时间差较大,就不能合并了,若时间差较小,由于 延时应带和捎带应答 机制,可能会合并。
而三次握手中 B 发送的 ACK 和 SYN 都是由内核负责的,是同一时机所以能够合并。
好啦! 以上就是对 TCP 三次握手 与 四次挥手的讲解,希望能帮到你 !
评论区欢迎指正 !