TCP的三向握手和四向挥手是面试中最常见的考点之一。很多读者都知道三四次,但问得再深一点,往往不能给出准确的答案。本文详细讲解了一个TCP连接的三次握手和四次握手。图片来自PexelsTCPConnection在客户端和服务端之间发送和返回数据的过程中,需要创建一个叫做TCPConnection的东西。由于TCP没有连接的概念,只有请求和响应。请求和响应都是数据包,它们都通过由客户端发起并由TCP创建的服务器接收的类似连接的通道。这个连接总是可以保持,HTTP请求是在这个连接之上发送的。可以在一个TCP连接上发送多个HTTP请求,这种模式在不同版本中有所不同。在HTTP/1.0中,TCP连接是在创建HTTP请求时同步创建的。向服务器发送HTTP请求,服务器响应后关闭TCP连接。在HTTP/1.1中,可以通过某种方式声明始终保持此连接。发送一个请求后,可以发送另一个请求。这样做的好处是:在创建TCP连接的过程中,需要进行“三次握手”的消耗,“三次握手”就是有3次网络传输。如果保持TCP连接,则发送第二个请求而不消耗此“三次握手”。在HTTP/2中,HTTP请求也可以在同一个TCP连接中并发传输。TCP报文格式介绍比较重要的字段有:Sequencenumber:Seq序列号,占32位,用于标识从TCP源端发送到目的端的字节流,在发起方发送数据时标记。Acknowledgementnumber:Ack序号,占32位,只有当ACK标志为1时,确认号字段才有效,Ack=Seq+1。Flags:一共6个,分别是URG、ACK、PSH、RST、SYN、FIN等。六个标志位的具体含义如下:URG:Urgentpointer(紧急指针)有效。ACK:确认序号有效。PSH:接收方应尽快将此消息传递给应用层。RST:重置连接。SYN:发起一个新的连接。FIN:释放一个连接。需要注意的是:不要将确认序号Ack和flag中的ACK混淆。确认方Ack=发起方Seq+1,两端配对。TCP的三次握手详解“三次握手”所谓三次握手就是建立一个TCP连接。这个连接必须是一方主动打开,另一方被动打开。下面是client主动发起连接的示意图:在握手前主动打开连接的client结束CLOSED阶段,被动打开的server也结束CLOSED阶段进入LISTEN阶段,然后开始“三方式握手”。①首先,客户端向服务器发送TCP报文。其中:标志位为SYN,表示“请求建立新连接”;序号为Seq=x(x一般为1);然后客户端进入SYN-SENT阶段。②服务器收到客户端的TCP报文后,结束LISTEN阶段。并返回一条TCP消息。其中:标志位为SYN和ACK,表示“确认客户端的报文Seq序列号有效,服务端可以正常接收客户端发送的数据,同意建立新的连接”(即告诉服务器已收到您的数据的客户端)。序号为Seq=y;确认号Ack=x+1,表示收到客户端的序号Seq,其值加1作为自己的确认号Ack的值;然后服务器进入SYN-RCVD阶段。③客户端收到服务器确认收到数据的TCP报文后,表明客户端向服务器发送数据正常,SYN-SENT阶段结束。并返回最后一段TCP消息。其中:标志位为ACK,表示“确认收到服务器同意连接的信号”(即告诉服务器,我知道你收到了我发送的数据)。序号为Seq=x+1,表示从服务器收到确认号Ack,将其值作为自己的序号值。确认号Ack=y+1,表示收到服务端序号Seq,其值加1作为自己确认号Ack的值;然后客户端进入ESTABLISHED阶段。服务器收到客户端的“确认收到服务器数据”的TCP报文后,可以看出服务器向客户端的数据传输是正常的。结束SYN-SENT阶段,进入ESTABLISHED阶段。在客户端和服务端传输的TCP报文中,双方的确认号Ack和序列号Seq的值是根据对方的Ack和Seq值计算出来的,保证了TCP报文传输的连续性.一旦某一方发送的TCP报文丢失,“握手”就无法继续,从而保证了“三次握手”的顺利完成。之后,客户端和服务器进行正常的数据传输。这就是“三次握手”的过程。“三次握手”的动态过程“三次握手”的通俗理解是一个栗子:将客户端比作男孩,将服务器比作女孩。用他们的交流来说明“三次握手”的过程:男孩喜欢女孩,于是写信告诉女孩:我爱你,请和我约会!写完信后,男孩焦急地等待着,因为他不认识这封信。能否顺利传达给少女。女孩收到男孩的情书后,欣喜若狂。原来我们是相爱的!于是我给男孩写了一封回信:你的情书我收到了,我明白你的用意。其实我也喜欢你!我愿意与你交往!写完信后,女孩焦急地等待着,因为她不知道回信能否顺利转达给男孩。男孩收到回信后很开心,因为女孩收到了他寄来的情书,从回信中知道女孩喜欢他,愿意和他交往。然后男孩又写了一封信告诉女孩:我收到了你的心和信,谢谢你,我爱你!女孩收到男孩的回信后也很开心,因为男孩收到了她寄出的情书。结果,男孩和女孩都知道了对方的意图,然后愉快地交流起来~~这就是通俗版的“三次握手”。期间一共交换了三个信件,这就是“三次握手”,确认两个方向网络上的数据传输通道是否正常。为什么要第三次握手是为了防止服务器打开一些无用的连接增加服务器的开销,防止无效的连接请求段突然传到服务器,导致错误。由于网络传输有延迟(通过网络光纤和各种中间代理服务器),在传输过程中,例如客户端发起SYN=1请求建立连接(第一次握手)。如果服务端直接创建连接,返回一个包含SYN、ACK、Seq的数据包给客户端,由于网络传输导致数据包丢失,客户端在丢失包后还没有收到服务端返回的数据。客户端可以设置一个超时时间,当超时时间到时,关闭连接创建请求。然后重新发出创建连接的请求,但是服务器不知道。如果没有第三次握手告诉服务器客户端已经收到服务器传来的数据,服务器不知道客户端是否收到了服务器。返回的信息。这个过程可以理解为:这样,服务器就没有创建或关闭连接端口的请求,服务器端口一直是打开的。当客户端超时重新发出请求时,服务器会重新打开一个端口连接。那么服务器端最后一个没有收到请求数据的端口总是打开的。长此以往,如果这样的端口过多,会造成服务器端开销的严重浪费。另一种情况是无效客户端发送的请求信息由于某种原因传递给了服务端,服务端认为是客户端发送的有效请求,收到后出现错误。所以我们需要“第三次握手”来确认这个过程,这样客户端和服务端就可以及时检测到网络等一些问题导致的连接创建失败,从而不需要等待就可以关闭服务端上的端口。也可以这样理解:“第三次握手”就是客户端向服务端发送数据的时候。这个数据是为了告诉服务器客户端在“第二次握手”时是否收到了服务器发送的数据。如果发送的数据是“收到”的报文,服务器收到后会正常建立TCP连接,否则会建立TCP连接失败,服务器会关闭连接端口。这减少了服务器开销和接收无效请求的错误。抓包验证下面是抓包工具抓取的一些数据包,可以用来分析TCP的三次握手:图为完整的TCP连接的“三次握手”过程。在52528→80中,52528是本地(客户端)端口,80是服务器端口。80端口和52528端口之间的三次往返就是“三次握手”过程。注意客户端在“第一次握手”发送的TCP报文中使用了[SYN]作为标志,客户端序号Seq=0。接下来,在“第二次握手”服务器返回的TCP报文中,使用[SYN,ACK]作为标志;服务器端序列号Seq=0;确认号Ack=1(“第一次握手”中客户端序列号Seq的值+1)。最后,“第三次握手”客户端以[ACK]为标志向服务器发送TCP报文;其中客户端序号Seq=1(“第二次握手”中服务器端确认Ack的值);确认号Ack=1(“第二次握手”中服务器端序号Seq的值+1).这样就完成了“三次握手”的过程,符合前面分析的结果。TCP的四次握手,我们熟悉的就是“三次握手”,因为比较简单。但是,我们并不常听到“挥四声”,即使听到了,也未必能够详细解释它的具体过程。下面详细、直观、完整的介绍一下“四波”的过程。“四次挥手”详解所谓四次挥手就是TCP连接的释放(release)。连接的释放必须是一方主动释放,另一方被动释放。下面是客户端主动发起释放连接的示意图:客户端在挥手结束ESTABLISHED阶段前主动释放连接。随后开始了“四波”。①首先,客户端要释放连接,向服务器发送TCP报文。其中:标志位为FIN,表示“请求释放连接”;序号是Seq=U。然后client进入FIN-WAIT-1阶段,也就是半关闭阶段。并停止在客户端向服务器端发送数据,但客户端仍能接收到服务器端传来的数据。注意:这里没有发送的是正常连接时传输的数据(非确认报文),不是所有的数据,所以客户端还是可以发送ACK确认报文的。②服务器端收到客户端的TCP报文后,确认客户端要释放连接,然后服务器端结束ESTABLISHED阶段,进入CLOSE-WAIT阶段(半关闭状态),返回一条TCP报文。其中:标志位为ACK,表示“收到客户端发送的释放连接的请求”。序号Seq=V,确认号Ack=U+1,表示在收到客户端报文的基础上,将序号Seq值加1作为本段确认号Ack的值的消息;然后服务器开始准备释放服务器到客户端方向的连接。客户端收到服务器的TCP报文后,确认服务器已经收到客户端的释放连接请求,然后客户端结束FIN-WAIT-1阶段,进入FIN-WAIT-2阶段。第一次“两波”不仅让服务器知道客户端要释放连接,还让客户端知道服务器理解它释放连接的请求。因此,可以确认客户端到服务器的连接是关闭的。③服务器发送ACK确认报文后,已经经过CLOSED-WAIT阶段,准备释放服务器与客户端的连接,再次向客户端发送TCP报文。其中:标志位为FIN、ACK,表示“准备释放连接”。注意:这里的ACK不是确认收到服务端消息的确认消息。序号为Seq=W,确认号为Ack=U+1,表示在收到客户端报文的基础上,将其序号Seq值加1作为本段确认号Ack的值的消息。然后服务器结束CLOSE-WAIT阶段,进入LAST-ACK阶段。并停止从服务器到客户端的方向发送数据,但服务器仍能接收到客户端传来的数据。④客户端收到服务器发送的TCP报文,确认服务器准备释放连接,结束FIN-WAIT-2阶段,进入TIME-WAIT阶段,向服务器发送报文。其中:标志位为ACK,表示“已收到服务器准备释放连接的信号”。序号为Seq=u+1;即在收到服务器端消息的基础上,将其确认号Ack的值作为该段消息的序号值。确认号为Ack=w+1;表示在收到服务器端消息的基础上,将其序列号Seq的值作为该段消息的确认号的值。然后客户端在TIME-WAIT阶段开始等待2MSL。服务器收到客户端发送的TCP报文后,结束LAST-ACK阶段,进入CLOSED阶段。这正式确认了服务器到客户端方向的连接关闭。客户端等待2MSL后,结束TIME-WAIT阶段,进入CLOSED阶段,从而完成“四次挥手”。最后的“挥手两次”不仅让客户端知道服务器准备释放连接,还让服务器知道客户端知道它准备释放连接了。因此,可以确认服务器到客户端的连接是关闭的,这样就完成了“四次挥手”。类似于“三波”,在客户端和服务端之间传输的TCP报文中,双方的确认号Ack和序列号Seq的值是根据对方的Ack和Seq值来计算的。这样就保证了TCP报文传输的连续性。一旦某一方发送的TCP报文丢失,“一波”就无法继续,从而保证了“四波”的顺利完成。“四挥”的动态过程通俗的理解是:把client比作男生,把server比作女生。通过他们的分手来说明“四次挥手”的过程:《第一次挥手》:男生看透人心久了,发现女生变成了他讨厌的样子,无法忍受,于是他决定分手,然后写信告诉女孩。《第二次挥手》:女孩收到信后,就知道男孩要和她分手了。她怒不可遏,心中暗骂:你算什么东西?你一开始不是这样的!于是她立马给男孩写信回复:分手就分手,给我点时间,我会整理你的东西,全部还给你!男孩收到女孩的第一封信后,他明白女孩知道他想和她分手了。然后等姑娘收拾东西。《第三波》:过了几天,女孩把男孩给她的东西都收拾好了,于是又写信给男孩:你的东西我收拾好了,快带走,我们永不告别以后彼此!“第四波”:男孩收到女孩的第二封信后,知道女孩已经收拾行李,可以正式分手了,于是再次写信告诉女孩:我知道了,我走了把它拿回来!这里有双方。各自的坚持:既然女孩发了第二封信,如果一天之内没有收到男孩的回信,她就会再发一封信催男孩去收拾东西!自从男孩寄出第二封信后,两天内她都没有再收到。如果收到女孩的信,你会认为女孩已经收到你的第二封信;如果你在两天内又收到女孩的来信,你会认为女孩没有收到你的第二封信,你需要再写一封信,再等两天.....如果双方的信都可以正常接收,至少只有四封信才能彻底分手!这就是“四次挥手”。为什么握手三次,挥手却要四次?作为旗帜。SYN是请求连接标志,表示服务器同意建立连接;ACK是确认信息,意思是告诉客户端,服务器已经收到了它的请求信息。即SYN连接建立报文和ACK确认报文是在同一次“握手”中传输的,所以“三次握手”既不过分也不过分,只是为了让双方能够清楚地沟通。TCP之所以在释放连接时需要“挥手四次”,是因为FIN释放连接报文和ACK确认报文分别是第二次和第三次“握手”发送的。为什么在建立连接时一起传输,而在释放连接时分开传输?当连接建立后,被动服务器结束CLOSED阶段,进入“握手”阶段,无需任何准备,可以直接返回SYN和ACK报文,开始连接。释放连接时,被动服务器突然收到主动客户端释放连接的请求,无法立即释放连接。因为还有必要的数据需要处理,服务器先返回ACK确认收到报文,只有在CLOSE-WAIT阶段准备好释放连接后,才返回FIN报文释放连接。所以是“三次握手”、“四次挥手”。为什么client在TIME-WAIT阶段要等2MSL来确认server是否收到client发送的ACK确认报文?当client发送最后的ACK确认报文时,并不确定server能不能收到。消息的这一部分。因此,客户端在发送ACK确认报文后,会设置一个时长为2MSL的定时器。MSL是指MaximumSegmentLifetime:一个TCP包在传输过程中的最大生命周期。2MSL是服务器发送的FIN报文和客户端发送的ACK确认报文能够保持有效的最长时间。如果服务器在1MSL内没有收到客户端的ACK确认报文,就会再次向客户端发送FIN报文:如果客户端在2MSL内再次收到服务器的FIN报文,说明服务器由于各种原因由于某种原因没有收到客户端发送的ACK确认报文。client再次向server发送ACK确认报文,定时器复位,重新开始2MSL计时。否则,客户端在2MSL内没有再次收到服务器的FIN报文,说明服务器已经正常收到ACK确认报文,客户端可以进入CLOSED阶段,完成“四次挥手”。因此,客户端必须经历持续时间为2SML的TIME-WAIT阶段;这就是客户端比服务器晚进入CLOSED阶段的原因。抓包验证图中展示的是一个完整的TCP连接释放的“四次挥手”过程。在80→55389中,假设80是本地(客户端)端口,55389是服务器端口。80和55389端口之间的四次来回就是“四次挥手”的过程:“第一次挥手”客户端发出的释放连接的FIN请求报文,以[FIN,ACK]为标志,其中报文序列号Seq=2445;确认号Ack=558。注意:这里“第三次握手”的ACK不是确认ACK报文。“第二波”服务器返回的ACK确认报文以[ACK]为标志;消息序号Seq=558;确认号Ack=2246。“第三波”服务器继续返回以[FIN,ACK]为标志的同意释放连接的FIN报文;消息序号Seq=558;确认号Ack=2246。“FourthWave”客户端发送的ACK,以[ACK]为标志,确认收到消息;消息序号Seq=2446;确认号Ack=559。下一个“握手”传输报文中的序号Seq值等于上一个“握手”传输报文中的确认号Ack值。下一个“握手”传输消息中的确认号Ack值等于上一个“握手”传输消息中的序号Seq值。因此,这是一个连续的“四波”过程,与前面的分析是一致的。
