相信大家对网络协议中 TCP 三次握手和四次挥手都很了解,那我们要怎么把这过程描述出来呢?
三次握手:
第一次握手:客户端发送一个 SYN 报文请求连接
第二次握手:服务器收到请求 SYN 报文,并应答客户端一个 SYN+ACK 报文
第三次握手:客户端收到服务器的应答 SYN+ACK 报文后,回应一个 ACK 报文
服务器收到 ACK 报文,三次握手完成建立
这里说一点,三次挥手时,第一次,第二次不能携带数据,在确认之后的第三次挥手时可以携带数据
四次挥手:
第一次挥手:客户端发送一个 FIN 报文关闭连接请求
第二次挥手:服务器收到请求,并发送一个 ACK 报文确认应答
第三次挥手:如果服务器也想断开连接,再发送一个 FIN 报文,并等待最后一个 ACK 的到来(ACK 是确认客户端收到 FIN)
第四次挥手:客户端收到 ACK 报文和 FIN报文,然后给服务器发送一个 ACK 报文,最后关闭客户端
服务器收到最后一个 ACK 报文,完全关闭服务器
作用呢, 是为了确认客户端与服务器之间的发送与接收能力是否正常
其实可以再描述的再仔细一些
刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态,其次开始
三次握手:
第一次握手:
客户端:[CLOSED -> SYN_SENT] 客户端调用 connect, 发送同步
服务器:[CLOSED -> LISTEN] 服务器端调用 listen 后进入 LISTEN 状态, 等待客户端连接;
客户端给服务端发送一个 SYN 报文,并指明客户端的初始化序列号 ISN,此时客户端处于 SYN_SENT 状态
第二次握手:
服务器:[LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段), 就将该连接放入内核等待队列中, 并向客户端
发送 SYN 确认报文.
服务器收到客户端 SYN 报文,以自己的 SYN 报文作为应答,并也指定自己的初始化序列号 ISN
然后会把客户端的I SN+1 作为 ACK 的值回应给客户端,表示服务器已经收到请求
第三次握手:
客户端收到 SYN 报文后,会返回一个 ACK 报文,也会将服务器的 ISN+1 作为 ACK 的值回应给服务器,表示已经收到服务器的 SYN 报文,此时客户端处于 Established
最后,服务器收到 ACK 报文后,也处于 Established 状态,完成连接的建立,此时开始正常的通信
四次挥手:
第一次挥手:
[ESTABLISHED -> FIN_WAIT_1]
客户端主动调用 close 时,客户端发送一个 FIN 报文,报文中会指定一个序列号,客户端此时处于一个 FIN_WAIT_1 状态
第二次挥手:
[ESTABLISHED -> CLOSE_WAIT]
当客户端主动关闭连接(调用close),服务器收到 FIN 后,发送一个 ACK 报文,并把客户端的序列号 +1 作为 ACK 报文的序列号值,表明收到客户端的报文,此时服务器处于 close_wait_1 状态
[FIN_WAIT_1 -> FIN_WAIT_2]
客户端收到服务器对结束报文段的确认, 则进入FIN_WAIT_2, 开始等待服务器的结束报文段;
第三次挥手:
[CLOSE_WAIT -> LAST_ACK] 进入 CLOSE_WAIT 后说明服务器准备关闭连接(需要处理完之前的数据); 当服务器真正调用 close 关闭连接时, 和客户端第一次挥手一样,发送一个 FIN 报文,且指定一个序列号, 此时服务器进入 LAST_ACK 状态, 等待最后一个 ACK 到来(这个ACK是客户端确认收到了 FIN)
第四次挥手:
[FIN_WAIT_2 -> TIME_WAIT]
客户端收到服务器发来的结束 FIN 报文段, 进入 TIME_WAIT, 并发出一个 ACK 报文段作为应答
[TIME_WAIT -> CLOSED]
客户端要等待一个 2MSL (Max Segment Life, 报文最大生存时间)的时间, 才会进入 CLOSED 状态
最后服务器:
[LAST_ACK -> CLOSED]
服务器收到 ACK 报文之后,就关闭连接了,处于 CLOSED 状态
这里面要注意 TIME_WAIT 状态,为什么客户端发送 ACK 之后不直接关闭,而是要等一阵子才关闭?
这里是为了确保服务器收到客户端发送的 ACK 报文,如果没有收到的话,有可能出现了丢包的情况,服务器会重新发 FIN
报文给客户端,客户端收到之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文
这里等待的时间为什么是2MSL (Max Segment Life, 报文最大生存时间)的时间?
MSL是TCP报文的最大生存时间, 因此TIME_WAIT持续存在2MSL的话就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的)同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发LAST_ACK)