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

相关推荐
赖small强2 小时前
【ZeroRange WebRTC】RTP/RTCP/RTSP协议深度分析
webrtc·rtp·rtsp·rtcp
赖small强2 小时前
【ZeroRange WebRTC】视频文件RTP打包与发送技术深度分析
webrtc·nal单元分割·rtp负载封装·分片策略
赖small强2 小时前
【ZeroRange WebRTC】KVS WebRTC 示例中的 HTTP 通信安全说明
https·webrtc·tls·aws sigv4·信道安全·时间与重放控制
chen_song_2 小时前
低时延迟流媒体之WebRTC协议
webrtc·rtc·流媒体
恪愚3 小时前
webRTC:流程和socket搭建信令服务器
运维·服务器·webrtc
赖small强17 小时前
【ZeroRange WebRTC】Amazon Kinesis Video Streams WebRTC SDK 音视频传输技术分析
音视频·webrtc·nack·pli·twcc·带宽自适应
赖small强19 小时前
【ZeroRange WebRTC】Amazon Kinesis Video Streams WebRTC Data Plane REST API 深度解析
https·webrtc·data plane rest·sigv4 签名
赖small强1 天前
【ZeroRange WebRTC】Kinesis Video Streams WebRTC 三大平面职责与协同关系总结
websocket·webrtc·control plane·data plane
赖small强1 天前
【ZeroRange WebRTC】Amazon Kinesis Video Streams WebRTC Control Plane API 深度解析
https·webrtc·control plane
赖small强1 天前
【ZeroRange WebRTC】Kinesis Video Streams WebRTC Data Plane WebSocket API 深度解析
websocket·webrtc·sdp·offer/answer·master/viewer