前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >VOIP使用单端口替换动态端口池进行UDP通信

VOIP使用单端口替换动态端口池进行UDP通信

作者头像
呱牛笔记
发布2023-05-02 15:03:05
4620
发布2023-05-02 15:03:05
举报
文章被收录于专栏:呱牛笔记

做过VOIP的同学都知道,基于UDP实现RTP包收发时需要进行SDP协商或者ICE协商,通常服务器都是用一个端口池来和客户端进行RTP包的转发,而当前的网络环境下,开放端口池给运维带来了维护的风险,也给部分代理场景下带来了实现的复杂度,所以如果使用一个端口用来做媒体数据包的转发,那带来了极大的便利;

以WebRTC的服务器Janus为例,主要需要修改libnice返回的端口配置;以RtpProxy的实现为例,修改SIP协商时,始终返回固定端口给对方,注意需要关闭O_NONBLOCK属性:

1、rtpp_create_listener方法中,原来是通过在端口池中随机选择一个可用的端口,现在只需要返回固定端口就可以了:

代码语言:javascript
复制
#ifdef USE_SINGLE_PORT
    return create_twinlistener(5600, &cta);//modify 2020-03-12
#else
    return (CALL_METHOD(rpp, get_port, create_twinlistener,
      &cta));
#endif

2、设置端口复用属性:

代码语言:javascript
复制
  //add for set reuse. 
    int reuse = 1;
    setsockopt(pvt->fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));  
  //add end.

3、收到第一个RTP包时,调用accept方法,在内核中生成对方IP/端口和fd句柄之间的映射关系,注意,调用accept方法后,不能再使用recvfrom 或者 sendto 方法发送数据包,替换为recv/send方法,如使用这两个接口,则目的地址只能为NULL:

代码语言:javascript
复制
struct rtpp_socket_priv {
    struct rtpp_socket pub;
    int fd;
    
   struct sockaddr_storage raddr;
  int raddr_len ; 
};
 
 
调用例子,在收到第一个UDP包的时候,得到对方的ip地址和端口,然后使用connect方法连接到对方
 
    struct rtp_packet *packet;
 
    packet = rtp_packet_alloc();
    if (packet == NULL) {
        return NULL;
    }
 
    pvt = PUB2PVT(self);
 
    packet->rlen = sizeof(packet->raddr);
    packet->size = recvfrom(pvt->fd, packet->data.buf, sizeof(packet->data.buf), 0,
      sstosa(&packet->raddr), &packet->rlen);
  
        if (packet->size > 0 && 0 != check_update_source_addr(pvt, packet)) {
        rtp_packet_free(packet);
        return (NULL);
     } 

// 检查和连接函数
static int check_update_source_addr(struct rtpp_socket_priv *pvt, struct rtp_packet *packet){
  if (pvt == NULL || packet == NULL){
        return -1;
  }
 
  if (pvt->raddr_len == 0){
    //主要逻辑,就是收到第一个UDP包的时候(判断是否有存储对方的地址,没有则是第一次接收到包),得到对方的ip地址和端口,然后使用connect方法连接到对方
    char saddr[MAX_ADDR_STRLEN] = {'\0',};
    sstosa(&packet->raddr)->sa_family = AF_INET;
    addr2char_r(sstosa(&packet->raddr), saddr, sizeof(saddr));
   
    pvt->raddr_len = packet->rlen;
    //memcpy(&pvt->raddr, &packet->raddr, packet->rlen);  
    sstosa(&pvt->raddr)->sa_family = AF_INET;
    satosin(&pvt->raddr)->sin_addr.s_addr = satosin(&packet->raddr)->sin_addr.s_addr;
    satosin(&pvt->raddr)->sin_port = satosin(&packet->raddr)->sin_port;
   
    struct sockaddr_in serv_addr;
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port=   satosin(&packet->raddr)->sin_port;
    serv_addr.sin_addr.s_addr = satosin(&packet->raddr)->sin_addr.s_addr;
    bzero(&(serv_addr.sin_zero),8);
    int ret = connect(pvt->fd, &serv_addr, sizeof(serv_addr));
    if (0 == ret){
      printf ("ret:%d, pvt %p: connect %s, port: %d success.\n", ret, pvt, saddr, ntohs(satosin(&packet->raddr)->sin_port));
    }else{     
      printf ("ret ret:%d, errno:%d, pvt %p: connect %s, port: %d failed.\n", ret, errno , pvt, saddr, ret, ntohs(satosin(&packet->raddr)->sin_port));
      return -1;
    }
}else{ 
    int rval = 0;
    if (packet->rlen == pvt->raddr_len){
        rval = memcmp(&pvt->raddr, &packet->raddr, packet->rlen);
    }else{
        rval = -1;
    }
    if (rval != 0) {
       char saddr[MAX_ADDR_STRLEN] = {'\0',};
       char taddr[MAX_ADDR_STRLEN] = {'\0',};
       sstosa(&packet->raddr)->sa_family = AF_INET;
       sstosa(&pvt->raddr)->sa_family = AF_INET;
       addr2char_r(sstosa(&pvt->raddr), saddr, sizeof(saddr));
       addr2char_r(sstosa(&packet->raddr), taddr, sizeof(taddr));
        printf("error, not the same address, desire ip:%s, port:%d, but receive ip:%s, port:%d.\n",  saddr, ntohs(satosin(&pvt->raddr)->sin_port),  taddr, ntohs(satosin(&packet->raddr)->sin_port));
        return -1;
    }
  }
  return 0;
}

经过验证,在高内核版本上,单端口复用会出现ICE连接失败的现象,只能依靠一个端口监听,然后通过ICE连接标识做多用户分发!

--

补充更新-2021-04-30

这种单端口的实现受限于操作系统内核句柄和客户端的分发实现,可能存在数据混乱的情况,只能作为一个思路而已,更好的单端口实现还是需要在协议报文中识别是不同的用户,然后分发给不同的组或者目标;

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/03/19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档