BCM VOIP 注册流程分析

注册发起

//初始化注册
callRegSetup( ®Id, &pu[i] );
	//分配一个注册控制块
	regIdx = CreateRegIdx();
	
	//将用户参数保存到注册控制块中
	//callgcb.reg[i]->suser.username
	// callgcb.reg[i]->suser. display
	// callgcb.reg[i]->suser. authname
	// callgcb.reg[i]->suser. password
	// callgcb.reg[i]->suser. realmlist. realm[k].realm
	// callgcb.reg[i]->suser. realmlist. realm[k]. username
	// callgcb.reg[i]->suser. realmlist. realm[k]. password
	// callgcb.reg[i]->suser. registrar.addr
	// callgcb.reg[i]->suser. aorHostport.addr
	// callgcb.reg[i]->suser. mohserver.addr
	// callgcb.reg[i]->suser. mohserver.display
	// callgcb.reg[i]->suser. routeSet.route[k].addr.addr
	// callgcb.reg[i]->suser. mohserver.scheme = CCURITYPE_SIP;
	// callgcb.reg[i]->suser. mohserver.params = NULL;
	// callgcb.reg[i]->aorValid
	// callgcb.reg[i]->registrarEnabled
	// callgcb.reg[i]->routeSetEnabled
	SetSuserParm(regIdx, suser);
	
	//初始状态为CCSTS_UNREGISTERED
	reg->regState = CCSTS_UNREGISTERED;
	
	//无源码,不太清楚
	reg->pUAAuthentication->SetManager(&sipcb);
	reg->pUAAuthentication->ReleaseIfRef();
	
	//如果当前用户配置了realm参数,则调用协议栈接口添加鉴权凭证
	for (UINT32 i = 0; i < realmlist->num; i++)
		realm = realmlist->realm[i].realm;
		user = realmlist->realm[i].username;
		password = realmlist->realm[i].password;
		reg->pUAAuthentication->AddCredentialsA(realm, user, password);
	
	if (reg->registrarEnabled)
		//如果用户配置了路由集,则转化为MT5内部格式
		if (reg->routeSetEnabled)
			SetRoute(regIdx, reg->suser.registrar.addr, reg->suser.registrar.port, GO 
			preloadRoute);
		
		//格式化From头域地址
		FormAddrOfRecord(regIdx, addressOfRecord);
		
		//格式化Request-URI
		
		//格式化Contact头域
		
		//调用协议栈句柄,初始化注册管理控制块
		reg->pUARegistration = BRCM_NEW(MX_NS CUARegistration);
		reg->pUARegistration->Initialize(&sipcb, &sipcb, &addressOfRecord, &contactHdrs,
		&localContactHdrs, &sipUri, &addressOfRecord, &serverLocatorListModifierCb, 
		(mxt_opaque)serverLocOpaque, serverLocatorPrevListModifierCb,
serverLocPrevOpaque, preloadRoute );

//调用协议栈句柄,设置一些通用头域
SetGenHeaders(*rid, NULL);
		
		//将鉴权模块附加到注册控制块中
		reg->pUARegistration->SetAuthenticationS(reg->pUAAuthentication, callgcb.mutex);
		
		//没有源码,不太清楚
		reg->pUARegistration->SetOpaqueS((mxt_opaque)(*rid), callgcb.mutex);
		reg->pUAAuthentication->SetOpaqueS((mxt_opaque)(*rid), callgcb.mutex);

//将注册控制块索引关联到cmEndpt中
cmEndpt[i].regId = regId;

if( cmEndpt[i].registrarEnabled )
	//向注册对象中添加Authorization和Supported头
sprintf( hdrValue, "Digest username=\"%s\", realm=\"%s\", nonce=\"\", uri=\"sip:%s\", response=\"\", algorithm=MD5",pu[i].authname, pu[i].registrar.addr, pu[i].registrar.addr );
headers.list[headers.num].type = CCHDR_GENERIC;
headers.list[headers.num].hdr.generic.name = CHDR_AUTHORIZATION_NAME;
headers.list[headers.num].hdr.generic.value = hdrValue;
headers.map[headers.num] = CCHDRMAP_REQ_ONLY;
headers.num++;

headers.list[headers.num].type = CCHDR_GENERIC;
headers.list[headers.num].hdr.generic.name = CHDR_SUPPORTED_NAME;
headers.list[headers.num].hdr.generic.value = CHDR_SUPPORTED_VALUE;
headers.map[headers.num] = CCHDRMAP_REQ_ONLY;
headers.num++;
callSetParm( regId, CCPARM_GENHDRS, (void *)&headers );

//重置注册超时定时器
cmEndpt[i].regRetryTimerMs = 0;
cmEndpt[i].regActTimerMs = EPT_REGACT_TIMER;

//发起注册
callRegister( regId, NULL );
	//仅在未注册或者已经注册成功的最终状态下,才处理注册请求
	if ((reg->regState == CCSTS_UNREGISTERED)|| (reg->regState == CCSTS_REGISTERED))
		//注册控制块状态迁为CCSTS_REGINPROGRESS
		reg->regState = CCSTS_REGINPROGRESS;
		
		//构建Contact头域
		FormRegistrationContacts(regIdx, localContactHdrs);
		
		//当前content为空,不处理
		FormatContent(content, msgBody);
		
		//调用协议栈句柄,发起注册请求
		reg->pUARegistration->RegisterA(&localContactHdrs, TO msgBody);
 

注册超时

//当前没有板子调试,协议栈没有源码,猜测当前流程从此入口进入。

1、callctrl事件回调
SIPCB::EvFailedA
	RegistrationState(pRegistration, eState, pPacket);
		switch (FSM_(eState, callgcb.reg[uRegIdx]->regState))
		//正在注册过程中,收到eREGISTRATION_REQUEST_TIMEOUT事件
		case FSM(eREGISTRATION_REQUEST_TIMEOUT, CCSTS_REGINPROGRESS):
			//当前注册控制块状态迁为CCSTS_UNREGISTERED
			callgcb.reg[uRegIdx]->regState = CCSTS_UNREGISTERED;
				//给callmgr发送CCEVT_STATUS/ CCRSN_REG_TIMEOUT事件
				GCBEVTSTATUS(rid, CCRSN_REG_TIMEOUT, pp);

2、callmgr处理从callctrl发来的CCEVT_STATUS/ CCRSN_REG_TIMEOUT事件
cmProcessCallEvent
	switch (event)
	case CCEVT_STATUS:
		switch( reason )
		case CCRSN_REG_TIMEOUT:
			//获取cmEndpt控制块
			endpt = cmFindEndptFromRegId( cid );
			
			//取消注册超时探测定时器
			cmEndpt[endpt].regActTimerMs = 0;
		
		//映射为CMEVT_STATUS事件
		cnxevent = cmMapById( cmCallEvtMap, reason);
	
	if ((cnxevent == CMEVT_STATUS) && (endpt != UNKNOWN))
		switch (reason)
		case CCRSN_REG_TIMEOUT:
			//如果已处于在线状态
			if( cmEndpt[endpt].inservice == TRUE )
				//复位在线状态
				cmEndpt[endpt].inservice = FALSE;
				
				//更新是否在线状态显示
				cmDisplay( endpt, UNKNOWN, CMLCD_SERVER);
					switch ( msgtype)
					case CMLCD_SERVER:
						cmEndptNotify( endpt, UNKNOWN, CMEVT_INSERVICE, 
						cmEndpt[endpt].inservice);
							cmEndptStateEngine( endpt, cid, event, data );
								case FSM( CMEVT_INSERVICE, CMST_ONHOOK):
									//这里主要调用了cmPublishEvent,当然该函数的回调
									//中没有对CMEVT_INSERVICE事件的处理。
				
				//更新cmEndpt关联兄弟控制块的inservice
				cmUpdateRegSib( endpt );
				
				//检查SIP报文是否含有Retry-Afterr头域,如果有则将当前线路的重新注
				//册时间更新为该值。当前超时流程不会有报文存在,所以不执行此流程。
				if( headers &&  headers->num )
					for( i = 0; i < (int)headers->num; i++ )
						if( headers->list[i].type == CCHDR_RETRY_AFTER )
							cmEndpt[endpt].regRetryTimerMs =  
							headers->list[i].hdr.retry.deltasec * 1000;
				
				//设置重新注册时间为用户配置的值
				if( !cmEndpt[endpt].regRetryTimerMs )
					cmEndpt[endpt].regRetryTimerMs = 
					cmProvisionGet(PROVIS_SIP_REGRETRY_TIMER, endpt)) * 1000
	
	//当前没有对CCEVT_STATUS/CCRSN_REG_TIMEOUT事件的处理
	cmProtProcessCallEvent( endpt, cnxevent, reason, packet );
 

注册成功

1、callctrl事件回调
SIPCB::EvRegisteredA
	//从sip报文中获取Allow-Events头域字段,并存储到注册控制块中reg->allowevts
	GetAllowEvents(reg->allowevts, (MX_NS CSipPacket*)pPacket);
	
	RegistrationState(pRegistration, IUARegistrationMgr::eREGISTERED, pPacket);
		//提取应答码,并将SIP包转化成callctrl格式
		if (pPacket && pPacket->IsResponse())
			uStatus = pPacketCopy->GetStatusLine()->GetCode();
			strPhrase = pPacketCopy->GetStatusLine()->GetPhrase();
			
			GetPacket(*pPacketCopy, outPacket);
			pp = (outPacket.content != NULL || outPacket.hdrs != NULL) ? &outPacket : NULL;
		
		switch (FSM_(eState, callgcb.reg[uRegIdx]->regState))
		//在正在注册状态下收到注册成功
		case FSM(eREGISTERED, CCSTS_REGINPROGRESS):
			//注册控制块状态迁为CCSTS_REGISTERED
			callgcb.reg[uRegIdx]->regState = CCSTS_REGISTERED;
			
			//给callmgr发送CCEVT_STATUS/ CCRSN_REG_OK事件
			GCBEVTSTATUS(rid, CCRSN_REG_OK, pp);

2、callmgr处理从callctrl发来的CCEVT_STATUS/CCRSN_REG_OK事件
cmProcessCallEvent
	switch (event)
	case CCEVT_STATUS:
		switch( reason )
		case CCRSN_REG_OK:
			//获取cmEndpt控制块
			endpt = cmFindEndptFromRegId( cid );
			
			//取消注册超时探测定时器
			cmEndpt[endpt].regActTimerMs = 0;
		
		//映射为CMEVT_STATUS事件
		cnxevent = cmMapById( cmCallEvtMap, reason);
	
	if ((cnxevent == CMEVT_STATUS) && (endpt != UNKNOWN))
		switch (reason)
		case CCRSN_REG_OK:
			if( cmEndpt[endpt].inservice == FALSE )
				//在cmEndpt控制块中记载当前已经在线 
				cmEndpt[endpt].inservice = TRUE;
				
				//该函数最终触发cmPublishEvent回调,但该回调中没有对应事件处理
				cmDisplay( endpt, UNKNOWN, CMLCD_SERVER);
				
				//将关联的兄弟cmEndpt也标记为在线
				cmUpdateRegSib( endpt );
	
	cmProtProcessCallEvent( endpt, cnxevent, reason, packet );
		if (cnxevent == CMEVT_STATUS  && (endpt != UNKNOWN))
			if (reason == CCRSN_REG_OK )
				//如果注册应答报文中含有Service-Route头域,则记录到cmEndpt控制块
				if( headers && headers->num )
					for( i = 0 ; i < headers->num ; i++ )
						if( headers->list[i].type == CCHDR_SERVICE_ROUTE )
							strcpy( cmEndpt[endpt].srAddr,
headers->list[i].hdr.route.service.addr );

cmEndpt[endpt].srPort =
headers->list[i].hdr.route.service.port;
				
				//发送MWI消息订阅
				cmMwiSubscribe(endpt);
					//用户开启了订阅服务
					if ( cmEndpt[endpt].explicitMwi.enabled )
						//构造to字段
						cmSetCalledParty( endpt, &calledparty, to );
						
						//分配呼叫控制块,并初始化用户配置参数
						callSetup( &cid, cmEndpt[endpt].regId, CCCIDTYPE_MWI, 
						&calledparty);
						
						//将消处订阅类型的呼叫控制块索引关联到cmEndpt中
						cmEndpt[endpt].explicitMwi.mwiCid = cid;
						
						//设置PREFERRED_ID头域
						callSetParm( cid, CCPARM_PREFERRED_ID, (void *)&prefId );
						
						//如果本地配有outband服务器,或者注册应答中含有
//Service-Route头域,则当前注册包加入Route头域
						cmSetServiceRoute( cid, endpt );
						
						//调用协议栈句柄发起Subscribe请求
						callSubscribe( cmEndpt[endpt].explicitMwi.mwiCid, 
						SUBSCRIBTION_TIMEOUT_SECS, NULL, NULL );
						
						//cmCnx控制块关联cmEndpt控制块
						cmCnx[cid].endpt = endpt;
						
						//MWI状态机变迁为CMMWI_SUBSCRIBING
						cmEndpt[endpt].explicitMwi.mwiState = CMMWI_SUBSCRIBING;
				
				//发送注册订阅
				cmRegEvtPkgStateEngine( endpt, UNKNOWN, cnxevent, CCRSN_REG_OK );
					ep = &cmEndpt[endpt];
					regevt = &cmRegEvtPkg.regEvtProfBlk[endpt];
					
					switch( FSM( event, state ))
					case FSM( CMEVT_STATUS, CMSUBSTATE_IDLE ):
						switch( reason )
						case CCRSN_REG_OK:
							//发起订阅
							cmRegEvtPkgSubscribe( UNKNOWN, regevt );
								//创建一个呼叫控制块,并初始化并初始化用户配置
//参数
								callSetup( &cid, cmEndpt[endpt].regId, 
CCCIDTYPE_EVENT, &to )

//注册订阅控制块关联呼叫控制块,并初始化状态
cmCnx[cid].endpt = pRegEvt->endpt;
cmCnx[cid].state = CMST_IDLE;
pRegEvt->reqId.cid = cid;
pRegEvt->reqId.state = CMSUBSTATE_SUBSCRIBING;

//设置PREFERRED_ID头域
callSetParm( cid, CCPARM_PREFERRED_ID, (void *)&to );

//设置Accept: application/reginfo+xml头域
sprintf( display, "Accept" );
sprintf( eventValue, "%s/%s",
CMREGEVT_ACCEPT_HEADER_TYPE_APP,
CMREGEVT_ACCEPT_HEADER_SUBTYPE_REGINFO );

headers.list[headers.num].type = CCHDR_GENERIC;
headers.list[headers.num].hdr.generic.name = display;
headers.list[headers.num].hdr.generic.value = 
eventValue;
headers.map[headers.num] = CCHDRMAP_ALWAYS;
headers.num++;
callSetParm( cid, CCPARM_GENHDRS, (void *)&headers );

//如果本地配有outband服务器,或者注册应答中含有
//Service-Route头域,则当前注册包加入Route头域
cmSetServiceRoute( cid, endpt );

//调用协议栈句柄发起Subscribe请求
callSubscribe( cid, SUBSCRIBTION_TIMEOUT_SECS,
eventValue, NULL )

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值