其他分享
首页 > 其他分享> > PJSIP自动注册机制踩坑

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, &reg_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