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 如下

相关推荐
wangruofeng29 分钟前
20822 star 的开源录屏软件弃 Tauri 换 Electron:表层是换壳,里子是 46 个 Rust 模块
github·音视频开发
SendTomo5 小时前
P2P文件传输工具对比:SendTomo vs send.wang
javascript·网络协议·webrtc·html5·p2p
SendTomo6 小时前
WebRTC文件互传工具实测对比
javascript·网络·webrtc·html5·p2p
SendTomo1 天前
跨设备文件互传新方案
javascript·网络·webrtc·html5·p2p
qingmiaozhuan1 天前
电脑录屏软件追剧高光截取:FFmpeg帧裁剪与可视化方案对比
ffmpeg·电脑·音视频开发·录屏技术·电脑录屏
码流怪侠2 天前
StereoCrafter 源码级深度拆解:腾讯如何把 2D 视频“抠“成 3D
github·音视频开发·webvr
红尘散仙3 天前
浏览器不能监听端口、没有文件系统,凭什么能点对点传文件?
rust·webrtc·webassembly
福大大架构师每日一题4 天前
mediamtx v1.19.3正式发布:认证安全、WebRTC跨域、MoQ稳定性、RTSP与HLS全面升级
安全·ffmpeg·webrtc
hellojackjiang20114 天前
移动端实时音视频聊天框架 RainbowAV v2.2.7发布
webrtc·实时音视频·即时通讯
淡淡的香烟4 天前
Android中WebRTC通信使用
android·webrtc