织梦CMS - 轻松建站从此开始!

罗索

Linux下几种RTP协议实现的比较和JRTPLIB编程讲解(2)

jackyhwei 发布于 2011-04-12 09:45 点击:次 
4 基于jrtplib的NAT穿透 4.1 NAT穿透的基础只是 有关于NAT穿透的基础知识 4.2 rtp/rtcp传输涉及到的NAT穿透 rtp/rtcp传输数据的时候,需要两个端口支持。即rtp端口
TAG:

4 基于jrtplib的NAT穿透

4.1 NAT穿透的基础只是有关于NAT穿透的基础知识

4.2 rtp/rtcp传输涉及到的NAT穿透

    rtp/rtcp传输数据的时候,需要两个端口支持。即rtp端口用于传输rtp数据,即传输的多媒体数据;rtcp端口用于传输rtcp控制协议信息。 rtp/rtcp协议默认的端口是rtcp port = rtp port + 1 。详细的说,比如A终端和B终端之间通过rtp/rtcp进行通信,

如上图,

                                                          本地IP:PORT                                                        NAT映射后IP:PORT

UACA RTP的发送和接收IP:PORT : 192.168.1.100:8000                                             61.144.174.230:1597

UACA RTCP的发送和接收IP:PORT:192.168.1.100:8001                                             61.144.174.230:1602

UACB RTP的发送和接收IP:PORT : 192.168.1.10:8000                                                61.144.174.12:8357

UACB RTCP的发送和接收IP:PORT:192.168.1.10:8001                                                61.144.174.12:8420

上图可以得到一下一些信息:

 (一) 本地端口 RTCP PORT = RTP PORT + 1;但是经过NAT映射之后也有可能满足这个等式,但是并不一定有这个关系。

(二)在NAT设备后面的终端的本地IP:PORT并不被NAT外的设置可知,也就无法通过终端的本地IP:PORT与之通信。而必须通过NAT映射之后的公网IP:PORT作为目的地址进行通信。

如上图的终端A如果要发送RTP数据到终端B,UACA发送的目的地址只能是:61.144.174.12:8357。同理,UACB发送RTP数据给UACA,目的地址只能是: 61.144.174.230:1597 。

(三)也许看到这里,如何得到自己的外网IP:PORT呢?如何得到对方的外网IP:PORT呢?这就是NAT IP:PORT转换和穿孔(puncture),下回分解。

4.3 NAT 地址转换

如上所述,终端需要知道自己的外网IP:port,可以通过STUN、STUNT、TURN、Full Proxy等方式。这里介绍通过STUN方式实现NAT穿透。

STUN: Simple Traversal of UDP Through NAT。即通过UDP对NAT进行穿透。

STUNT:Simple Traversal of UDP Through NATs and TCP too.可以通过TCP对NAT进行穿透。

STUN是一个标准协议,具体的协议内容网络上很多。在此不累述了。

为 了通过STUN实现NAT穿透,得到自己的公网IP:PORT,必须有一个公网STUN服务器,以及我们的客户端必须支持STUN Client功能。STUN Client 通过UDP发送一个request到STUN服务器,该请求通过NAT设备的时候会把数据报头中的本地IP:PORT换成该本地IP:PORT对应的公网 IP:PORT,STUN服务器接收到该数据包后就可以把该公网IP:PORT 发送给STUN Client。这样我们就得到了自己的公网IP:PORT。这样别的终端就可以把该公网IP:PORT最为发送UDP数据的目的地址发送UDP数据。

推荐一款STUN client/server 程序代码,http://sourceforge.net/projects/stun/files/ 

这是一款开源软件。在客户端中的主要函数是下面这个:

NatType

stunNatType( StunAddress4& dest,       //in 公网STUN服务器地址,如stun.xten.net

             bool verbose,                              //in 调试时是否输出调试信息
             bool* preservePort=0,                //out  if set, is return for if NAT preservers ports or not
             bool* hairpin=0 ,                        //out  if set, is the return for if NAT will hairpin packetsNAT设备是否支持回环
             int port=0,                               // in 本地测试端口port to use for the test, 0 to choose random port
             StunAddress4* sAddr=0        // out NIC to use ,返回STUN返回的本地地址的公网IP:PORT
   );

             输入StunAddress和测试端口port,得到本地IP:PORT对应的公网IP:PORT.

4.4 对jrtplib  的改造

jrtplib中对rtp rtcp端口的处理关系是:rtcp port = rtp port + 1 。这就有问题,本地端口可以按照这个等式来设置端口,但是经过NAT映射之后的公网端口是随机的,有可能并不满足上述等式。

    int portbase = 6000;                        //设置本地rtp端口为6000

    transparams.SetPortbase(portbase);//默认的本地rtcp端口为6001.因为这里是本地设置,所一这样设置OK
    status = sess.Create(sessparams,&transparams);   
    checkerror(status);
  
    RTPIPv4Address addr(destip,destport); //设置目的地的rtp接收IP:PORT,公网传输的话就要设置为对方的rtp公网IP:PORT
    // AddDestination()的内部处理是把addr.ip和addr.port+1赋给rtcp。这样如果对方在公网上,就有问题了。因为对方的rtcp port 可能不等于rtp port +1;这就导致对方收不到rtcp数据包。

    status = sess.AddDestination(addr); 

    通过跟踪AddDestination()函数的实现,发现在class RTPIPv4Destination的构造函数中是这样构造一个发送目的地址的:

RTPIPv4Destination(uint32_t ip,uint16_t rtpportbase)                   
    {
        memset(&rtpaddr,0,sizeof(struct sockaddr_in));
        memset(&rtcpaddr,0,sizeof(struct sockaddr_in));
       
        rtpaddr.sin_family = AF_INET;
        rtpaddr.sin_port = htons(rtpportbase);
        rtpaddr.sin_addr.s_addr = htonl(ip);
       

            rtcpaddr.sin_family = AF_INET;
            rtcpaddr.sin_port = htons(rtpportbase+1);//默认把rtp的端口+1赋给目的rtcp端口。
            rtcpaddr.sin_addr.s_addr = htonl(ip);

        RTPIPv4Destination::ip = ip;
    }

        为了实现:可以自定义目的IP地址和目的rtp port和rtcp port。为了实现这么目标,自己动手改造下面几个函数:构造函数RTPIPv4Destination() 、RTPSession::AddDestination(),思路是在目的地址设置相关函数中增加一个rtcp ip 和port参数。

RTPIPv4Destination(uint32_t ip,uint16_t rtpportbase,uint32_t rtcpip,uint16_t rtcpport)                   
    {
        memset(&rtpaddr,0,sizeof(struct sockaddr_in));
        memset(&rtcpaddr,0,sizeof(struct sockaddr_in));
       
        rtpaddr.sin_family = AF_INET;
        rtpaddr.sin_port = htons(rtpportbase);
        rtpaddr.sin_addr.s_addr = htonl(ip);
       
        /**If rtcport has not been set separately, use the default rtcpport*/
        if ( 0 == rtcpport )
        {
            rtcpaddr.sin_family = AF_INET;
            rtcpaddr.sin_port = htons(rtpportbase+1);
            rtcpaddr.sin_addr.s_addr = htonl(ip);
        }else
        {
            rtcpaddr.sin_family = AF_INET;
            rtcpaddr.sin_port = htons(rtcpport);
            rtcpaddr.sin_addr.s_addr = htonl(ip);
        }
       
        RTPIPv4Destination::ip = ip;
    }

int RTPSession::AddDestination(const RTPAddress &addr,const RTPIPv4Address &rtcpaddr)
{
    if (!created)
        return ERR_RTP_SESSION_NOTCREATED;
    return rtptrans->AddDestination(addr,rtcpaddr);
}

       在调用RTPSession::AddDestination、定义RTPIPv4Destination的时候实参也相应增加目的rtcp参数。

       这样改造之后就可以自定义独立的设置目的地址rtp ,rtcp端口了。

5 jrtplib 移植中遇到的问题及解决方法

把jrtplib移植到arm11平台,遇到一些问题,如下。

5.1 字节序的问题

jrtplib中的报头的字节序问题,网上可以搜到一些,但都是只言片语,没有详细的把解决方案说出来。ARM采用的是Big-Endian, 而X86采用的是Little-Endian。目前我所采用的解决方法是让两台互相通信的主机所使用的jrtplib的Endian格式一致,都为 Big-Endian或都为Little-Endian,这只需要修改jrtplib-version/src/rtpconfig_unix.h 文件,默认是采用的Little-Endian方式,里面的注释部分有说若加

#define RTP_BIG_ENDIAN

表示采用Big-Endian的字节方式,否则默认为Little-Endian方式。至于原因还没弄清楚。可以发邮件给作者问一下。

5.2 Can't retrieve login name的错误
上述都没有问题了,又遇到另外的问题,在N800的客户端建立RTPSession的过程中,报了Can't retrieve login name的错误,在网上搜索后,找到一篇博客讲到嵌入式系统由于某些原因系统可能没有login name, 而在RTPSession的Create->InternalCreate->CreateCNAME方法,其中的getlogin_r, getlogin和getenv操作会因为logname为空而返回ERR_RTP_SESSION_CANTGETLOGINNAME的错误。我在 N800的机器上做了个实验,使用getlogin和getenv("LOGNAME")确实都不能得到登录名。要解决上述问题,可以对jrtplib的 源代码进行修改, 即修改RTPSession的CreateCNAME,即使getlogin_r, getlogin和getenv三个函数调用都不能得到登录名,也要设置一个登录名。

(poseidonqiu)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201104/11189.html]
本文出处:blog.chinaunix.net 作者:poseidonqiu
顶一下
(2)
100%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容