WebRTC中的TURN

在使用webrtc的时候,在有些情况下NAT是无法穿越的,那怎么办呢?那么在特定的情景下,这时就需要公网的服务器作为一个中继,对来往的数据进行转发,于是这种转发协议就被定义为TURN。TURN和其他中继协议的不同之处在于,它允许客户端使用同一个中继地址(relay address)与多个不同的peer进行通信。

注:值得一提的是,TURN协议本身是STUN的一个拓展,因此绝大部分TURN报文都是STUN类型的,作为STUN的一个拓展,TURN增加了新的方法(method)和属性(attribute)。

WebRTC TURN原理

turn的新方法

0x003    Allocate
0x004    Refresh
0x006    Send
0x007    Data
0x008    CreatePermission
0x009    ChannelBind

新turn属性

0x000c   CHANNEL-NUMBER
0x000D  LIFETIME
0x0010  Reserved      (was BANDWIDTH)
0x0012  XOR-PEER-ADDRESS
0x0013  DATA
0x0016 XOR-RELAYED-ADDRESS
0x0018  EVEN-PORT
0x0019  REQUESTED-TRANSPORT
0x001A  DON’T-FRAGMENT
0x0021  Reserved      (was TIMER-VAL)
0x0022  RESERVATION-TOKEN

注:上面属性中的部分属性长度不是4字节的倍数,采用STUN的规则,使用1~3个padding字节来补齐。

#define ALGIN_4BYTES(v) (((v)+3) / 4 * 4)

turn交互流程

图片

1 Allocations

    户端发送分配请求(Allocate request)到服务器,然后服务器 返回分配成功响应,并包含了分配的地址。

图片

2 Permissions

      在发送数据之前首先要鉴权,检查是否有发送的权限,这个完成之后它就来发数据。

图片

3 发送机制

发送机制有两种方法。1.Send和Data 2.Channel这两种方法都是通过stun协议发送信息。

  户端发送一个Send Indication到服务端,其中包含:

  • XOR-PEER-ADDRESS属性,指定对等端的(服务器反射)地址.
  • DATA属性,包含要传给对等端的信息.
  • 图片

对方收到data数据:

图片
图片

注:发送数据前要封装成stun协议,并且附带XOR-PEER-ADDRESS属性,指定对等端的(服务器反射)地址。收到数据不是裸数据,也是从stun里面提取data属性里面的数据(0x0013  DATA)。

信道机制(Channels)

ChannelData message不使用STUN头部,而使用一个4字节的头部,包含了 一个称之为信道号的值(channel number).每一个使用中的信道号都与一个特定的peer绑定,即作为对等端地址的一个记号。

图片

上图中0x4001为信道号,即ChannelData message的头部中头2字节,值得一提的是信道号的选取有如下要求:

number = ((uint16_t)data[0] << 8) | (uint16_t)data[1];
length = ((uint16_t)data[2] << 8) | (uint16_t)data[3];

注:

  • 0x0000-0x3FFF : 这一段的值不能用来作为信道号
  • 0x4000-0x7FFF : 这一段是可以作为信道号的值,一共有16383种不同值在目前来看是足够用的
  • 0x8000-0xFFFF : 这一段是保留值,留给以后使用
图片
图片

4 dtls加密

webrtc是要加密才可以正常交互的。

web请求端会通过Send Indication或者Channel发送数据进行dtls协议交互。

图片
图片
  const uint8_t* data1 = data;
  uint16_t number;
  uint16_t length;
  number = ((uint16_t)data1[0] << 8) | (uint16_t)data1[1];
  length = ((uint16_t)data1[2] << 8) | (uint16_t)data1[3];
  if ( length + 4 == bytes)
  {
    const void* data2 = (const uint8_t*)data + 4;
    if (avt->webrtc && janus_is_dtls((const uint8_t*)data2, length))
    {  
        if (!avt->dtls)
        {
          struct dtls_handler_t handler;
          memset(&handler, 0, sizeof(struct dtls_handler_t));
          handler.send = ice_transport_dtls_onsend_plush;
          handler.onconnected = ice_transport_dtls_onconnected;
          avt->dtls = janus_dtls_srtp_create((const void*)local, locallen, (const void*)peer, peerlen, JANUS_DTLS_ROLE_SERVER, STUN_PROTOCOL_UDP, &handler, avt);
          janus_super_sctp(avt->dtls, avt->super_sctp);
          janus_dtls_srtp_handshake(avt->dtls);
        }
        avt->srtp_valid = janus_dtls_srtp_incoming_msg(avt->dtls, (const uint8_t*)data + 4, length);

    }
    
  }

static int turn_client_send_channel_data(struct stun_agent_t* turn, const struct turn_allocation_t* allocate, const struct turn_channel_t* channel, const void* data, int bytes)
{
    uint8_t ptr[1600];
    if (bytes + 4 > sizeof(ptr))
        return -1; // MTU too long
    ptr[0] = (uint8_t)(channel->channel >> 8);
    ptr[1] = (uint8_t)(channel->channel);
    ptr[2] = (uint8_t)(bytes >> 8);
    ptr[3] = (uint8_t)(bytes);
    memcpy(ptr + 4, data, bytes);
    
    send(turn->param, allocate->addr.protocol, (const struct sockaddr*)&allocate->addr.host, (const struct sockaddr*)&allocate->addr.peer, ptr, bytes+4);
}

交互流程

  • web端请求:
图片

服务端应答:

https://datatracker.ietf.org/doc/html/rfc5766

注:上面显示DTLSv1.2都是我提取dtls数据转发给服务器显示用的,其实交互流程是不存在的。

图片

参考

  • [1] github
  • [2] 陈老大、juans
  • [2] C/C++面向WebAssembly编程

作者:aliveyun

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(1)

相关推荐

发表回复

登录后才能评论