【面试必会】全网最具深度的三次握手,腾讯 Android 开发面试记录
第二次握手:SYNACK 报文
当收到服务器发来的
SYNACK
报文段后,客户端也需要给该连接分配缓存和变量,然后再次发送一个确认报文给服务端,其中,SYN
标志位设置为0
,将确认号
设置为server_isn + 1
,另外,此次报文可以携带负载数据:
第三次握手:ACK 报文
细节拓展
三次握手的状态转换图(建议达到能默写下来的熟练程度)
![三次握手状态图](https://mmbiz.qpic.cn/mmbiz_jpg/QhRDUrJbPe7Uj5vVP4B82jibfFeD6FwMfqMzMdYwWWIND3woAC
Ab6IW8kCic2WtVQrbS6Ud9J17xwOvv8cz6dbng/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1)三次握手状态图
服务器为什么要使用特殊的初始序号
server_isn
?这为什么是必要的呢?
这个细节和问题深究第3题
是一致的,服务器使用特定的初始序列号 server_isn
(从源
和目的地IP
和端口
的散列
中获取)可以用来抵御 SYN 洪水攻击。具体为什么,以及什么是SYN 洪泛攻击
,问题深究部分我们会再详谈。
为了下文描述方便,我们将三次握手分别称为:
SYN 报文
、SYNACK 报文
、ACK 报文
问题深究
1.为什么要三次握手而不是两次?
简单来说,三次握手的目的是为了让双方验证各自的接收能力和发送能力。
第一次握手,A 发送
SYN
到B
,B
接收到了后,能确认什么呢?显然,B
能确认A
的发送
能力和B
的接收
能力;第二次握手,
B
发送SYNACK
到A
,A
接收到后,能确认什么呢?A
能确认B
的发送能力和A
自己的接收能力,此外,A
收到了SYNACK
,那么说明前面A
发的SYN
成功到达B
的手中,所以也能确认A
自己的发送
能力和B
的接收
能力;至此,A
已经确认了双方各自的发送能力和接收能力都是OK
的,因此转为ESTABLISHED
状态;第三次握手,
A
发送ACK
到B
,B
接收后,能确认什么呢?
直接的,B
能确认A
的发送
能力和B
的接收
能力,另外由于B
能收到ACK
说明前面发送的SYNACK
已经成功被接受了,说明能确认A
的接收
能力和B
的发送
能力。
如果使用两次握手,就不能确认上述所说的四种能力,那么就会导致问题。
假定不采用第三次报文握手,那么只要 B 发出确认,新的连接就建立了。
现假定一种异常情况,即A
发出的SYN
报文段并没有丢失,而是在某些网络节点长时间滞留了,以致延误到连接释放后的某个时间才到达B
。本来这是一个早已失效的报文段。但B
收到此失效的连接请求报文段后,却误以为是A
又发出一次新的连接请求,于是就向A
发出确认报文段,同意建立连接。
由于现在A
并没有发出建立连接的请求,因此不会理睬B
的确认,也不会向B
发送数据,但B
却以为新的运输连接已经建立了,并一直等待A
发来的数据。B
的许多资源就这样白白浪费了。
2.两个 TCP 建立请求相互之间同时发起时会发生什么?建立几个连接?
首先理解题意,我们知道三次握手
正是建立TCP连接
的过程,我们假设 A 是客户端,B 是服务端:
rfc793-同时启动
这里先给大家讲一下上图中一些符号的含义:
右箭头 (-->) :从 A 发送到 B 的 TCP 报文段,且 B 接收到了;
左箭头 (<--) :从 B 发送到 A 的 TCP 报文段,且 A 接收到了;
省略号 (…) :TCP 报文段仍在网络中(delayed);
丢失 ("XXX") :TCP 报文段丢失或者被拒绝。
注释会放在括号中;
TCP 状态代表了处于中间的报文段到达之后的状态(AFTER);
报文段的内容只显示了序列号(SEQ)、控制符(CTL)和 ACK,其余内容被省略。
接下来我们详细来看看上图中,两个请求同时相互发起时,两个TCP
均会经历如下状态的转换:
同步请求的状态转换
上述的状态转换图可以跟前面的三次握手的转换图进行对比理解,同时发起的两个请求最终只会建立一个连接
。
SYN-RECEIVED
跟SYN-RCVD
是一样的,前者rcf793
中的描述方法,后者是《计算机网络-自顶向下方法》中的使用方法。
3. 客户端正在和服务端建立 TCP 连接,然而当服务器变 SYN-RCVD 后,此时一个旧的 SYN 报文 又到达了,服务器会如何处理?
其实这道题更加深挖了TCP 建立连接
的过程,我们可以在rfc793
中了解到详细信息。
rfc793-RST
从上图可以看到,第三行就是旧的SYN 连接
到达服务器时,第四行是服务器照常返回,第五行是客户端给服务端发送RST 报文
,将服务端重置为LISTEN
。
我们需要从上图了解到的一点是,服务端在SYN_RECEIVED
状态下,接收到旧的SYN 报文
时是不能作出判断的,而是照常返回,当客户端接收到该报文后发现异常,才会发送RST 报文
,重置连接。
关于RST 报文
,我一开始也很疑惑,直到看到rfc793 原文
:
rfc793-page33
确实说明了TCP B
不能检测这个旧的SYN 报文
是否正确,所以正常返回。而客户端收到会进行检测,发现是旧的报文,就会返回RST 报文
。
4.第三次握手失败了怎么办?
这个问题在网上找到的答案质量参差不齐,翻阅了rfc793
,仔细研究后,最终整理出以下答案:
首先考虑失败的情况:
ACK 报文丢失导致第三次握手失败
当客户端收到服务端的SYNACK
应答后,其状态变为ESTABLISHED
,并会发送ACK
包给服务端,准备发送数据了。如果此时ACK
在网络中丢失(如上图所示),过了超时计时器后,那么服务端会重新发送SYNACK
包,重传次数根据/proc/sys/net/ipv4/tcp_synack_retries
来指定,默认是5
次。如果重传指定次数到了后,仍然未收到ACK
应答,那么一段时间后,Server
自动关闭这个连接。
问题就在这里,客户端已经认为连接建立,而服务端则可能处在SYN-RCVD
或者CLOSED
,接下来我们需要考虑这两种情况下服务端的应答:
服务端处于
CLOSED
,当接收到连接已经关闭的请求时,服务端会返回RST 报文
,客户端接收到后就会关闭连接,如果需要的话则会重连,那么那就是另一个三次握手了。服务端处于
SYN-RCVD
,此时如果接收到正常的ACK 报文
,那么很好,连接恢复,继续传输数据;如果接收到写入数据等请求呢?注意了,此时写入数据等请求也是带着ACK 报文
的,实际上也能恢复连接,使服务器恢复到ESTABLISHED
状态,继续传输数据。
这个结论也可以在STACKFLOW
上找到验证:
What if a TCP handshake segment is lost?
上图圈住的部分:
总的来说,如果一个
ACK 报文
丢失了,但它的下一个数据包没有丢失,那么连接正常,否则,连接会被重置。
5.知道 SYN 攻击吗?如何防范?
所谓SYN 洪泛攻击
,就是利用SYNACK 报文
的时候,服务器会为客户端请求分配缓存,那么黑客(攻击者),就可以使用一批虚假的ip
向服务器大量地发建立TCP 连接
的请求,服务器为这些虚假ip
分配了缓存后,处在SYN_RCVD
状态,存放在半连接队列
中;另外,服务器发送的请求又不可能得到回复(ip 都是假的,能回复就有鬼了),只能不断地重发请求
,直到达到设定的时间/次数后,才会关闭。
服务器不断为这些半开连接
分配资源(但从未使用),导致服务器的连接资源被消耗殆尽,不过所幸,我们可以使用SYN Cookie
进行有效地防御。
所谓的
SYN Cookie
防御系统,与前面接收到SYN 报文
就分配缓存不同,此时暂不分配资源;同时利用SYN 报文
的源
和目的地IP
和端口
,以及服务器存储的一个秘密数
,使用它们进行散列,得到server_isn
,然后附着在SYNACK 报文
中发送给客户端,接下来就是对ACK 报文
进行判断,如果其返回的ack
字段正好等于server_isn + 1
,说明这是一个合法的ACK
,那么服务器才会为其生成一个具有套接字的全开的连接。
SYN Cookie 防御
当然这种方案也有一定
缺点
,最明显的就是服务器不保存连接的半开状态
,就丧失
了重发SYN-ACK消息
的能力,这一方面会降低正常用户的连接成功率,另一方面会导致某些情况下正常通信的双方会对连接是否成功打开产生误解,如客户端发给服务端的第三次握手消息(ACK
)半路遗失,客户端认为连接成功了,服务端认为没收到ACK
,连接没成功,这种情况就需要上层应用采取策略特别处理了。
6.(ISN)是固定的吗?
不固定,client_isn
是随机生成的,而server_isn
则需要根据SYN 报文
中的源、ip和端口
,加上服务器本身的密码数
进行相同的散列得到,显然这也不是固定的。
7.三次握手过程中可以携带数据吗?
讲过程的时候其实已经讲了,第三次握手是可以携带数据的
,而前两次不行。
8. 关于 https 的认证过程?
限于篇幅,此处暂时不讲,留意后续文章。
四次挥手
和握手
类似,每次挥手
也代表一次报文的发出和接收。
过程描述
因为自顶向上这本书里面的图比较简略,这里采用谢希仁版本的计算机网络中的图:
计算机网络-谢希仁-四次挥手
首先,当前客户端和服务器的状态都为ESTABLISHED
,接下来我们详细讲解下上图的过程:
客户主机发起连接释放的请求,设置
FIN
为1
,当然,序号seq
也会带上,这里假设为u
;发送完毕后,客户端进入FIN-WAIT-1
状态。
评论