图解TCP协议中的三次握手和四次挥手
1.前言
通常,建立TCP需要三次握手
才能建立,而断开连接则需要四次握手
。
然而,昨晚同事就这个问题抛出了自己的疑问点:
- 建立TCP,为什么不能
两次握手
呢? - 为什么三次握手就有保证呢?是不是可能需要
四次握手
呢?
因此,记录一下笔记。我们的讨论内容涉及到:
- 网络链路重传、网络链路延迟
- 服务端资源消耗
- 网络攻击、网络
2. TCP经典通信状态流转
首先,来一张经典图片:
2.1 Client状态经历过程
从client(客户端)来看,状态机
包括6种
状态,分别为:
Closed
SYN-SEND
ESTABLISED
FIN-WAIT-1
FIN-WAIT-2
TIME-WAIT
2.2 Server状态经历过程
从Server(服务端)来看,状态机
包括6种
状态,分别为:
Closed
LISTEN
SYN_RCVD
ESTABLISED
CLOSE_WAIT
LAST_ACK
3.TCP通信过程
在了解阶段过程之前,先看看TCP报文。
3.1 TCP报文
比较重要的字段有:
1.序号(sequence number)
:占4个字节,指本报文段所发送的数据的第一个字节的序号。
2.确认号(acknowledgement number)
:同样占4个字节,是期望收到对方下一个报文段的第一个字节的序号。(即收到对方的报文段最后一个字节的序号加一)(序号和确认号是TCP可靠传输的关键部分
)
3.(部分)标志位(Flags)
- 确认(
ACK
):只有标志位ACK=1时确认号字段(acknowledgement number)才有效;(注意区分 确认号ACK 和 标志位 ACK
) - 同步(
SYN
):SYN=1表示这是一个“连接请求(SYN)”或“连接接受(SYN-ACK)”报文; - 终止(
FIN
):用于释放连接,FIN=1表明报文段发送完毕,要求释放连接。
4.窗口
:占2字节,窗口大小最大为65535,是用来让对方设置发送窗口的依据。滑动窗口大小,用来告知发送端接受端的缓存大小,以此控制发送端发送数据的速率,从而达到流量控制
。
3.2 TCP通信三阶段
参考文献:
整个TCP通信可以分为三个阶段:
建立连接阶段(三次握手Hello)
数据传输阶段(多次传输数据,不是本次重点)
断开释放连接阶段(四次挥手拜拜)
3.2.1 建立连接阶段(三次握手Hello)
所谓的三次握手,其实就是TCP连接的建立过程。
这里可以简单概括为影响了三个状态(不管是客户端还是服务器):
CLOSED
(简称C)SYN
(简称S)ESTABLISHED
(简称E)
这里以一个业务场景来讲述握手协议:
- 单片机菜鸟哥现场解答众粉丝技术问题(鸟哥作为服务端,众粉丝作为众客户端)
3.2.1.1 第一次握手 ——你好,我是你的粉丝张三,很高兴认识你
握手流向:客户端 ——>服务端
场景说明:
你好,我是你的粉丝张三,很高兴认识你
协议说明:
- 客户端主动往服务端发起SYN(
Seq = X
)的报文,表示请求建立新连接
,随后进入SYN
状态(从 CLOSED状态切换到SYN状态
)
3.2.1.2 第二次握手 ——你好,我是单片机菜鸟哥,很高兴认识你+1
握手流向:客户端 <——服务端
场景说明:
你好,我是单片机菜鸟哥,很高兴认识你+1
协议说明:
- 服务端在
LISTEN
状态下收到客户端的SYN报文后,会给客户端返回响应应答SYN +ACK
(Seq = Y,Ack = x +1,表示收到客户端的Seq号
),随后进入SYN
状态(服务端从LISTEN状态下切换到SYN状态
)
3.2.1.3 第三次握手 ——很好,我们可以开始技术交流了
握手流向:客户端 ——>服务端
场景说明:
很好,我们可以开始技术交流了
,这个时候鸟哥就可以开始进入粉丝答疑环节。
协议说明:
- 客户端收到
SYN+ACK
包之后,回复给服务端一个ACK
(Seq = x+1,Ack = y + 1
,Ack等于服务端Seq +1,表示收到服务端的Ack),然后进入到ESTABLISHED
状态 - 服务端收到
ACK
包之后,知道客户端收到了自己的应答,也进入到ESTABLISHED
状态 - 双方开始正常通信
整个过程都是在计算Ack和Seq的值,保证了TCP报文传输的连贯性。一旦出现某一方发出的TCP报文丢失,便无法继续"握手",以此确保了"三次握手"的顺利完成。
这里引用一个动态图:
这就是 “三次握手
”的过程。
粉丝A:你好,我是你的粉丝A,很高兴认识你
鸟哥:
鸟哥一直进入答疑,然而这是恶作剧,并没有人问。
问题1:两次握手可以吗?
1)第一次握手
- 第一次握手的时候,客户端发送 SYN 包之后,切换到 SYN 状态,这个和三次握手中的第一次是一样的。
2)第二次握手
- 假设只有两次握手,那么服务端在获得 SYN 包之后就会进入到
ESTABLISHED
状态(请注意,这里服务端比客户端提前进入了ESTABLISHED状态
,ESTABLISHED状态下会请求跟数据传输阶段有关的内存消耗
,然后等待数据通信
) - 然后,回复给客户端SYN+ACK包,表示我收到了你的连接请求。
这里就是我们讨论的点:
-
正常网络和用户请求
(也就是每个请求都不需要重发,每个请求都是正常请求)的前提下,两次握手是足够,省了一次。
-
假设此时有
网络延迟
问题,客户端连续发送两个SYN
包,是不是意味着 服务端会建立两个连接?来者不拒。(当然有人就说了,我是不是可以判断两次的SYN包呢,丢弃第二次。但是 我一个 ESTABLISHED 状态的 服务连接为什么还要消耗资源去处理 SYN 上的包呢
?这个是我们需要考虑的地方)。 -
假设网络是好的,根据
来者不拒
的原则,此时有个黑客想攻击我们的网络,不断发送 SYN 包
,是不是意味着 我们服务端需要不断建立非常庞大的无效连接
(每个连接都需要消耗内存资源) -
对标场景:
恶作剧A:你好,我是你的粉丝A,很高兴认识你,鸟哥立马进入答疑
恶作剧B:你好,我是你的粉丝B,很高兴认识你,鸟哥立马进入答疑
…
鸟哥一直进入答疑,然而这是恶作剧,并没有人问,浪费鸟哥时间。
两次握手的情况下,服务端比客户端更快进入了ESTABLISHED 状态,从资源消耗的角度来看,非常不合理(其实就跟手机用户访问网站一样,网站资源是非常珍贵的,反而是手机资源属于用户级别,浪费的只是自己的手机资源)。
结论:
两次握手导致服务端优先进入了 ESTABLISHED 状态,提前消耗了连接资源
。在普通情况(不存在网络攻击)下,客户端优先进入到 ESTABLISHED 状态,也是属于主动请求的一种表现,服务端收到ACK包之后才考虑消耗自己的资源,达到C-S的状态同步。
问题2:三次握手就可以了吗?是否考虑4次?
在不考虑网络攻击的情况下,三次握手已经满足了我们的要求,但是仍然解决不了网络攻击的问题。那么4次握手能解决网络攻击吗?
4次握手,其实就是形成了一个环形结构
。从哪里来,回到哪里去。
这里你会发现,就算多一次,仍然解决不了,因为客户端可以伪装发送ACK,服务端一样会进入到 ESTABLISHED 状态。而且4次握手,白白浪费了一次传输时间 MSL。
3.2.2 断开释放连接阶段(四次挥手拜拜)
四次挥手即TCP连接的释放(解除):
1)第一次握手,客户端提出释放连接,发送 FIN(Seq = U),然后进入FIN-WAIT-1,也就是半关闭状态。停止客户端向服务端发送数据,但是客户端仍然能接收服务端传输过来的数据。(客户端从 ESTABLISHED 状态 变更为 FIN-WAIT-1 状态
)
2)第二次握手服务端收到客户端的FIN 包后,知道了客户端想要释放连接,随后服务端从 ESTABLISHED
状态 切换到 CLOSE- WAIT
状态,并且返回一个 ACK 包(Seq=V,Ack=U+1)给 客户端。记住,这个时候服务端可能还没有完全接收完客户端的数据
(网络延迟),还在继续接收数据。客户端收到 ACK包之后,会进入到 FIN-WAIT-2
状态.
3)第三次握手,当服务端确收完了所有数据,就会给客户端发送 FIN
(Seq=w,Ack=U+1
)包,表示我真的接收完数据了,此时会从 CLOSE-WAIT状态切换到 LAST-ACK状态
。
4)第四次握手,当客户端收到 FIN 包之后,会给服务端也发送一个 ACK 包(Seq = U+1,ack=w+1
),同时给双方一个认真考虑是否真的释放连接关系的时间(考虑时间为2MSL,也就是一个网络来回的时间,也就是发送一个ACK包和FIN包的时间,刚好够服务端再次发送一个FIN包)。在考虑时间内,如果没有再次收到 FIN包,客户端就认为 服务端收到了 ACK 包,我们的关系到此结束。
业务场景:
…(省略好多字的交流)
粉丝A:我的整个问题问完了,谢谢鸟哥
鸟哥:没事,谢谢灵通
鸟哥:还有其他问题吗?
粉丝A:没有了,谢谢。
4.总结
源于与同事的互水讨论,更加对 TCP协议中的三次握手和四次挥手有更深刻的理解,在此谢谢大佬。