科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网网络频道在Vovida的基础上实现自己的SIP协议栈②

在Vovida的基础上实现自己的SIP协议栈②

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

处理本地的SIP的事件,包括对状态机的设置和命令/状态队列返回的操作在下面将//对它做详细的介绍 前面我们已经作了简单的介绍,这个函数的主要目的是在处理Rgister,Notify,和Subscribe等几个状态,并且分别调用他们的处理机;

作者:中国IT实验室 2007年9月6日

关键字: TCP/IP TCP/IP协议 TCP/IP设置 网络协议 IPv6 IPSEC IP网络

  • 评论
  • 分享微博
  • 分享邮件

HeartLessProxy::run()

myWorkerThread->run();

mySipThread->run();

通过上面可以看到有两个Run方法的调用,第一个是WorkThread的Run方法,它的主要作用是处理UaBuilder的Process方法,主要用来处理Sptr > > myFifo中的各种事件,前面已经详细的介绍了SipProxyEvent类的作用,这个类已经在前面介绍了,其实简单的说,它就是一个本地的各种事件的集合。

现在我们来看一下两个Run方法的实现:

2.5.1 WorkerThread的Run方法:

UaBuilder::process( const Sptr nextEvent )

{

//处理以下的四种事件

/// SipEvent

Sptr sipEvent;

sipEvent.dynamicCast( nextEvent );

if ( sipEvent != 0 )

{

//处理本地的SIP的事件,包括对状态机的设置和命令/状态队列返回的操作在下面将//对它做详细的介绍

if( processSipEvent( sipEvent ) )

{

return;

}

//向消息队列myCallContainer中插入相应的事件信息。

sendEvent( nextEvent );

return;

}

/// UaDeviceEvent

Sptr uaDeviceEvent;

uaDeviceEvent.dynamicCast( nextEvent );

if ( uaDeviceEvent != 0 )

{

处理本地的设备事件,最主要的就是处理摘机信号;

if( processUaDeviceEvent( uaDeviceEvent ) )

{

return;

}

sendEvent( nextEvent );

return;

}

/// UaDigitEvent

Sptr uaDigitEvent;

uaDigitEvent.dynamicCast( nextEvent );

if ( uaDigitEvent != 0 )

{

//处理在规定的时间间隔(Kickstart)主动呼叫事件的触发。

if( processUaDigitEvent( uaDigitEvent ) )

{

return;

}

sendEvent( nextEvent );

return;

}

/// UaTimerEvent

Sptr uaTimerEvent;

uaTimerEvent.dynamicCast( nextEvent );

if ( uaTimerEvent != 0 )

{

//在各种SIP命令的回应产生了超时事件后,系统的事件触发。例如:

//在StateTrying()中addEntryOperator( new OpStartTimer )在myEntryOperators队列中加入

//该Operator(指一个操作,例如呼叫或者是进入等待),这里我们这个Operator在时间到达

//以后户会被OpTimeout::process的方法检测到(isTimeout(event)进行检测,对StateTrying

//整个状态进行检测,也就是Trying事件),最后如果UaTimerEvent事件被触发,那么,//就会调用:stateMachine->findState( "StateError" )这个状态,进入错误状态,实施错误的

//处理机制,同时向myEntryOperators队列中加入一个新的Operator--OpStartErrorTone,

//从而被processUaTimerEvent过程扑捉到,最后通过SendEvent发送到执行队列里去。

if( processUaTimerEvent( uaTimerEvent ) )

{

return;

}

sendEvent( nextEvent );

return;

}

assert( 0 );

}

2.5.1.1 processSipEvent

顾名思义,processSipEvent方法是对队列中的SIP消息进行处理,我们来看下面的程序:

bool UaBuilder::processSipEvent( const Sptr sipEvent )

{

Sptr statusMsg;

statusMsg.dynamicCast( sipEvent->getSipMsg() );

// 检验是否为返回的状态码(主要是对Notify,Subscribe,Register三种状态进行单独处理)

//下面做详细介绍

if ( statusMsg != 0 )

{

if( handleStatusMsg( sipEvent ) )

{

return true;

}

}

//在这里表示接收到一个SIP的消息,

//检验是否为一个SIP的消息而不是一个状态(例如是否为Invite命令)

/// Let's check the call info, now

callId = sipEvent->getSipCallLeg()->getCallId();

callInfo = calls->findCall( callId );

if ( callInfo == 0 )

{

//下面分成两种状况进行讨论,一种是接受到Invite的消息,一种是接收到一个普通的

//命令,例如

Sptr inviteMsg;

inviteMsg.dynamicCast( sipEvent->getSipMsg() );

if ( inviteMsg == 0 )

{

//如果大家在这里有什么奇怪的话没有必要,为什么除了inviteMsg以外的所有的消

//息都不处理呢?其实这些消息都在SipThread这个程序中处理了,在Ua这个大状态

//机中所有的状态都是以Invite这个消息作为启动的。每一个INVITE启动一个系列的//消息和状态。

return true;

}

else

{

//收到一个Invite消息,这个时候我们就要进入相应的处理机制中了;

callInfo = calls->newCall

( sipEvent->getSipCallLeg()->getCallId() );

callInfo->setFeature( stateMachine );

如果进入的状态是自动呼叫(Auto Call)或者是自动应答(Auto Answer)状态(这

//两种状态的确定要在CFG文件中体现)

if ( UaConfiguration::instance()->getLoadGenOn() )

{

/// Assume this is a new call...

/// Also assume that we are not in use.

callInfo->setState( stateMachine->findState( "StateAutoIdle" ) );

//StateAutoIdle这个状态是一个自动应答和自动呼叫(按照呼叫列表)时候的状态,这里我

//们不做介绍,它本身和手动呼叫是非常相似的。

}

else // LoadGen is off

{

//下面这个程序会进入等待远端SIP事件和本地呼叫事件的状态StateIdle

if( handleCallWaiting( callInfo ) )

{

cpLog( LOG_ERR, "Returned from handleCallWaiting\n" );

return true;

}

}

} // lots of brackets!

}

return false;

} /// UaBuilder::processSipEvent

handleStatusMsg在做什么?

前面我们已经作了简单的介绍,这个函数的主要目的是在处理Rgister,Notify,和Subscribe等几个状态,并且分别调用他们的处理机;

Rgister调用它的处理机:

handleRegistrationResponse他的主要作用是处理返回的各种Rgister状态,例如200,4XX或者是100等状态,另外它还负责在作为Mashal Server的时候转发各种状态时候,重新设定Expire的值;另外要注意的是在Register中增加了一个新的返回--Trying这个是非常合理的,特别是大型网络中,对服务器端的性能判定很有效,所以使用协议栈的同志能好好利用这个机制;另外如果发挥的值是401/407状态(未授权),还需要调用authenticateMessage做相应的处理,以返回的(401/407)状态中所带的密钥加密新的Rgister消息,发送给Register服务器重新进行授权判定;有兴趣的可以看看BaseAuthentication中的addAuthorization函数。在介绍UaMarshal和Redirect Server的时候会着重讨论这个问题。

注明:Subscribe的处理机在Feature Server章节里面在再详细介绍)。

2.5.1.2 processUaDeviceEvent

前面说了,processUaDeviceEvent主要是用来处理本地的设备事件,最主要就是处理摘机信号,在这里程序的流程我就不详细的列出,不过我们从主要的程序主体部分可以看出:

在uaDeviceEvent->type == DeviceEventHookUp也就是检测了摘机以后,程序会采取某些必要的方式取得CallID(主要是通过CFG文件),最后让程序进入状态机的StateIdle状态,这个状态是接收和发送消息的初始状态,我们可以在后面将会重点介绍这个状态;

2.5.1.3 processUaDigitEvent

也是主要通过判定CFG文件中的LoadGen_On的参数是On或者是Off来决定是否进入StateAutoIdle状态,或者是StateAutoRS状态(自动通过Marshal Server进行中转所有的SIP的消息和状态,在Marshal Server的时候会做详细的介绍)。

2.5.1.4 processUaTimerEvent

这个的流程也实在没有什么好说的,前面也有了一定的介绍,如果大家对这些还有不明白的话,可以看一下SIP协议中Trying过程的走势,主要是对超时处理部分的介绍,就会明白(按照前面所说的UaBuilder::Process中关于SIP命令消息超时的介绍部分)。

2.5.2 SipThread的Run方法:

Void SipThread::thread()

{

… …

while ( true )

{

try

{

//接收所发送的消息,并且准备置入相关的队列中;

Sptr sipRcv( mySipStack->receive(1000) );

if ( sipRcv != 0 )

{

Sptr sipMsg = sipRcv->back();

if ( sipMsg != 0 )

{

//根据本地的地址来检查是否发生了路由环路

if ( discardMessage(sipMsg) ) continue;

// 在这里的myOutputFifo就是 myCallProcessingQueue(异地输入消息的队

//列),在Workthread构建的时候会把这个队列带入作为处理参量

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章