1.前言
本文以博主在某次前端面试中被问到“什么是TCP协议中的三次握手和四次挥手?”为契机,经过整理教材、百度百科以及他人博客,再结合博主自身的理解,尽可能的以通俗易懂的语言来解释TCP协议中的三次握手和四次挥手的具体过程。
2.TCP连接和断开
客户端与服务端在建立TCP连接时需要经过三次握手才能建立,而断开连接则需要四次挥手。整个过程全览如下图所示:
我知道,直接看此图,相信大多数伙伴是懵逼的,下面我们就分别从建立连接和断开连接进行详细介绍。
3.“三次握手”建立连接
客户端与服务端在建立TCP连接时需要经过三次握手才能建立,其具体过程图解如下图:
上图中间的三个箭头即为三次握手。
官方说明(说也白说,还是不懂)
- 第一次握手:建立连接时,客户端发送SYN包(SYN=j)到服务器,此时客户端进入SYN_SENT状态,并挂起等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
- 第二次握手:当服务器收到客户端发来的SYN包后,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
- 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态。
至此完成三次握手。
怎么样,懵逼吧,我都说了,这种说明,说了也是白说!
博主说明(通俗易懂)
其实建立连接三次握手的过程跟我们平时打电话的过程是一样样的,我们来看这样一个场景:
某天,隔壁老王给隔壁老张打电话,有如下对话:
老王:天王盖地虎!
老张:天王盖地虎,小鸡炖蘑菇!
老王:小鸡炖蘑菇!
电话接通,老王和老张Blalalala....
下来我们结合上面三次握手的图解仔细分析下这个场景,
- 首先,老王给老张打电话,老王就是客户端,老张就是服务端。只有老王给老张打了电话,老张才会去接电话,也就是说老张不会主动去找老王,只有老王找老张,老张才会应答老王。
对应到上图中就是客户端主动打开,服务端在监听等待,只有当客户端主动发起建立连接请求的时候,服务端才会配合客户端进行连接。 - 老王先说句“天王盖地虎!”,说完后老王等待老张回应,老王进入等待状态。
这个过程对应于图中第一次握手客户端发送SYN包,发完客户端进入SYN_SENT状态。 - 老张听到老王的话后,回复老王“天王盖地虎,小鸡炖蘑菇!”在这里有个问题,许多同学会认为老张听到老王的话后应该直接回复“小鸡炖蘑菇”就可以了呀,干嘛还要再把老王的话再说一遍。其实不然,老张之所以要重复老王说的那句“天王盖地虎”,是因为老张得清楚的表明我不但能听到你说话,而且我听的非常准确,你说的是“天王盖地虎”而没有被我听成是“上山打老虎”。
这个过程就对应于图中第二次握手,当服务端收到客户端发来的SYN包后,服务端将SYN包并加上自己的ACK包一起发给客户端,告诉客户端,我不但能收到你发来的东西,我还收的很完整,没有丢包,不信我把刚收到你发来的包发给你看看。 - 老王听到老张说的“天王盖地虎,小鸡炖蘑菇!”后,心想“嗯,老张能听到我说话,而且还听得很准确,我也得告诉老张我能听到他说话。”,随后,老王就把老张说的“小鸡炖蘑菇”再说给老张,表示告诉老张我也能听到你说话,而且我也听得很准确哦。
这个过程就对应于图中第二次握手,当服务端收到客户端发来的SYN包后,服务端将SYN包并加上自己的ACK包一起发给客户端,告诉客户端,我不但能收到你发来的东西,我还收的很完整,没有丢包,不信我把刚收到你发来的包发给你看看。 - 老王听到老张说的“天王盖地虎,小鸡炖蘑菇!”后,心想“嗯,老张能听到我说话,而且还听得很准确,我也得告诉老张我能听到他说话。”,随后,老王就把老张说的“小鸡炖蘑菇”再说给老张,表示告诉老张我也能听到你说话,而且我也听得很准确哦。
这个过程就对应于图中的第三次握手,当客户端端收到服务端发来的SYN+ACK包后,客户端将ACK包再发给服务端,告诉服务端,我也能收到你发来的东西,我也收的很完整,没有丢包,不信我也把刚收到你发来的包发给你看看。 - 当老张听到老王说的“小鸡炖蘑菇”后,老张心想“嗯,老王也能听到我说话”。如此,通过以上过程,老王和老张就知道对方能听到自己说话,接下来就可以Blalalala....
听完这么一分析,是不是豁然开朗!!!
那么问题来了
建立连接一定要握手三次吗?两次握手为什么不行?四次握手为什么不行?
还是以老王给老张打电话场景为例,
先看两次握手:
老王:老张,你能听到我说话吗?(第一次握手)
老张:我能听到,老王,你能听到我说话吗?(第二次握手)
此时,两次握手结束,按理连接已经建立成功,可是老张还不知道老王能不能听到自己说话,所以接下来
老王:我们今天去老刘家打麻将吧
老张:我能听到,老王,你能听到我说话吗?
老王卒。
再看四次握手:
老王:老张,你能听到我说话吗?(第一次握手)
老张:我能听到,老王,你能听到我说话吗?(第二次握手)
老王:我能听到,老张,你能听到我说话吗?(第三次握手)
老张:我能听到。(第四次握手)
老王和老张Blalalal....。
显然,两次握手根本就无法建立连接,而四次握手虽然可以建立连接,但是耗费资源啊
4.“四次挥手断开连接”
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。断开流程如下图所示:
上图中间的四个箭头即为四次挥手。
官方说明
- 当准备断开TCP连接时,客户端作为主动关闭方向服务端发送一个FIN,用来关闭客户端到服务端的数据传送,此时客户端进入FIN_WAIT1状态。
- 服务端接收到这个FIN后,向客户端发回一个ACK确认包,此时服务端进入CLOSE_WAIT状态。
- 客户端接收到服务端发回的ACK确认包后,进入FIN_WAIT1状态。
- 服务端关闭与客户端的连接,并发送一个FIN给客户端,此时服务端进入LAST_ACK状态。
- 客户端收到服务端发来的FIN包后,发回ACK报文确认,然后进入TIME_WAIT连接度断开状态,等2MSL后客户端即可回到CLOSED可用状态了。
- 服务端接收到ACK后,便进入连接断开状态
四次挥手只有官方说明没有博主说明,是因为博主还没有相当一个特别恰当的例子来比拟这个过程,等博主想到了再来补上
5.TCP通信十种状态
TCP从连接到断开总共要经历十种状态,如下图:
- LISTEN:表示服务端的处于连接监听状态,可以接受来自客户端的连接。
- SYN_SENT:这个状态与SYN_RCV相对应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。
- SYN_RCV:这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂。
- ESTABLISHED:客户端/服务端连接建立。
- FIN_WAIT_1:该状态是当客户端SOCKET在ESTABLISHED状态时,它想主动关闭连接,向服务端发送了FIN报文,此时该客户端SOCKET即进入到FIN_WAIT_1状态。
- CLOSE_WAIT:该状态的含义是表示服务端在等待关闭连接。当客户端关闭SOCKET后发送FIN报文给服务端,服务端毫无疑问地会回应一个ACK报文给客户端,此时服务端则进入到CLOSE_WAIT状态。
- FIN_WAIT_2:当客户端处于FIN_WAIT_1状态时,当收到服务端回应ACK报文后,则客户端会立即进入到FIN_WAIT_2状态。
- LAST_ACK:该状态是服务端在发送FIN报文后,最后等待客户端的ACK报文。当收到客户端的ACK报文后,也即可以进入到CLOSED可用状态了。
- TIME_WAIT:该状态表示客户端收到了服务端的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。
- CLOSED:这个状态没什么好说的了,表示连接关闭后回到初始状态。
具体流程
连接之前,服务端处于LISTEN(1)状态,当客户端向服务端发送连接请求SYN后,客户端处于SYN_SENT(2)状态,当服务端收到请求并发送回应SYN+ACK后,服务端处于SYN_RECV(3)状态,当客户端收到服务端的回应后,客户端处于已连接ESTABLISHED(4)状态,并向服务端发送ACK,最后服务端也处于已连接ESTABLISHED(4)状态
断开之前,当客户端向服务端发送断开请求FIN之后,客户端处于FIN_WAIT1(1)状态,服务端收到FIN之后,服务端处于CLOSE_WAIT(2)状态,服务端向客户端发送ACK回应,此时客户端处于FIN_WAIT2(3)状态,服务端也即变为LAST_ACK(4)状态,服务端向客户端发送FIN请求,客户端状态变为TIME_WAIT(5),最后,客户端向服务端回应ACK,客户端和服务端状态都变为CLOSED(6)。
6.结语
至此,TCP连接的三次握手和四次挥手全部总结完了,懂没懂就看个人理解了,反正我也是费了好大劲才理清其中的关系。
原文地址:https://www.cnblogs.com/wangjiachen666/p/9610338.html