PJSIP自动注册机制踩坑
作者:互联网
PJSIP自动注册机制踩坑
严格意义上说并不一定算是PJSIP的坑,只是理解不一样而已,事情是这样,使用中发现有些时候PJ注册失败后会停止继续注册,导致app如果没有人工干预会一直离线状态,这个在生产环境是比较伤的,开始研究一下它的注册机理。
PJSIP的自动注册分两条线:线路一是注册成功之后的定时注册(也称心跳),线路二是注册失败之后的定时重新注册,这个两个时间和频次以及执行的模块是不一样的,并且同个账户只会在其中一条线上。
设置的代码如下(C++ API,C的类似)
acc_cfg.regConfig.timeoutSec = regTimeout; //自动注册时间间隔,默认300秒,即每5分钟注册一次
acc_cfg.regConfig.firstRetryIntervalSec = 0; //注册失败后,首次重新注册时间,默认0,立即发起重新注册
acc_cfg.regConfig.retryIntervalSec = 10; //注册失败后,重新发起注册的时间间隔,默认300秒
acc_cfg.regConfig.randomRetryIntervalSec = 1; //重新发起注册的时间随机范围(为了避免多账户情况下同时发起注册),正负1,实际时间是9~11秒,默认10秒
acc_cfg.regConfig.delayBeforeRefreshSec = 5; //注册超时前(timeoutSec)多长时间刷新客户端注册状态,默认5秒
//假设timeoutSec=30,那么第25秒会刷新注册状态,第30秒发起注册,随后客户端收到注册结果回调。
acc_cfg.regConfig.unregWaitMsec = 2; //销毁协议栈前等待注销的最大时间,默认4秒
1、线路一,注册成功后的心跳
执行模块是sip_reg.c,首次账户首次注册成功后,由消息驱动并开启新的注册Timer,注册一直成功则会循环往复,注册失败后跳转到线路二,调用流程大概是这样:
收到消息 --> regc_tsx_callback --> if(state_code/100==2) --> schedule_registration -->开启新的timer,delay比如30秒,回调是regc_refresh_timer_cb
而regc_refresh_timer_cb主要做三个事情:1、创建注册报文pjsip_regc_register;2、发送消息pjsip_regc_send;3、如果消息发送失败,则调用上层回调regc_cb;
这里最关键的是pjsip_reg_send过程,设置了regc_tsx_callback回调,由此产生正常情况下的注册心跳循环。
而出问题的点是第三步,如果发送失败则返回code=400的消息回调给上层,代码:
static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
struct pj_timer_entry *entry)
{
pjsip_regc *regc = (pjsip_regc*) entry->user_data;
pjsip_tx_data *tdata;
pj_status_t status;
PJ_UNUSED_ARG(timer_heap);
PJ_LOG(3, (THIS_FILE, "regc_refresh_timer_cb send regist msg...."));
/* Temporarily increase busy flag to prevent regc from being deleted
* in pjsip_regc_send() or in the callback
*/
pj_atomic_inc(regc->busy_ctr);
entry->id = 0;
status = pjsip_regc_register(regc, 1, &tdata);
if (status == PJ_SUCCESS) {
status = pjsip_regc_send(regc, tdata);
}
if (status != PJ_SUCCESS && regc->cb) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL,
PJ_FALSE);
}
/* Delete the record if user destroy regc during the callback. */
if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
pjsip_regc_destroy(regc);
}
}
上层(pjsua_acc.c)的处理函数reg_cb函数,判定一系列的错误之后开始重新发起注册的线路二,代码:
/* Check if we need to auto retry registration. Basically, registration
* failure codes triggering auto-retry are those of temporal failures
* considered to be recoverable in relatively short term.
*/
if (acc->cfg.reg_retry_interval &&
(param->code == PJSIP_SC_REQUEST_TIMEOUT ||
param->code == PJSIP_SC_INTERNAL_SERVER_ERROR ||
param->code == PJSIP_SC_BAD_GATEWAY ||
param->code == PJSIP_SC_SERVICE_UNAVAILABLE ||
param->code == PJSIP_SC_SERVER_TIMEOUT ||
param->code == PJSIP_SC_TEMPORARILY_UNAVAILABLE ||
PJSIP_IS_STATUS_IN_CLASS(param->code, 600))) /* Global failure */
{
schedule_reregistration(acc);
}
/* Call the registration status callback */
if (pjsua_var.ua_cfg.cb.on_reg_state) {
(*pjsua_var.ua_cfg.cb.on_reg_state)(acc->index);
}
if (pjsua_var.ua_cfg.cb.on_reg_state2) {
pjsua_reg_info reg_info;
pjsip_regc_info rinfo;
pjsip_regc_get_info(param->regc, &rinfo);
reg_info.cbparam = param;
reg_info.regc = param->regc;
reg_info.renew = !param->is_unreg;
(*pjsua_var.ua_cfg.cb.on_reg_state2)(acc->index, ®_info);
}
但是这里唯独没有400的进入条件,造成重新发起注册的链条中断,添加之。
2、线路二,注册失败后的重新注册
执行模块是pjsua_acc.c,注册失败后,由消息驱动并开启新的自动重新注册Timer,直到注册成功跳转到线路一,调用流程:
regc_tsx_callback发生错误 --> regc_cb --> schedule_reregistration --> 开启新的重新注册Timer,delay时间比如10秒(首次时间根据设置),回调是auto_rereg_timer_cb。
在这个回调中做了两个事情:1、发送注册报文;2、如果发送失败则立即调用schedule_reregistration进入下一次重新注册周期;
在发送注册报文的方法pjsua_acc_set_registration中,逻辑和上面的一致,将会设置regc_tsx_callback回调,如此往复。
另外在regc_tsx_callback的回调中,两个地方调用了pjsip_regc_send,但是再往上回调时采用的code并不是固定的400,有可能导致漏失一些错误码,导致重新注册的逻辑链条中断掉,这里我采用的方式是直接返回400错误码。
status = pjsip_regc_register(regc, regc->auto_reg, &tdata);
if (status == PJ_SUCCESS) {
status = pjsip_regc_send(regc, tdata);
}
if (status != PJ_SUCCESS) {
/* Only call callback if application is still interested
* in it.
*/
if (!regc->_delete_flag) {
/* Should be safe to release the lock temporarily.
* We do this to avoid deadlock.
*/
pj_lock_release(regc->lock);
call_callback(regc, status, 400,//edit by tsx->status_code,
&rdata->msg_info.msg->line.status.reason,
rdata, -1, 0, NULL, PJ_FALSE);
pj_lock_acquire(regc->lock);
}
}
至此,正常情况下的心跳注册和异常状况下的重新注册链条可以衔接,测试俩天没有发现中断的问题。
---------------------
作者:飞奔的图腾
来源:CSDN
原文:https://blog.csdn.net/liang12360640/article/details/88721496
版权声明:本文为博主原创文章,转载请附上博文链接!
标签:status,reg,cb,pjsip,自动,注册,PJSIP,regc 来源: https://blog.csdn.net/ddddffffggggg/article/details/94600847