DTLS-握手为什么常失败

在做弱网测试的时候, 发现了一个奇怪的问题, 我们分别在上行和下行通道上施加从 5% , 10% 到 20% 的丢包,

当仅在上行通道施加丢包时, 一切运行良好, 我们的 FEC/RTX 功能使得音视频通话依然流畅,

可是在下行通道施加丢包时, 重新连接服务器常常不成功, 时常出现连接失败的情况, 但是机率也不是 100%,

经过 wireshark 抓包发现一个 DTLS handshark 经常会不成功, DTLS v1.2 协议中规定的握手流程如下

lua 复制代码
Client                                          Server
------                                          ------

ClientHello             -------->                           Flight 1

                        <-------    HelloVerifyRequest      Flight 2

ClientHello             -------->                           Flight 3

                                          ServerHello    \
                                          Certificate*     \
                                    ServerKeyExchange*      Flight 4
                                    CertificateRequest*     /
                        <--------      ServerHelloDone    /

Certificate*                                              \
ClientKeyExchange                                          \
CertificateVerify*                                          Flight 5
[ChangeCipherSpec]                                         /
Finished                -------->                         /

                                    [ChangeCipherSpec]    \ Flight 6
                        <--------             Finished    /

而我发现的握手不成功的流程如下

很显然, 我在下行通道施加丢包时, 可能会导致 Server 发回给 Client 的 new session ticket 等消息丢失, 可是 Openssl 1.1 在实现时虽然按照协议对握手消息进行了重传, 例如 client 如果没有收到来自 server 的期望的消息, 它就会重传.

lua 复制代码
Client                                   Server
------                                   ------
ClientHello           ------>

                        X<-- HelloVerifyRequest
                                         (lost)

[Timer Expires]

ClientHello           ------>
(retransmit)

Server 也一样会重传, DTLS 协议定义了一个简单的超时重传的状态机

sql 复制代码
              +-----------+
              | PREPARING |
        +---> |           | <--------------------+
        |     |           |                      |
        |     +-----------+                      |
        |           |                            |
        |           | Buffer next flight         |
        |           |                            |
        |          \|/                           |
        |     +-----------+                      |
        |     |           |                      |
        |     |  SENDING  |<------------------+  |
        |     |           |                   |  | Send
        |     +-----------+                   |  | HelloRequest
Receive |           |                         |  |
   next |           | Send flight             |  | or
 flight |  +--------+                         |  |
        |  |        | Set retransmit timer    |  | Receive
        |  |       \|/                        |  | HelloRequest
        |  |  +-----------+                   |  | Send
        |  |  |           |                   |  | ClientHello
        +--)--|  WAITING  |-------------------+  |
        |  |  |           |   Timer expires   |  |
        |  |  +-----------+                   |  |
        |  |         |                        |  |
        |  |         |                        |  |
        |  |         +------------------------+  |
        |  |                Read retransmit      |
Receive |  |                                     |
   last |  |                                     |
 flight |  |                                     |
        |  |                                     |
       \|/\|/                                    |
                                                 |
    +-----------+                                |
    |           |                                |
    | FINISHED  | -------------------------------+
    |           |
    +-----------+
         |  /|\
         |   |
         |   |
         +---+

      Read retransmit
   Retransmit last flight

由此分析, 然后查看相关代码, openssl 1.1 的实现中虽然有超时和重传, 可是它忽略了一个地方, 当服务器发送 "new session ticket " 等消息, 也就是最后一个握手 Flight 后, 它就认为握手结束了, Server服务器端不再处理握手消息了, 可是由于丢包, client 没有收到来自 server 的最后一个握手消息, client 会重新发送上一条握手消息, 期待 server 有所响应, 可以 server 并没有理睬它.

知道了原因, 改起来就快了, 我在openssl 调用的上层缓存了 server 发出去的最后一条握手消息, 当 client 在 server 握手流程结束后依然发送握手消息, server 就马上发送回之前缓存的握手消息, 从而完美地解决了这个问题, 新的 Flow 如下

相关推荐
iOStanhaitao2 小时前
23.视频播放器项目实战-音视频播放
音视频开发
数据知道5 小时前
指纹浏览器:DNS 泄漏防范与 WebRTC 本地 IP 屏蔽的底层实现
爬虫·网络协议·tcp/ip·安全·webrtc·数据采集·指纹浏览器
iOStanhaitao18 小时前
6.第一个c++安卓程序编译运行
音视频开发
换个昵称都难1 天前
webrtc源码解析概要介绍
webrtc
换个昵称都难1 天前
WebRTC 完整调用流程(前端纯 JS 实现,最简可运行)
webrtc
换个昵称都难2 天前
webrtc 拥塞控制GCC 和PCC
webrtc
Cxiaomu2 天前
React接入WebRTC实时视频实践
react.js·音视频·webrtc
AndyHuang19762 天前
WebRTC 强制 Relay 模式下 TCP 重连失败深度排查与优化实战
webrtc
换个昵称都难2 天前
webrtc pacing 平滑发包模块
webrtc
换个昵称都难2 天前
webrtc 音频混音介绍
音视频·webrtc