TCP三次握手
一句话:在TCP连接建立过程中,客户端首先向服务端发送SYN报文,服务端接收到SYN报文后会回复一个SYN+ACK报文,客户端再回复一个ACK报文,这样就完成了TCP连接的建立。
详细过程
刚开始客户端处于 Closed
的状态,服务端处于 Listen
状态。
缩写
SYN
: Synchronize Sequence Numbers
,同步序列号
ISN
: Initial Sequence Number
,初始同步序列号
第一次握手
客户端给服务端发一个 SYN
报文,并指明客户端的初始化序列号 ISN
。
此时客户端处于 SYN_SENT
状态。
第二次握手
服务器收到客户端的 SYN
报文之后,回复一个 ACK
报文作为应答,把客户端的 ISN + 1
作为 ACK
的值,表示已经收到了客户端的 SYN
,约定从数据流从这个数开始。并且指定自己的初始化序列号 ISN
。
此时服务器处于 SYN_REVD
的状态。
第三次握手
客户端收到 SYN
报文之后,回复一个 ACK
报文,也是一样把服务器的 ISN + 1
作为 ACK
的值,表示已经收到了服务端的 ACK
,并约定数据流从这个数开始。
此时客户端处于 ESTABLISHED
状态。服务器收到 ACK
报文之后,变为 ESTABLISHED
状态,双方就建立起了连接。
发送第一个 SYN
的一端将执行主动打开(active open),接收这个 SYN
并发回下一个 ACK
的另一端执行被动打开(passive open)
为什么需要三次握手
弄清这个问题,我们需要先弄明白三次握手的目的是什么,能不能只用两次握手来达到同样的目的。
三次握手的目的是建立可靠的连接,两次握手无法达到这个目的。
第一次握手:客户端发送网络包,服务端收到了。
这样服务端就能得出结论:客户端的发送能力是正常的。
第二次握手:服务端发包,客户端收到了。
这样客户端就能得出结论:服务端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
如果这个阶段就视为连接建立成功,那么:
后续服务端发送的数据,客户端可能收不到,这是不可靠连接。
服务端成功建立连接是会为连接创建 fd 并放入全连接队列,但如果客户端的接收能力有问题,那么服务端的资源是浪费的。
第三次握手:客户端发包,服务端收到了。
这样服务端就能得出结论:客户端的接收能力正常。
因此,需要三次握手才能确认双方的接收与发送能力是否正常。
半连接队列
服务器第一次收到客户端的 SYN
之后,会处于 SYN_RCVD
状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,称为半连接队列。
虽然叫队列,但半连接队列其实是个 hash表
当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
服务器发送完 ACK
包,如果未收到客户确认包,则会进行退避式重传(间隔变大)。如果重传次数超过配置的最大重传次数,则会将该连接信息从半连接队列中删除。
SYN攻击
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到 SYN
洪泛攻击。SYN
攻击就是 Client 在短时间内伪造大量不存在的 IP 地址,并向 Server 不断地发送 SYN
包,Server 则回复确认包,并等待 Client 确认,由于源地址不存在,因此 Server 需要不断重发直至超时,这些伪造的 SYN
包将长时间占用半连接队列,导致正常的 SYN
请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN
攻击是一种典型的 DoS/DDoS
攻击。
检测 SYN
攻击非常容易,当你在服务器上看到大量的半连接状态的连接时,特别是源 IP 地址是随机的,基本上可以断定这是一次 SYN
攻击。在 Linux/Unix
上可以使用系统自带的 netstats
命令来检测 SYN
攻击。
线上这种情况有四个 TCP
参数可以调,第一个是:tcp_synack_retries
可以用来减少重试次数;第二个是:tcp_max_syn_backlog
,可以增大 SYN
连接数;第三个是:tcp_abort_on_overflow
处理不过来干脆就直接拒绝连接了。第四个方法是开启 cookies
功能,使用 cookie
,不使用半连接队列
ISN 是固定的吗?
是动态随机生成的。
当一端为建立连接而发送它的 SYN
时,它会为连接设一个初始序号。ISN
随时间而变化,因此每个连接都将具有不同的 ISN
。ISN
可以看作是一个 32 bit
的计数器,每 4ms
加 1
。
这样选择序号的目的在于:
这个号要作为以后的数据通信的序号,以保证收到的数据不会因为网络上的传输的问题而乱序。如果序号固定,就无法分辨顺序了
增加安全性。如果序号固定,攻击者很容易猜出后续的确认号
三次握手过程中可以携带数据吗?
其实第三次握手的时候,是可以携带数据的(HTTP3
就是这么做的)。但是,第一次、第二次握手不可以携带数据
假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN
报文中放入大量的数据,重复发 SYN
报文的话,会大量浪费服务器的资源。
而对于第三次的话,此时客户端已经处于 ESTABLISHED
状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。
TCP状态机
最后附上一张 TCP
状态机的图。
参考
Last updated