我正在尝试让PJSIP在我的iOS项目中工作以连接到我的Asterisk服务器。我使用wireshark进行了数据包捕获,由于某种原因,PJSIP没有像应有的那样响应401挑战。
更奇怪的是,这种情况确实很少见。就像正在发生某种竞争状况一样,但这全部存在于PJSIP本身中。因为我只想...
pjsua_acc_add(&accountConfig, PJ_TRUE, &accID);
然后由PJSIP库执行其操作。
这是数据包捕获的一个片段。10.200.0.72是我的Asterisk服务器。 10.200.154.118是我的测试iOS设备。
REGISTER sip:10.200.0.72 SIP/2.0
Via: SIP/2.0/UDP 10.200.154.118:5080;rport;branch=z9hG4bKPjLjNBXVcaqqPqYVtTR2GDEQJhhGYG7Fo3
Max-Forwards: 70
From: <sip:[email protected]>;tag=JgDAcidZf0Ew-Jqgei0Am3Znyl2SEFg.
To: <sip:[email protected]>
Call-ID: hTMu0Vi.Kte6PntnMJdxQmeBWtKdXZd0
CSeq: 59555 REGISTER
Contact: <sip:[email protected]:5080;ob>
Expires: 300
Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS
Content-Length: 0
REGISTER sip:10.200.0.72 SIP/2.0
Via: SIP/2.0/UDP 10.200.154.118:5080;rport;branch=z9hG4bKPjLjNBXVcaqqPqYVtTR2GDEQJhhGYG7Fo3
Max-Forwards: 70
From: <sip:[email protected]>;tag=JgDAcidZf0Ew-Jqgei0Am3Znyl2SEFg.
To: <sip:[email protected]>
Call-ID: hTMu0Vi.Kte6PntnMJdxQmeBWtKdXZd0
CSeq: 59555 REGISTER
Contact: <sip:[email protected]:5080;ob>
Expires: 300
Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS
Content-Length: 0
SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 10.200.154.118:5080;rport=5080;received=10.200.154.118;branch=z9hG4bKPjLjNBXVcaqqPqYVtTR2GDEQJhhGYG7Fo3
Call-ID: hTMu0Vi.Kte6PntnMJdxQmeBWtKdXZd0
From: <sip:[email protected]>;tag=JgDAcidZf0Ew-Jqgei0Am3Znyl2SEFg.
To: <sip:[email protected]>;tag=z9hG4bKPjLjNBXVcaqqPqYVtTR2GDEQJhhGYG7Fo3
CSeq: 59555 REGISTER
WWW-Authenticate: Digest realm="asterisk",nonce="1485330086/50f04167f65383db99f5c0cf17651984",opaque="73b3a6a8599485f8",algorithm=md5,qop="auth"
Server: FPBX-13.0.190.11(13.12.1)
Content-Length: 0
SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 10.200.154.118:5080;rport=5080;received=10.200.154.118;branch=z9hG4bKPjLjNBXVcaqqPqYVtTR2GDEQJhhGYG7Fo3
Call-ID: hTMu0Vi.Kte6PntnMJdxQmeBWtKdXZd0
From: <sip:[email protected]>;tag=JgDAcidZf0Ew-Jqgei0Am3Znyl2SEFg.
To: <sip:[email protected]>;tag=z9hG4bKPjLjNBXVcaqqPqYVtTR2GDEQJhhGYG7Fo3
CSeq: 59555 REGISTER
WWW-Authenticate: Digest realm="asterisk",nonce="1485330086/50f04167f65383db99f5c0cf17651984",opaque="73b3a6a8599485f8",algorithm=md5,qop="auth"
Server: FPBX-13.0.190.11(13.12.1)
Content-Length: 0
这是我的OBJ-C代码,用于实现此目标...
- (void) startAndRegisterOnServer:(NSString *)sipDomain withUserName:(NSString *)sipUser andPassword:(NSString *)password success:(void (^)(void))success failure:(void (^)(int errorCode, NSString *errorMessage))failure
{
pj_status_t status;
status = pjsua_create();
if (status != PJ_SUCCESS)
{
failure (status, @"Error in pjsua_create");
return;
}
// Setup Config and Initialize
pjsua_config config;
pjsua_config_default (&config);
config.cb.on_incoming_call = &on_incoming_call;
config.cb.on_call_media_state = &on_call_media_state;
config.cb.on_call_state = &on_call_state;
config.cb.on_reg_state2 = &on_reg_state2;
pjsua_logging_config logConfig;
pjsua_logging_config_default(&logConfig);
logConfig.console_level = 5;
status = pjsua_init(&config, &logConfig, NULL);
if (status != PJ_SUCCESS)
{
failure (status, @"Error in pjsua_init");
return;
}
// Add UDP transport
pjsua_transport_config udpTransportConfig;
pjsua_transport_config_default(&udpTransportConfig);
udpTransportConfig.port = 5080;
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &udpTransportConfig, NULL);
if (status != PJ_SUCCESS)
{
failure (status, @"Error adding UDP transport");
return;
}
// Add TCP transport.
pjsua_transport_config tcpTransportConfig;
pjsua_transport_config_default(&tcpTransportConfig);
tcpTransportConfig.port = 5080;
status = pjsua_transport_create(PJSIP_TRANSPORT_TCP, &tcpTransportConfig, NULL);
if (status != PJ_SUCCESS)
{
failure (status, @"Error adding TCP transport");
return;
}
// Startup PJSUA
status = pjsua_start();
if (status != PJ_SUCCESS)
{
failure (status, @"Error starting PJSUA");
return;
}
// Register with SIP Server
pjsua_acc_config accountConfig;
pjsua_acc_config_default(&accountConfig);
// Account ID
char sipAccount [MAX_SIP_ACCOUNT_LENGTH];
sprintf (sipAccount, "sip:%s@%s", [sipUser UTF8String], [sipDomain UTF8String]);
accountConfig.id = pj_str(sipAccount);
// Register URI
char regUri[MAX_SIP_REGISTER_URI_LENGTH];
sprintf(regUri, "sip:%s", [sipDomain UTF8String]);
accountConfig.reg_uri = pj_str(regUri);
// Set the credentials in accountConfig
accountConfig.cred_count = 1;
accountConfig.cred_info[0].scheme = pj_str("digest");
accountConfig.cred_info[0].realm = pj_str("asterisk");
accountConfig.cred_info[0].username = pj_str((char *)[sipUser UTF8String]);
accountConfig.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
accountConfig.cred_info[0].data = pj_str((char *)[password UTF8String]);
pjsua_acc_id accID;
status = pjsua_acc_add(&accountConfig, PJ_TRUE, &accID);
[PJSIPInterfaceManager sharedInstance].accountID = accID;
if (status != PJ_SUCCESS)
{
failure (status, @"Error registering account with server");
return;
}
success();
}
有什么想法吗?同样,在极少数情况下,我可以使它工作,但它就像50次中的1次。
**更新**
因此经过大量的故障排除。我相信这可能与NAT有关。我有两台服务器。一个位于我的网络内部,这是此示例基于(10.200.0.72)和一台外部服务器的基础。但是我在两台服务器上都有问题。因此,接下来是我使用外部Asterisk服务器进行的通宵测试。我让我的iOS客户端整夜运行,但无法注册。它设置为默认的5分钟重试。这是因为CSeq没有增加时,它始终一直在重新发送同一数据包。然后在深夜,客户端终于连接了。
在日志中,我注意到了这一点...
04:19:22.706 pjsua_acc.c ...SIP registration failed, status=408 (Request Timeout)
04:24:20.767 pjsua_acc.c ....IP address change detected for account 0 (10.200.154.118:5080 --> [MY EXTERNAL IP]:31943). Updating registration (using method 4)
04:24:20.810 pjsua_acc.c ....sip:100@[EXTERNAL ASTERISK IP]: registration success, status=200 (OK), will re-register in 300 seconds
请注意,我编辑了外部Asterisk服务器的IP和公共IP。
您会在第二行看到PJSIP客户端注意到我的IP已更改。实际上,设备保留了10.200.154.118。但是由于某种原因,它突然开始在数据包的VIA线路中为路由器发送我的公共IP。然后,您可以在下一行中看到它已成功注册。
这是数据包捕获中的VIA行...
从:
Via: SIP/2.0/UDP 10.200.154.118:5080;rport;branch=z9hG4bKPjegwWD-UEDhH3c5cShdumEEbKvfmK45Zl
至:
Via: SIP/2.0/UDP [MY EXTERNAL IP]:31943;rport;branch=z9hG4bKPjxcKgVfSngqxxTy7MnGhZU5Y3RrjpczNr
CSeq编号也最终增加,并且发送了Authorization标头。
然后大约3小时20分钟后,它又回到了失败状态。但是它仍在使用我的外部IP(它没有还原回它的10个地址。)这是在服务器发送大量OPTIONS消息之后。连续20个小时,客户没有回应...
OPTIONS sip:100@[MY EXTERNAL IP]:31943;ob SIP/2.0
Via: SIP/2.0/UDP [EXTERNAL ASTERISK IP]:5060;branch=z9hG4bK6128eaf9;rport
Max-Forwards: 70
From: "Unknown" <sip:Unknown@[EXTERNAL ASTERISK IP]>;tag=as165c224d
To: <sip:100@[MY EXTERNAL IP]:31943;ob>
Contact: <sip:Unknown@[EXTERNAL ASTERISK IP]:5060>
Call-ID: 341a15d536c3a3b45a73567056e34b6b@[EXTERNAL ASTERISK IP]:5060
CSeq: 102 OPTIONS
User-Agent: Mozilla
Date: Fri, 27 Jan 2017 14:45:48 GMT
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE
Supported: replaces, timer
Content-Length: 0
老实说,我不知道此OPTION消息的全部含义。但是在连续执行约20个操作后,随后的REGISTER失败了,并继续这样做。
我也刚注意到一些东西。该端口从客户端上的静态5080侦听端口更改为随机动态端口。您会在两条VIA行中看到这一点。这使我认为,即使我在网络中看到该数据包,也不会到达客户端,因为它没有在该端口上侦听?我应该在客户端上设置任何配置以解决此问题?
有什么见解吗?
最佳答案
前一段时间我在PJSIP上工作。我将启动代码与现有代码进行了比较,这里有些差异。不确定是否会有所帮助。
在顶部,我做一个:
if (pjsua_acc_get_count() > 0)
{
[self unregisterAccount];
}
有点下降:
accountConfig.reg_timeout = 110;
accountConfig.ka_interval = 5;
accountConfig.allow_contact_rewrite = 0;
最后,这是
unregisterAccount
方法:- (void)unregisterAccount
{
@synchronized(self)
{
pj_status_t status;
unsigned count;
[self registerThread];
count = pjsua_acc_get_count();
if (count > 0)
{
pjsua_acc_info* info = calloc(count, sizeof(pjsua_acc_info));
for (int n = 0; n < count; n++)
{
status = pjsua_acc_enum_info(info, &count);
if (status == PJ_SUCCESS)
{
status = pjsua_acc_del(info[n].id);
if (status != PJ_SUCCESS)
{
NSLog(@"Unregister unsuccessful");
}
}
}
free(info);
}
accountId = -1;
}
}
我希望这会有所帮助,但我想不会。并非只有您一个人,PJSIP和SIP通常很难在每种情况下都能正常工作。祝您成功!
如果没有帮助,我想PJSIP的论坛仍在工作吗?
关于ios - PJSIP不响应身份验证到401,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41846403/