PJSIP2.6支持IPv6笔记
��前段时间做了一个Voip的应用,在上架时�因为IPv6的原因连续被拒三次,��网上试了很多���方法,都没能成功,当时真是心力交瘁。最终在google的帮助下顺利解决了IPv6问题并成功上架。希望这篇文章能帮助后来人能少走弯路。
编译�支持IPv6的�静态库
�下载pjsip源码,在pjsip目录pjlib/include/pj/config_site.h
中添加: #define PJ_HAS_IPV6 1
,然后重新编译生成支持��IPv6的静态库
�具体编译��步骤可参考:
Getting Started: Building for Apple iPhone, iPad and iPod Touch
�在PJSUA中添加IPv6支持
�选择TCP,UDP时,首先得�确认SIP�服务器支持UDP协议还是TCP协议。��然后�服务器地址�不能用IPv4地址,需要改为域名。
pjsua_transport_config tp_cfg;
pjsip_transport_type_e tp_type;
pjsua_transport_id tp_id = -1;
pjsua_transport_config_default(&tp_cfg);
tp_cfg.port = 5060;
/* TCP */
tp_type = PJSIP_TRANSPORT_TCP6;
status = pjsua_transport_create(tp_type, &tp_cfg, &tp_id);
if (status != PJ_SUCCESS)
...
/* UDP */
tp_type = PJSIP_TRANSPORT_UDP6;
status = pjsua_transport_create(tp_type, &tp_cfg, &tp_id);
if (status != PJ_SUCCESS)
acc_cfg.transport_id = udp6_tp_id; // udp6_tp_id is an UDP IPv6 transport ID, e.g: outputed by
// pjsua_transport_create(PJSIP_TRANSPORT_UDP6, ..., &udp6_tp_id)
/* Enable IPv6 in media transport */
acc_cfg.ipv6_media_use = PJSUA_IPV6_ENABLED;
/* Finally */
status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL);
if (status != PJ_SUCCESS)
这是从pjsip官方文档复制过来的代码片段,��详细的��代码请查阅pjsip IPv6 支持。
�走到这一步,没问题的话,�在IPv6环境下�可以进行SIP���注册,但是不能打电话和接电话。
����用PJ-nat64�临时解决IPv6�问题
��pjsip�开启��IPv6支持不能打电话和接电话。最后无发现有pj-nat64
�开源代码,尝试后在IPv6环境能成功解决打电话和接电话功能,并不需要服务器做其它的处理。地址:pj-nat64。
�pj-nat64�集成��步骤
把pj-nat64.c
和pj-nat64.h
放进项目中,然后�在合适的地方启用。在启动pjsip的地方,加上这段代码。可以�通过判断在IPv6��环境下再启用pj-nat64。判断IPv6的代码,��见文章未。
pj_nat64_enable_rewrite_module();
在on_reg_state
回调中,打开PJSUA�_IPV6_ENABLED
属性,并激活PJ_NAT64重写。
if (isIPv6) {
yourAccountConfig.ipv6_media_use = PJSUA_IPV6_ENABLED;
pj_nat64_set_options(NAT64_REWRITE_ROUTE_AND_CONTACT);
}
代码需要按照自己的情况进行适配修改。
�问题�排�错
�Sip在IPv6上的消息体
���贴上正确的Sip IPv6消息体,以检查�SIP�消息是否正确,以排错,�报文数据摘自IE�TF - Sip Test Messages on IPv6。
注册
// Message Details: reg-good
REGISTER sip:[2001:db8::10] SIP/2.0
To: sip:user@example.com
From: sip:user@example.com;tag=81x2
Via: SIP/2.0/UDP [2001:db8::9:1];branch=z9hG4bKas3-111
Call-ID: SSG9559905523997077@hlau_4100
Contact: "Caller" <sip:caller@[2001:db8::1]>
CSeq: 98176 REGISTER
Content-Length: 0
加上端口
// Message Details: reg-good-port
REGISTER sip:[2001:db8::10]:5070 SIP/2.0
To: sip:user@example.com
From: sip:user@example.com;tag=81x2
Via: SIP/2.0/UDP [2001:db8::9:1];branch=z9hG4bKas3-111
Call-ID: SSG9559905523997077@hlau_4100
Contact: "Caller" <sip:caller@[2001:db8::1]>
CSeq: 98176 REGISTER
Content-Length: 0
IPv6的SIP请求放在SDP消息体中
// Message Details: inv-good
INVITE sip:user@[2001:db8::10] SIP/2.0
To: sip:user@[2001:db8::10]
From: sip:user@example.com;tag=81x2
Via: SIP/2.0/UDP [2001:db8::9:1];branch=z9hG4bKas3-111
Call-ID: SSG9559905523997077@hlau_4100
Contact: "Caller" <sip:caller@[2001:db8::1]>
CSeq: 8612 INVITE
Content-Type: application/sdp
Content-Length: 268
v=0
o=assistant 971731711378798081 0 IN IP6 2001:db8::20
s=Live video feed for today's meeting
c=IN IP6 2001:db8::1
t=3338481189 3370017201
m=audio 6000 RTP/AVP 2
a=rtpmap:2 G726-32/8000
m=video 6024 RTP/AVP 107
a=rtpmap:107 H263-1998/90000
IPv6的bye请求
// Message Details: bye-good
BYE sip:user@host.example.com SIP/2.0
Via: SIP/2.0/UDP [2001:db8::9:1]:6050;branch=z9hG4bKas3-111
Via: SIP/2.0/UDP 192.0.2.1;branch=z9hG4bKjhja8781hjuaij65144
Via: SIP/2.0/TCP [2001:db8::9:255];branch=z9hG4bK451jj;
received=192.0.2.200
Call-ID: 997077@lau_4100
CSeq: 89187 BYE
To: sip:user@example.net;tag=9817--94
From: sip:user@example.com;tag=81x2
�判断IPv6��环境代码
当时项目时间急,就在网上Copy了这段代码,现在已无法知道原出处,对�原作者表示抱歉。
#define IOS_CELLULAR @"pdp_ip0"
#define IOS_WIFI @"en0"
#define IOS_VPN @"utun0"
#define IP_ADDR_IPv4 @"ipv4"
#define IP_ADDR_IPv6 @"ipv6"
#import <ifaddrs.h>
#import <arpa/inet.h>
#import <net/if.h>
+ (BOOL)isIpv6{
NSArray *searchArray =
@[ IOS_VPN @"/" IP_ADDR_IPv6,
IOS_VPN @"/" IP_ADDR_IPv4,
IOS_WIFI @"/" IP_ADDR_IPv6,
IOS_WIFI @"/" IP_ADDR_IPv4,
IOS_CELLULAR @"/" IP_ADDR_IPv6,
IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ;
NSDictionary *addresses = [self getIPAddresses];
NSLog(@"addresses: %@", addresses);
__block BOOL isIpv6 = NO;
[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
{
NSLog(@"---%@---%@---",key, addresses[key] );
if ([key rangeOfString:@"ipv6"].length > 0 && ![[NSString stringWithFormat:@"%@",addresses[key]] hasPrefix:@"(null)"] ) {
if ( ![addresses[key] hasPrefix:@"fe80"]) {
isIpv6 = YES;
}
}
} ];
return isIpv6;
}
+ (NSDictionary *)getIPAddresses
{
NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
// retrieve the current interfaces - returns 0 on success
struct ifaddrs *interfaces;
if(!getifaddrs(&interfaces)) {
// Loop through linked list of interfaces
struct ifaddrs *interface;
for(interface=interfaces; interface; interface=interface->ifa_next) {
if(!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) {
continue; // deeply nested code harder to read
}
const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
NSString *type;
if(addr->sin_family == AF_INET) {
if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
type = IP_ADDR_IPv4;
NSLog(@"ipv4 %@",name);
}
} else {
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
type = IP_ADDR_IPv6;
NSLog(@"ipv6 %@",name);
}
}
if(type) {
NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
addresses[key] = [NSString stringWithUTF8String:addrBuf];
}
}
}
// Free memory
freeifaddrs(interfaces);
}
return [addresses count] ? addresses : nil;
}
———————————————————————————————
自己维护的几个专题,不定期更新高质量文章,欢迎关注!!
http://www.jianshu.com/c/563ba4d42d38[iOS - 大杂烩]
http://www.jianshu.com/c/1fee819318a7[iOS - 内存/数据/存储]
http://www.jianshu.com/c/6a28d1d173b5[iOS - 线程/底层]
http://www.jianshu.com/c/4dd5a67bbe1d[iOS - 音视频/图文/动画]
http://www.jianshu.com/c/2de7446d0de9[iOS - 网络处理]
http://www.jianshu.com/c/5458bca566a9[iOS - 安全/逆向]
http://www.jianshu.com/c/ca237eb95714[iOS - 优化/质量]
网友评论
pjsua_transport_config tp_cfg;
pjsip_transport_type_e tp_type;
pjsua_transport_id tp_id = -1;
pjsua_transport_config_default(&tp_cfg);
tp_cfg.port = 5060;
/* TCP */
tp_type = PJSIP_TRANSPORT_TCP6;
status = pjsua_transport_create(tp_type, &tp_cfg, &tp_id);
if (status != PJ_SUCCESS)
...
/* UDP */
tp_type = PJSIP_TRANSPORT_UDP6;
status = pjsua_transport_create(tp_type, &tp_cfg, &tp_id);
if (status != PJ_SUCCESS)
acc_cfg.transport_id = udp6_tp_id; // udp6_tp_id is an UDP IPv6 transport ID, e.g: outputed by
// pjsua_transport_create(PJSIP_TRANSPORT_UDP6, ..., &udp6_tp_id)
/* Enable IPv6 in media transport */
acc_cfg.ipv6_media_use = PJSUA_IPV6_ENABLED;
/* Finally */
status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL);
if (status != PJ_SUCCESS)
https://trac.pjsip.org/repos/wiki/IPv6 官网这句话完美解释了pjsip在nat64环境下是不能正常工作的,具体表现是:1.能登录上。2. ipv4端呼叫ipv6端,可以接收到呼叫,但是接听失败。3.ipv6呼叫ipv4,呼不通。