TCP 的三次握手,也是需要确认双方的两样能力: 发送的能力和接收的能力。于是便会有下面的三次握手的过程:
握手过程
刚开始客户端处于 Closed
的状态,服务端处于 Listen
状态。 进行三次握手:
握手 | 描述 |
---|---|
1️⃣ | 客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN(c)。此时客户端处于 SYN_SEND 状态。 |
2️⃣ | 服务器收到客户端的 SYN 报文之后,会以自己的 SYN + ACK 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s)。 同时会把客户端的 ISN + 1 作为 ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_REVD 的状态。 |
3️⃣ | 客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。 |
从图中可以看出,SYN 是需要消耗一个序列号的,下次发送对应的 ACK 序列号要加 1,为什么呢?只需要记住一个规则:
凡是需要对端确认的,一定消耗 TCP 报文的序列号。
SYN 需要对端的确认, 而 ACK 并不需要,因此 SYN 消耗一个序列号而 ACK 不需要。
为什么需要三次握手,两次不行吗?
试想如果是用两次握手,则会出现下面这种情况:
如果是两次,你现在发了 SYN 报文想握手,但是这个包滞留在了当前的网络中迟迟没有到达,TCP 以为这是丢了包,于是重传,两次握手建立好了连接。
看似没有问题,但是连接关闭后,如果这个滞留在网路中的包到达了服务端呢?这时候由于是两次握手,服务端只要接收到然后发送相应的数据包,就默认建立连接,但是现在客户端已经断开了。
看到问题的吧,这就带来了连接资源的浪费。
三次握手过程中可以携带数据吗?
第三次握手的时候,可以携带。前两次握手不能携带数据。
如果前两次握手能够携带数据,那么一旦有人想攻击服务器,那么他只需要在第一次握手中的 SYN 报文中放大量数据,那么服务器势必会消耗更多的时间和内存空间去处理这些数据,增大了服务器被攻击的风险。
第三次握手的时候,客户端已经处于 ESTABLISHED
状态,并且已经能够确认服务器的接收、发送能力正常,这个时候相对安全了,可以携带数据。
同时打开会怎样?
如果双方同时发 SYN
报文,状态变化会是怎样的呢?
这是一个可能会发生的情况。
状态变迁如下:
在发送方给接收方发 SYN
报文的同时,接收方也给发送方发 SYN
报文,两个人刚上了!
发完 SYN,两者的状态都变为 SYN-SENT
。
在各自收到对方的 SYN
后,两者状态都变为 SYN-REVD
。
接着会回复对应的 ACK + SYN
,这个报文在对方接收之后,两者状态一起变为 ESTABLISHED
。
这就是同时打开情况下的状态变迁。