Darwin Streaming Server 支持UDP打洞

2024-04-29 02:08

本文主要是介绍Darwin Streaming Server 支持UDP打洞,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

RTSP客户端点播Darwin 视频时,SDP协商后的客户端端口可能是在NAT后面,所以需要Darwin支持NAT打洞的功能,从Darwin的源码看,官方的源码是不支持这个能力的。


通过抓取VLC客户端的包发现,VLC在播放RTSP流时,两次SETUP(音频流和视频分别协商端口)之后,会发送4个UDP打洞的包,但Darwin没有接收这些包,并且根据这些包来源的端口修改远端RTP和RTCP的端口。


文章Darwin Streaming Server 支持UDP穿透中给出了修改方法,尝试之后,发现有两个问题:

1、两次SETUP协商后,Darwin给返回的服务器的RTP和RTCP端口两次都一样

2、客户端发送的打洞的RTP和RTCP打洞的包,并没有接收完全。


针对问题1的修改就是将两次SETUP协商后,Darwin返回的端口不同并且唯一

问题2的修改方法是,在接收到SETUP协商后,开启一个线程接收发送到服务器的RTP和RTCP端口的包,并根据接收到的包的源端口更新远端的RTP和RTCP端口,即使没有收到打洞的包,不做任何处理,还是使用之前协商的端口往外发包。


第一个问题是将RTPStream::Setup方法中的:

fSockets = QTSServerInterface::GetServer()->GetSocketPool()->GetUDPSocketPair(sourceAddr, 0, fRemoteAddr, 
                                                                                        fRemoteRTCPPort);

修改为:

fSockets = QTSServerInterface::GetServer()->GetSocketPool()->CreateUDPSocketPair(sourceAddr, 0);


并将UDPSocketPool::CreateUDPSocketPair方法中两个变量的初值修改为如下:


    UInt16 curPort = kLowestUDPPort + usedNum++;
    UInt16 stopPort = kHighestUDPPort -1; // prevent roll over when iterating over port nums
    UInt16 socketBPort = curPort + 1;



第二个问题修改,头文件增加下面的方法和变量:

        void start_thread_for_nat();void setRemoteRTPPort(int value){fRemoteRTPPort = value; }void setRemoteRTCPPort(int value){fRemoteRTCPPort = value;}UInt16    getRemoteRTPPort(){return  fRemoteRTPPort;}UInt32    getRemoteRTPAddr(){return  fRemoteAddr;}Bool16    getQuitValue(){return  bQuit;}Bool16    getRunningValue(){return  bRunning;}void    setRunningValue(Bool16 value){ bRunning = value;}UInt16    getRemoteRTCPPort(){return  fRemoteRTCPPort;}UDPSocketPair*  getUDPSocketPair(){ return fSockets;}Bool16      bQuit;Bool16      bRunning;


在setup方法最后启动一个监听线程: 

    this->start_thread_for_nat();//errors should only be returned if there is a routing problem, there should be noneAssert(err == QTSS_NoErr);return QTSS_NoErr;
}

实现:

#include <pthread.h>
void* thread_for_nat(void *parms){Bool16      fUpdateRtpPort = false;Bool16      fUpdateRtcpPort = false;SInt64                  currentTime = OS::Milliseconds();RTPStream *pRTPStream = (RTPStream *)parms;if (pRTPStream == NULL){return NULL;}if (pRTPStream->getUDPSocketPair() == NULL){return NULL;}if (pRTPStream->getUDPSocketPair()->GetSocketA() == NULL ||pRTPStream->getUDPSocketPair()->GetSocketB() == NULL){return NULL;}qtss_printf("thread_for_nat enter, pRTPStream:%08x, rtp_port:%d\n", pRTPStream, pRTPStream->getRemoteRTPPort());pRTPStream->setRunningValue(true);while(1){UInt32 iRemoteAddr = 0;UInt16 iRemotePort = 0;char szBuff[64];UInt32 iBufLen = sizeof(szBuff);UInt32 iRecvLen = 0;if (pRTPStream->getQuitValue()){break;}if (!fUpdateRtpPort){OS_Error iRet = pRTPStream->getUDPSocketPair()->GetSocketA()->RecvFrom(&iRemoteAddr, &iRemotePort, szBuff, iBufLen, &iRecvLen);if (OS_NoErr == iRet){if (iRemoteAddr == pRTPStream->getRemoteRTPAddr()&& iRemotePort != pRTPStream->getRemoteRTPPort()&& iRecvLen > 0){qtss_printf("thread_for_nat update GetSocketA iRet:%d, fRemoteRTPPort:%d, fRemoteRTCPPort:%d, iRemotePort:%d\n", iRet, pRTPStream->getRemoteRTPPort(), pRTPStream->getRemoteRTCPPort(), iRemotePort);pRTPStream->setRemoteRTPPort(iRemotePort);fUpdateRtpPort = true;}else{qtss_printf("thread_for_nat update GetSocketA received the same port value, fRemoteRTPPort:%d, fRemoteRTCPPort:%d\n", pRTPStream->getRemoteRTPPort(), pRTPStream->getRemoteRTCPPort());fUpdateRtpPort = true;}}else{//qtss_printf("Setup update GetSocketA iRet:%d, fRemoteRTPPort:%d, fRemoteRTCPPort:%d\n", iRet, fRemoteRTPPort, fRemoteRTCPPort);}}if (!fUpdateRtcpPort){OS_Error iRet = pRTPStream->getUDPSocketPair()->GetSocketB()->RecvFrom(&iRemoteAddr, &iRemotePort, szBuff, iBufLen, &iRecvLen);if (OS_NoErr == iRet){if (iRemoteAddr == pRTPStream->getRemoteRTPAddr()&& iRemotePort != pRTPStream->getRemoteRTCPPort()&& iRecvLen > 0){qtss_printf("thread_for_nat update GetSocketB iRet:%d, fRemoteRTPPort:%d, fRemoteRTCPPort:%d, iRemotePort:%d\n", iRet, pRTPStream->getRemoteRTPPort(), pRTPStream->getRemoteRTCPPort(), iRemotePort);pRTPStream->setRemoteRTCPPort(iRemotePort);fUpdateRtcpPort = true;} else{qtss_printf("thread_for_nat update GetSocketB received the same port value, fRemoteRTPPort:%d, fRemoteRTCPPort:%d\n", pRTPStream->getRemoteRTPPort(), pRTPStream->getRemoteRTCPPort());fUpdateRtcpPort = true;}}else{//wait.//qtss_printf("Setup update GetSocketB iRet:%d, fRemoteRTPPort:%d, fRemoteRTCPPort:%d\n", iRet, fRemoteRTPPort, fRemoteRTCPPort);}}if (fUpdateRtcpPort && fUpdateRtpPort){qtss_printf("thread_for_nat exit for update end, break, pRTPStream:%08x, rtp_port:%d\n", pRTPStream, pRTPStream->getRemoteRTPPort());break;}if ( (OS::Milliseconds() - currentTime ) > 2000){qtss_printf("thread_for_nat exaust 2000 ms, break, pRTPStream:%08x, rtp_port:%d\n", pRTPStream, pRTPStream->getRemoteRTPPort());break;}}pRTPStream->setRunningValue(false);gid_thread = -1;return NULL;
}void RTPStream::start_thread_for_nat(){int ret=pthread_create(&gid_thread, NULL, thread_for_nat, (void*)this);if (ret != 0){qtss_printf("err:%d\n", ret);}else{qtss_printf("start_thread_for_nat create OK\n");}
}


这篇关于Darwin Streaming Server 支持UDP打洞的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/944850

相关文章

基于C++的UDP网络通信系统设计与实现详解

《基于C++的UDP网络通信系统设计与实现详解》在网络编程领域,UDP作为一种无连接的传输层协议,以其高效、低延迟的特性在实时性要求高的应用场景中占据重要地位,下面我们就来看看如何从零开始构建一个完整... 目录前言一、UDP服务器UdpServer.hpp1.1 基本框架设计1.2 初始化函数Init详解

SQL Server 中的表进行行转列场景示例

《SQLServer中的表进行行转列场景示例》本文详细介绍了SQLServer行转列(Pivot)的三种常用写法,包括固定列名、条件聚合和动态列名,文章还提供了实际示例、动态列数处理、性能优化建议... 目录一、常见场景示例二、写法 1:PIVOT(固定列名)三、写法 2:条件聚合(CASE WHEN)四、

Mybatis对MySQL if 函数的不支持问题解读

《Mybatis对MySQLif函数的不支持问题解读》接手项目后,为了实现多租户功能,引入了Mybatis-plus,发现之前运行正常的SQL语句报错,原因是Mybatis不支持MySQL的if函... 目录MyBATis对mysql if 函数的不支持问题描述经过查询网上搜索资料找到原因解决方案总结Myb

mysql_mcp_server部署及应用实践案例

《mysql_mcp_server部署及应用实践案例》文章介绍了在CentOS7.5环境下部署MySQL_mcp_server的步骤,包括服务安装、配置和启动,还提供了一个基于Dify工作流的应用案例... 目录mysql_mcp_server部署及应用案例1. 服务安装1.1. 下载源码1.2. 创建独立

SQL Server中行转列方法详细讲解

《SQLServer中行转列方法详细讲解》SQL行转列、列转行可以帮助我们更方便地处理数据,生成需要的报表和结果集,:本文主要介绍SQLServer中行转列方法的相关资料,需要的朋友可以参考下... 目录前言一、为什么需要行转列二、行转列的基本概念三、使用PIVOT运算符进行行转列1.创建示例数据表并插入数

golang实现nacos获取配置和服务注册-支持集群详解

《golang实现nacos获取配置和服务注册-支持集群详解》文章介绍了如何在Go语言中使用Nacos获取配置和服务注册,支持集群初始化,客户端结构体中的IpAddresses可以配置多个地址,新客户... 目录golang nacos获取配置和服务注册-支持集群初始化客户端可选参数配置new一个客户端 支

Python 基于http.server模块实现简单http服务的代码举例

《Python基于http.server模块实现简单http服务的代码举例》Pythonhttp.server模块通过继承BaseHTTPRequestHandler处理HTTP请求,使用Threa... 目录测试环境代码实现相关介绍模块简介类及相关函数简介参考链接测试环境win11专业版python

SQL Server 查询数据库及数据文件大小的方法

《SQLServer查询数据库及数据文件大小的方法》文章介绍了查询数据库大小的SQL方法及存储过程实现,涵盖当前数据库、所有数据库的总大小及文件明细,本文结合实例代码给大家介绍的非常详细,感兴趣的... 目录1. 直接使用SQL1.1 查询当前数据库大小1.2 查询所有数据库的大小1.3 查询每个数据库的详

Spring Boot 整合 SSE(Server-Sent Events)实战案例(全网最全)

《SpringBoot整合SSE(Server-SentEvents)实战案例(全网最全)》本文通过实战案例讲解SpringBoot整合SSE技术,涵盖实现原理、代码配置、异常处理及前端交互,... 目录Spring Boot 整合 SSE(Server-Sent Events)1、简述SSE与其他技术的对

Linux之UDP和TCP报头管理方式

《Linux之UDP和TCP报头管理方式》文章系统讲解了传输层协议UDP与TCP的核心区别:UDP无连接、不可靠,适合实时传输(如视频),通过端口号标识应用;TCP有连接、可靠,通过确认应答、序号、窗... 目录一、关于端口号1.1 端口号的理解1.2 端口号范围的划分1.3 认识知名端口号1.4 一个进程