今天来说一说,在面试的时候,面试官经常会问到的TCP三次握手和四次挥手。上一篇给大家简单介绍了计算机网络的五层体系结构,那我们今天就来一起聊一聊传输层TCP协议的三次握手和四次挥手。
为什么要三次握手、四次挥手?
有些人可能会疑问了,TCP的三次握手跟四次挥手是什么东西?为什么要三次握手和四次挥手?
TCP协议在传输数据的时候,客户端(Client)跟服务端(Server)会建立连接,然后把需要传输的文件进行分段,以及提供可靠传输和流量控制,在数据传输完成后,当前的会话也要断开连接,避免资源浪费。其实TCP的三次握手就是建立连接的过程,而四次挥手就是断开连接的过程。
TCP三次握手
怎么去更好的理解TCP的三次握手过程, 就好比我在我女神表白。
我: "女神,我喜欢你很久了,你能听到吗?"
女神:“我能听到,我也喜欢你,你能听到吗?”
我: “我也能听到,那我们在一起吧。”
然后我就跟我女神幸福快乐的在一起了(嘻嘻.jpg)。让我们跟着这个图,来说一下每一次握手发生了什么。
TCP协议还有一个特点就是 面向字节流 ,它会把数据都分成一个个字节,然后进行分段传输,在分段传输的时候,每一段是由不同的字节序号组成的。
先介绍一下图中的一些字段:
SYN:同步序列号,是用来建立连接的握手信号。
ack:确认序号,当ACK为1时,ack有效,当ACK为0时,ack无效。
seq:序号。
ACK:确认序号有效。
FIN: 结束标志,用来表示断开连接。
最开始的时候,客户端和服务端都是Closed(关闭)状态,准备发送连接请求前,Server会进入LISTEN(监听)状态。
- 第一次握手: 客户端(Client)会给服务端(Server)发送请求报文段,并指定同步序列号SYN = 1,ACK = 0, 初始序号为seq = x,(seq里面就是字节的序号),同时TCP的客户端进程进入SYN-SENT(同步已发送)状态。
- 第二次握手: 服务端收到客户端发送的请求报文SYN后,会向客户端发送一个SYN报文作为应答,表示同意建立连接,同时指定了自己的SYN = 1, ACK = 1,还会向客户端发送seq = y,来表示自己的一个初始序号,同时也会告诉客户端下一次应该从哪开始发送的确认序号,由于客户端发送过来的初始序号seq = x, 所以确认序号ack = x + 1,这时,TCP的服务端进入SYN-RCVD(同步收到)状态。
- 第三次握手: 客户端收到服务端的确认报文之后,会再次向服务端发送确认信息,表示已经收到。所以ACK = 1, seq = x + 1, ack = y + 1。TCP建立连接,客户端和服务器进入ESTAB-LISTEND(已建立连接状态)状态。
为什么握三次手,握两次或者四次行不行?
通过上面的三次握手,我们可以很清楚的知道,每一步握手的目的是什么。
第一次握手
客户端发送建立连接的请求报文段,服务端收到了,这样服务端能够得出客户端的发送和自己的接收是没有问题的。
第二次握手
服务端也发送一个请求报文段,表示自己收到了来自客户端的建立连接请求报文段,同时客户端也收到服务器响应的这个报文段,这样客户端能够得出服务端的发送和自己的接收是没有问题的,但是服务端并不知道客户端的接收有没有问题(只是客户端自己知道)所以还需要第三次握手来告知服务端。
第三次握手
客户端接着发送请求报文段,表示自己收到了服务端的确认信息,然后服务端也正常收到了。自此,握手结束,可以知道双方的发送和接收是正常的。
假如只握手了前两次,会造成什么样的后果呢?客户端第一次向服务端发送了建立连接的请求报文段,有可能因为网络的原因滞留了,客户端就会认为这个请求失效了,会重新向服务端发送一个连接请求,然后服务器正常响应连接。在某个时间段,第一次发送的连接请求才到达了服务端,如果没有第三次握手确认,那么此时服务端会误认为客户端又发了一个新的连接请求,会再一次响应客户端,客户端收到响应请求,发现这个请求刚刚已经发过了,而且也收到了服务端的响应,就忽略这个请求,但此时的服务端却还一直等待客户端的响应,这样就会造成资源的浪费。
假如握手了四次,又会是什么样子呢?第三次握手,服务端最后一次收到客户端的响应请求之后,如果此时进行第四次握手的话,那么应该是服务端接着去响应刚刚收到的这个请求,这个请求不管失败还是成功,意义都不大。因为三次握手之后,客户端和服务端都已经知道了双方的发送和接收是正常的,是可以进行数据传输的,就不需要再次发送确认请求了,而且完全可靠的通信协议也是不存在的。
TCP四次挥手
再给大家举一个例子。A和B是一对情侣,一天,A对B说
A:“咱们分手吧。”
B(震惊):“嗯。”
...沉默ing...
B:"那就分手吧。"
A: "嗯。"
然后A和B就分开了。同理,TCP断开连接,也要经历四次挥手的过程。
- 第一次挥手: 客户端主动断开连接,向服务端发送FIN报文段,即连接释放报文段(FIN=1,序号seq=u),并且状态变为FIN-WAIT-1(终止等待1)。
- 第二次挥手: 服务端收到客户端的FIN报文段,响应请求,也向客户端发送一个ACK = 1,seq = y, ack = u + 1(三次握手阶段已经解释过seq和ack,没看懂的可以重新看一遍) 的报文段,来表示自己已经收到了客户端的断开连接的请求报文,同时将自己的状态变为CLOSE-WAIT(关闭等待)。
客户端收到第二次的挥手报文之后,也会将自身的状态变为FIN-WAIT-2(关闭等待),此时客户端已经断开了对服务端的连接,也就是说,客户端不能再向服务端发送数据了,但是服务端并没有断开连接,它仍然要向客户端发送未发送完的数据,即TCP处于半关闭状态,这也是为什么要四次挥手的原因。
- 第三次挥手: 等待一段时间,当服务端将剩余数据发送完后,也会向客户端发送一个FIN = 1 的报文段,其中seq = w, ack = u + 1, ACK = 1,同时,自身进入LAST-ACK(最后确认)状态,来等待客户端的ACK。
- 第四次挥手: 客户端收到服务端的断开释放报文段FIN,同样对它进行响应,向服务段发送ACK = 1 ,seq = u + 1, ack = w + 1的报文段,并且自身进入TIME-WAIT状态,一段时间过后,服务端收到客户端的响应报文,将自身状态变为Closed,客户端等待TIME-WAIT时间过后,也将变为Closed状态。四次挥手完毕,客户端和服务端断开连接。
为什么最后一次挥手,客户端还要进入一个TIME-WAIT状态?
如果客户端最后一次挥手发送的确认请求报文,服务端没有收到的话,服务端就会认为,是因为它自己发送的FIN报文段没有发送出去,导致客户端没有收到,客户端没有收到,就不会给它发送确认请求报文,于是,服务器会再次发送FIN报文段,所以有一个时长为2MSL的等待时间。