• 软件测试技术
  • 软件测试博客
  • 软件测试视频
  • 开源软件测试技术
  • 软件测试论坛
  • 软件测试沙龙
  • 软件测试资料下载
  • 软件测试杂志
  • 软件测试人才招聘
    暂时没有公告

字号: | 推荐给好友 上一篇 | 下一篇

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

发布: 2007-6-23 14:09 | 作者:   | 来源:   | 查看: 13次 | 进入软件测试论坛讨论

领测软件测试网

   
  2.5 HeartLessProxy Run方法的实现
  
  HeartLessProxy::run()
  {
  myWorkerThread->run();
  mySipThread->run();
  }

  通过上面可以看到有两个Run方法的调用,第一个是WorkThread的Run方法,它的主要作用是处理UaBuilder的Process方法,主要用来处理Sptr < Fifo < Sptr < SipProxyEvent > > > myFifo中的各种事件,前面已经详细的介绍了SipProxyEvent类的作用,这个类已经在前面介绍了,其实简单的说,它就是一个本地的各种事件的集合。
  现在我们来看一下两个Run方法的实现:
  2.5.1 WorkerThread的Run方法:
  UaBuilder::process( const Sptr < SipProxyEvent > nextEvent )
  {
  //处理以下的四种事件
  /// SipEvent
  Sptr < SipEvent > sipEvent;
  sipEvent.dynamicCast( nextEvent );
  if ( sipEvent != 0 )
  {
  //处理本地的SIP的事件,包括对状态机的设置和命令/状态队列返回的操作在下面将//对它做详细的介绍
  if( processSipEvent( sipEvent ) )
  {
  return;
  }
  //向消息队列myCallContainer中插入相应的事件信息。
  sendEvent( nextEvent );
  return;
  }
  
  /// UaDeviceEvent
  Sptr < UaDeviceEvent > uaDeviceEvent;
  uaDeviceEvent.dynamicCast( nextEvent );
  if ( uaDeviceEvent != 0 )
  {
  //处理本地的设备事件,最主要的就是处理摘机信号;
  if( processUaDeviceEvent( uaDeviceEvent ) )
  {
  return;
  }
  sendEvent( nextEvent );
  return;
  }
  
  /// UaDigitEvent
  Sptr < UaDigitTimerEvent > uaDigitEvent;
  uaDigitEvent.dynamicCast( nextEvent );
  if ( uaDigitEvent != 0 )
  {
  
  //处理在规定的时间间隔(Kickstart)主动呼叫事件的触发。
  if( processUaDigitEvent( uaDigitEvent ) )
  {
  return;
  }
  sendEvent( nextEvent );
  return;
  }
  
  /// UaTimerEvent
  Sptr < UaTimerEvent > 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 > sipEvent )
  {
  Sptr < StatusMsg > 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;
  inviteMsg.dynamicCast( sipEvent->getSipMsg() );
  if ( inviteMsg == 0 )
  {
  //如果大家在这里有什么奇怪的话没有必要,为什么除了inviteMsg以外的所有的消
  //息都不处理呢?其实这些消息都在SipThread这个程序中处理了,在Ua这个大状态
  //机中所有的状态都是以Invite这个消息作为启动的。每一个INVITE启动一个系列的//消息和状态。
  return true;
  }
  else
  {
  //收到一个Invite消息,这个时候我们就要进入相应的处理机制中了;
  callInfo = calls->newCall
  ( sipEvent->getSipCallLeg()->getCallId() );
  assert( callInfo != 0 );
  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 < SipMsgQueue > sipRcv( mySipStack->receive(1000) );
  
  if ( sipRcv != 0 )
  {
  Sptr < SipMsg > sipMsg = sipRcv->back();
  
  if ( sipMsg != 0 )
  {
  //根据本地的地址来检查是否发生了路由环路
  if ( discardMessage(sipMsg) )
  {
  continue;
  }
  // 在这里的myOutputFifo就是 myCallProcessingQueue(异地输入消息的队
  //列),在Workthread构建的时候会把这个队列带入作为处理参量
  Sptr < SipEvent > nextEvent = new SipEvent(myOutputFifo);

延伸阅读

文章来源于领测软件测试网 https://www.ltesting.net/


关于领测软件测试网 | 领测软件测试网合作伙伴 | 广告服务 | 投稿指南 | 联系我们 | 网站地图 | 友情链接
版权所有(C) 2003-2010 TestAge(领测软件测试网)|领测国际科技(北京)有限公司|软件测试工程师培训网 All Rights Reserved
北京市海淀区中关村南大街9号北京理工科技大厦1402室 京ICP备2023014753号-2
技术支持和业务联系:info@testage.com.cn 电话:010-51297073

软件测试 | 领测国际ISTQBISTQB官网TMMiTMMi认证国际软件测试工程师认证领测软件测试网