TCP的三次握手四次挥手,估计大家都听过。但是真的能把每一步说明白的人比较少。我还记得在之前面试的时候被面试官一顿问,然后一脸懵B... 都是大学没好好上课 ,这篇文章就跟大家讲讲到底这三握四挥是在搞什么飞机。
三次握手
握手是指的双方进行连接的操作。
为什么是三次握手
确认通信能力
我们要明白,如果需要进行通信,首先需要保证的是双方都具有发信和收信能力。在不知双方能力状态下进行的通信都是无法保证可靠性和通信效率的。那么通信双方如何确认对方的通信能力呢?
- A请求B进行连接。(B已确认B的收信能力和A的发信能力)
- B返回ACK相应。(A已确认双方的收发能力)
- A返回ACK并建立连接。(B确认双方收发能力) 上面可以看到,至少是进行三次握手,才能确认双方能力。
防止产生脏数据连接
网络通信情况复杂,不可能保证每一消息都能正常到达其目的地。在TCP连接中,TTL的网络报文的生存时间一般都会比TCP的连接超时时间要长。这样就有可能出现一个问题。A在发送第一次连接请求时,可能网络拥塞,导致数据包未短时间内到达。到达超时时间后,A又发送了一次连接请求,这次正常进行连接。连接结束断开后,A的第一次连接请求到达B,B返回ack。如果是两次握手,A通过当前的状态,直接拒绝B的请求,但B会单方面认为连接已经建立,实际上并不是...
每一次握手到底是传输了什么?
想知道除了ip和端口号,每次传输的信息是什么,我们首先得知道TCP传输信息的结构。
第一次握手
- 首先会将TCP的header部分的控制位(上图的flags)中的SYN置为1。表示希望建立连接。
- 还有一个重要的数据,seq,即序号。这个东西是干嘛用的呢?这个涉及到TCP安全性和可靠性,与ack即确认号紧密相关。TCP在传输数据时,如果数据比较大,会进行拆分操作,将大数据拆成一个个小的数据包。序号就是在这个时候用的。我们必须要知道这个包的顺序是什么,才能把真正的数据在服务端还原。seq的顺序并不是从0或者1开始的,而是一个随机值。因为如果序号从1开始,那么整个通信的过程非常容易被预测。正因为是随机的,所以对方不知道你的seq,所以我们需要在开始收发数据之前,将seq先发送给对方。序号的初始值的传递就是通过SYN=1的操作传递的。
客户端此时处于同步状态,即可以建立连接。服务端处于监听状态。
第二次握手
- 标志位。因为已经收到了客户端的建立连接请求。所以必须要发送ack,以告知客户端自己已经收到消息。所以ACK的标志位要置为1,且SYN也是1,因为还未建立连接。
- seq。同第一次握手一样,也是一个随机值(TCP为全双工,所以双方都需要保留seq方便处理数据包)。
- ack。是对客户端发过来的序列号进行计算得到的。
服务端处于SYN接收状态。
第三次握手
- 标志位。此时只需要ACK=1。SYN已经不需要了,双方已经同步完seq等信息。
- seq。可以说是第二次握手收到的ack。
- ack。是对第二次握手收到的序列号进行计算得到的。用以告知已收到二次握手信息。
客户端处于连接建立状态,服务端收到信息之后也会进入连接建立状态,双方可以进行通信。
四次挥手
TCP连接在数据传输完成之后需要关闭,不然会一直占用系统资源。TCP的连接关闭需要四次通信才行,分手也是个麻烦事儿。
为什么四次才能断开连接呢?
分手这件事情,两边都说明白,分别断开即可。但是想要确认对方都要断开,那么一次两次是不够的。
我们还是拿A、B来举例。假设A要主动断开和B的连接。
- A发送断开请求。(需要等待B的回复,不然B未收到消息 就单方面的断开有点不负责任。超时重传)
- B收到请求并回复A。(B收到请求后,很伤心,但是没有办法,只能断开连接,但是B是被动的接收断开,所以需要通知其应用程序做关闭准备)
- B这边准备完了,通知A可以断开了。
- A回复B,我收到消息了,断开连接吧。
请求发送的信息是什么?
关闭由哪方开始?
答案是哪边都可以,无论是客户端还是服务器端,都可以主动发起关闭请求。发起关闭请求的前提是数据发送完毕,不一定非得等待对方确认完成。
第一次挥手
假设客户端发起关闭请求。那么客户端发完消息后,会进入FIN_WAIT阶段。此处已经无法再发送应用程序消息,只能处理关闭相关信息。
第二次挥手
服务端收到第一次挥手消息后,返回收到消息的ack。服务端会进入CLOSE_WAIT阶段。这个阶段是等待关闭阶段,通知应用程序发送剩余数据,处理现场信息,关闭相应资源。
第三次挥手
你应该有点好奇为什么第三次挥手和第二次挥手都是同一个服务。第二次主要是一个ack响应,第三次主要是一个服务端关闭通知消息。两者目的不同。等到三次挥手完毕,那么服务端会进入LAST_ACK状态,即等待最后客户端(主动发起关闭的一方)的ack确认。
第四次挥手
第四次挥手是主动发起关闭的一方A,对被动关闭一方B的FIN消息的确认。第四次信息发送完成后,A会进入TIME_WAIT阶段,而不是直接删除套接字。具体原因我们在下面讲。收到第四次挥手信息后,B会直接进行关闭操作。
为什么会有TIME_WAIT?
还是那句话,网络并不是一个理想世界,任何异常情况都有可能发生。为了保证TCP连接能够正常关闭,主动发起关闭方不能直接删除套接字,而是需要经过一段时间等待。这个时间一般是2MSL(Maxumun Segment Lifetime 最大报文生存时间)。原因如下
- 确认被动关闭方能够正常进入关闭状态。假设B未收到A最后的一个ack,会再次发送FIN消息(第三次挥手),如果A已经CLOSE了,那么B就会一直重试发送。所以A必须要进行一段时间的等待。
- 防止失效请求。假设A关闭了此次连接,又重新在原来的端口号上开启了新的连接。原来在网络上发送的一些包(已失效但未超过ttl)到来之后,无法进行区分是否是正常的包,导致数据混乱。
举个例子来证明没有TIME_WAIT的情况。
- A断开了,并且删除了套接字。
- B没有收到A的最后一次ack,导致FIN重发。
- A重新开启了新的套接字新的连接,但是这个套接字和之前删除的套接字拥有相同的端口号。
- B后来重发的FIN会错误的跑到新的套接字,导致A开始执行断开操作。
所以TIME_WAIT也是为了防止上面的误删除。
总结
在并发量很高的时候,熟悉TCP的原理和参数调优变得尤为重要。我们关注的不仅仅是几次握手、几次挥手,也应该关注一些细节,细节决定成败。更应该多问问为什么如此设计,能解决什么问题。
后续
后续主要是与大家分享讨论一下对TCP的滑动窗口机制和可靠性保障的理解。如果有时间,可以跟大家分享一下TCP的参数等配置相关内容。