PJSIP自动注册机制踩坑

pjsip 专栏收录该内容
4 篇文章 0 订阅

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);
	    }
	}

 

至此,正常情况下的心跳注册和异常状况下的重新注册链条可以衔接,测试俩天没有发现中断的问题。

 

  • 2
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值