美文网首页音视频开发笔记
DTLS 握手为什么常失败

DTLS 握手为什么常失败

作者: 老瓦在霸都 | 来源:发表于2023-07-07 10:34 被阅读0次

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

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

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

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

    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 的期望的消息, 它就会重传.

    Client                                   Server
    ------                                   ------
    ClientHello           ------>
    
                            X<-- HelloVerifyRequest
                                             (lost)
    
    [Timer Expires]
    
    ClientHello           ------>
    (retransmit)
    

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

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

    相关文章

      网友评论

        本文标题:DTLS 握手为什么常失败

        本文链接:https://www.haomeiwen.com/subject/vhnrudtx.html