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 调试时是否输出调试信息 输入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.AddDestination(addr); 通过跟踪AddDestination()函数的实现,发现在class RTPIPv4Destination的构造函数中是这样构造一个发送目的地址的: RTPIPv4Destination(uint32_t ip,uint16_t rtpportbase) 为了实现:可以自定义目的IP地址和目的rtp port和rtcp port。为了实现这么目标,自己动手改造下面几个函数:构造函数RTPIPv4Destination() 、RTPSession::AddDestination(),思路是在目的地址设置相关函数中增加一个rtcp ip 和port参数。 RTPIPv4Destination(uint32_t ip,uint16_t rtpportbase,uint32_t rtcpip,uint16_t rtcpport) int RTPSession::AddDestination(const RTPAddress &addr,const RTPIPv4Address &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方式。至于原因还没弄清楚。可以发邮件给作者问一下。 |