为什么说臭名昭著呢?是因为三次握手和四次挥手几乎是面试必问的问题。为了能更好的记忆,我尽量以最简洁的语言陈述。
目录
TCP报文格式
三次握手
为什么是三次?
四次挥手
为什么是四次?
TIME_WAIT状态的意义
声明:以下图片来源于网络
TCP报文格式
在介绍三次握手和四次挥手之前,先来了解一下TCP的报文信息,如下图所示:
在这里我们着重介绍一下6个控制位:URG、ACK、PSH、RST、SYN、FIN,这6个控制字段各占1位。各个控制位的功能如下:
- URG:紧急指针标识,为1时表示紧急指针有效。
- ACK:确认序号标识,为1时确认序号有效,为0时表示报文中不含确认信息。TCP规定,ACK为1的时候有效,也规定连接建立后所有发送的报文的ACK位为1.
- PUSH:为1的时候,指示接收方应该在接收到此报文以后,尽快将报文交给应用程序,而不是存放于缓存区内。
- RST:用于重置由于主机崩溃或者其他原因而出现的错误连接。
- SYN:在建立连接的时候用于同步序列号。当SYN=1而ACK=0表示这是一个连接请求报文,若同意连接,则应在响应报文中使SYN=1,ACK=1。
- FIN:用于释放TCP连接。为1时表示发送方已经没有数据发送了,即关闭本方的数据流。
了解了这些之后,我们就可以进入重头戏,看看三次握手和四次挥手了。
三次握手
首先,三次握手在客户端请求连接的时候进行,即执行到代码connect()的时候开始进行,整个过程如下图所示:
第一次握手:
首先,客户端将报文SYN置为1,然后随机生成一个序列号x发送给服务端。
第二次握手:
服务端收到客户端的报文之后,由于SYN=1,ACK=0(控制位默认为0),认为这是一个连接请求的报文。
同意连接,因此在将要发送的报文中另SYN=1,ACK=1,将确认号置为x+1,并且随机生成一个序列号y。
确认号和序列号在TCP报文格式中都有展示。
第三次握手:
客户端进行连接确认,在发送报文中令ACK=1(这时不是请求连接,因此不用设置SYN),序列号设置为x+1,确认号设置为y+1
三次握手之后TCP连接建立。
为什么是三次?
在这里解释一下,为什么是三次握手?这也是一个出现率比较高的面试题。
要回答问题,要从每次握手的意义(作用上)进行分析:
首先TCP是面向连接的,一次握手肯定不能建立连接,一条信息发出去,对方都没有回肯定建立不了连接。
同时两次握手会产生连接误判的情况。多于三次握手又会过于冗余。
第一次握手:
客户端什么都不能确认,服务端可以确认对方发送正常。
第二次握手:
客户端确认了自己发送接收正常,对方发送接收正常。
服务器确认自己接收正常,对方发送正常。
第三次握手:
客户端确认自己和对方发送接收正常。
服务器确认自己和对方发送接收正常。
从功能上看,三次握手是最有效的也是最可靠的建立连接的方式。
四次挥手
四次挥手即在终止TCP连接的时候,需要客户端和服务器共发送4各包确认连接的断开。
整个流程如下图所示:
第一次挥手:
客户端在发送的报文中令FIN=1,序列号设置为u,u等于前面已经传送过来的数据的最后一个字节的序号加1,可以参考TCP数据传输流程。客户端进入FIN_WAIT_1状态。
第二次挥手:
服务器在发送的报文中,令ACK=1,确认号置为u+1,同时设置自己的序列号V,服务器进入CLOSE_WAIT状态,客户端在收到信息以后进入FIN_WAIT_2状态。
在第二次挥手之后,在CLOSE_WAIT的持续时间内,服务端发送的消息客户端依旧接收。
第三次挥手:
服务器在发送完数据之后,CLOSE_WAIT结束之后,向客户端发送FIN+ACK包,即报文中FIN=1,ACK=1,同时确认码还为u+1,并且设置自己的序列号w。
第三次挥手之后,服务器进入LAST_ACK最后确认阶段。
第四次挥手:
客户端在收到服务器的FIN+ACK包后,向服务器发送ACK包进行确认,即报文中ACK=1,同时确认码为w+1,序列号为u+1。
之后客户端进入TIME_WAIT状态,TIME_WAIT结束后CLOSED。
而服务器在收到确认之后直接CLOSED。
为什么是四次?
第一次握手是客户端告诉服务器自己将要断开连接。
第二次握手是服务端将剩余的数据发送给客户端。
第三次握手发生在服务器发送完剩余数据之后,向客户端发送信息,表明数据发送完成,可以断开连接。
第四次握手:客户端收到第三次握手的报文后,向服务端确认自己已经收到了最后的信息,可以断开连接。并进入TIME_WAIT状态。
首先前3次挥手必须存在,相信大家都能理解,前三次确保服务器将所有信息都已经发出。
我们假设没有第四次挥手,那么服务器在不知道客户端是否收到自己发送的消息的情况下就断开了连接,就可能造成数据的丢失。
这四次挥手,可以保证TCP是一个可靠的数据流服务。
如果服务器和客户端同时关闭,四次握手将退化为3次握手。
TIME_WAIT状态的意义
TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态,这时因为我们必须假象网络是不可靠的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在客户端t发送出最后的ACK回复,但该ACK可能丢失。服务端如果没有收到ACK,将不断重复发送FIN片段。所以客户端不能立即关闭,它必须确认服务端接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。
如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。
正是上面这些机制保证了TCP的可靠服务。