跨网络边界通过做端口反向代理调通WebRTC音视频通话功能案例

2023-05-02 15:40:42 浏览数 (2)

"构成我们学习最大障碍的是已知的东西,不是未知的东西" ------现代医学奠基人贝尔纳

WebRTC 交互的流程大致如下:

图片来源网络:https://www.jianshu.com/p/a7e7cb4d6d64

1、进入房间;

2、获取媒体,交换SDP;

3、通过turnserver获取本机候选地址,交换candidate;

4、ICE进行候选地址进行连接,连通了,则可以进行音视频通话;

这次项目实施的环境是一个网络等级相对要求高的网络,客户要求Android手机在安全网络外头,PC客户端运行在安全网络里头,大概的拓扑结构图如下:

                        代理前置机         代理后置机      WebRTC服务器                     TURN服务器 

客户端(192段)   192.168.1.40     20.80.4.131       20.80.4.133 (8099)                20.80.4.133 (3478)

52652                 3478                 52652                                                                 3478

                           30008                                        30008   

Android手机通过VPN接入到代理前置机网络,访问安全网络20.80.4.*的服务器,需要通过代理前置开反向代理才能进入,并且代理设备之间还有隔离交换设备;

首先在代理前置机上开了WebRTC服务器8099和Turnserver 3478 的代理端口

20.80.4.133/8099 -   192.168.1.40/8099  tcp

20.80.4.133/3478 -   192.168.1.40/3478  udp

20.80.4.133/30008-30208 -   192.168.1.40/30008-30208   udp  端口池

Android客户端配置的服务器地址信息统一调整为代理的ip和端口,信令很顺利的就调通了,媒体预知肯定是不通,需要修改几个地方:

1、Android端candidate收集的本机地址中,在开启stun配置后,能获取到20.80.4.133的ip和端口,并将candidate发送给服务器;

 "candidate": {

      "candidate": "0:0:candidate:4020260996 1 udp 2122260223 10.172.25.131 52652 typ host generation 0 ufrag 7CB0 network-id 3 network-cost 50: ",

      "sdpMid": "0",

      "sdpMLineIndex": 0

   }

2、Android端收到服务器的candidate地址信息,需要将20.80.4.133地址修改为代理前置机的地址,这是因为所有20.80.4.133其实已经被代理前置机代理了,但服务器是没法感知;

经过上面的修改,以为ICE就能通了,其实还是不通,通过抓包分析,原来问题出在STUN打的洞上,客户端可以通过代理机的端口发送数据包到服务器,但服务器通过客户端的candidate地址和端口发送ice请求包,却出现icmp不可达的错误!原来,这个环境的代理机制是我们极少碰到的对称型NAT!我们说对于对称型NAT,是无法通过预先打洞的端口进行数据互通的。这种网络设备,对每个外部主机或端口的会话都会映射为不同的端口(洞)。只有来自相同的内部地址(IP:PORT)并且发送到相同外部地址(X:x)的请求,在NAT上才映射为相同的外网端口,即相同的映射。

打洞机制失效,怎么破?

修改思路:

1、所有数据包都经过TURN服务器转发?这个思路可行,但在这种网络条件下,如何实施貌似有些问题不明白,比方turnserver开的转发端口就需要对外做代理,服务器可能也需要开启turnserver,暂时放弃这个思路;

2、这种网络环境下,去掉STUN服务器,不需要stun做地址探测了,应用对网络环境是清晰的,并且需要去掉ICE的候选地址配对的相关流程,主要是ICE地址配置过程中也是STUN协议交互的过程:

客户端将本机的candidate发送给服务器时,服务器的地址配置信息为:

[20.80.4.133]:30008 --> [20.80.4.131]:52652

但经过了客户端和服务器的stun试探性连接后,服务器发现,客户端给服务器返回的公网地址是:192.168.1.40:30008,

出现了新的配对:

 [192.168.1.40]:30008 --> [20.80.4.131]:52652 

并且服务器给出了配对失败的错误,其实互联的Socket都是正常的话,ice的交互显得有些多此一举了,所以果断修改libnice的ice交互流程,去掉了ICE的地址配对完成后服务器的地址匹配判断逻辑,libnice的ice服务器这么修改:忽略客户端返回的stun response 中的服务器的公网地址; 

代码语言:javascript复制
static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent *agent, NiceStream *stream, NiceComponent *component, CandidateCheckPair *p, NiceSocket *sockptr, struct sockaddr *mapped_sockaddr, NiceCandidate *local_candidate, NiceCandidate *remote_candidate)
{
  CandidateCheckPair *new_pair = NULL;
  NiceAddress mapped;
  GSList *i, *j;
  NiceCandidate *local_cand = NULL;
 
  nice_address_set_from_sockaddr (&mapped, mapped_sockaddr);
  for (j = component->local_candidates; j; j = j->next) {
    NiceCandidate *cand = j->data;
    //忽略客户端返回的服务器地址,直接返回成功
    //if (nice_address_equal (&mapped, &cand->addr)) {
      local_cand = cand;
 
      /* We always need to select the peer-reflexive Candidate Pair in the case
       * of a TCP-ACTIVE local candidate, so we find it even if an incoming
       * check matched an existing pair because it could be the original
       * ACTIVE-PASSIVE candidate pair which was retriggered */
      for (i = stream->conncheck_list; i; i = i->next) {
        CandidateCheckPair *pair = i->data;
        if (pair->local == cand && remote_candidate == pair->remote) {
          new_pair = pair;
          break;
        }
      }
      break;
    //}
  }
 
}

经过上面的修改,webrtc的音视频通话功能正常!

0 人点赞