假设A的RTP使用端口1000,则起对应的RTCP端口必定为1001,在初始化RTP信令后,JRTPLIB会随机使用另外一个端口3333来发送RTP数据,1000端口是用来接收RTP数据的,1001接收RTCP包。
假设B的RTP使用端口2000,则起对应的RTCP端口必定为2001,在初始化RTP信令后,JRTPLIB会随机使用另外一个端口4444来发送RTP数据,2000端口是用来接收RTP数据的,2001接收RTCP包。
又假设A,B均在NAT后,经NAT转换后,利用STUN协议,A开始向B发包,B也向A发包,由于NAT的存在,B发给A的包并不是A发出去的目的地址端口发过来的,目的端口只受数据不发数据(A将数据包发给B的RTP接收端口后,NAT A就只接收B的这个RTP端口发过来的数据,对于B用来发送数据的端口而言,是不请自到的),因此NAT A会丢弃B发过来的包,同理NAT B也会丢弃A发过来的包。
这样一来,为了能UDP PUNCH HOLE,A,B就必须再往对方用来发送数据的端口打一个洞,以便欺骗NAT,但UDP是不可靠的,而NAT影射也有时间限制,要保证NAT的影射关系不变就的定时打洞给对方,个人感觉这种方法不是很好。
经过查看JRTPLIB源码,它的Create()函数里是这么写的
addr.sin_family = AF_INET;
addr.sin_port = htons(0); //就是这里使用随机端口
addr.sin_addr.s_addr = htonl(0);
if (bind(sendsock,(struct sockaddr *)&addr,sizeof(struct sockaddr)) != 0)
{
return ERR_RTP_CANTBINDSOCKET;
}
socklen = sizeof(struct sockaddr_in);
if (getsockname(sendsock,(struct sockaddr *)&addr,&socklen) != 0)
{
return ERR_RTP_CANTGETSOCKETPORT;
}
sendport = ntohs(addr.sin_port);//这里是发送数据的端口
很明显,它用的是随机端口,BIND成功后再查询这个端口的,在RTPSession里用于获取发送端口的函数GetSendPort()直接返回了这个sendport,如下:
int GetSendPort()
{
return sendport;
}
为此,我只好修改JRTPLIB V2.9的源码了,在rtpconnection.cpp里,将发送数据的sendto语句里的socket都换成rtpsocket,让他使用bind在rtp端口的socket即可,同时删除sendsocket,并将GetSendPort()直接返回portbase。经过修改,发送/接收数据时就只有RTP和RTCP端口了,而且收发数据都通过RTP绑定的那个端口进行。