协议栈的层次

SIP为应用层(Application-Layer)的协议,所以不需要改变操作系统便可以支持。SIP 已经获得3GPP (Third GenerationPartnershipProject)、3GPP2 (Third Generation PartnershipProjectNumber 2)等机构认证,成为未来第三代行动通讯 (3G) 的标准。


下面是SIP的分层图示,IETF坚持分层,不同模块功能相对独立,各层之间松散耦合。

 

 

 

 

关于Resiprocate设计

   Resiprocate中大部分类就是对RFC3261各种SIP元素、组件的封装,并且也体现了RFC协议设计的层次。

   在面向对象的设计中我们首先就要厘清问题域的所在;SIP Stack的设计就是要充分考虑完整展现RFC定义的各种元素和概念以及让这些独立而又关联的元素互动起来成为一个活的系统。

  可以这样来考虑,比如我们知道RFC定义了一个SIP MESSAGE的概念;下面是从

RFC文档拷贝的内容:

SIP 消息 = 起始行

             *消息头部

             CRLF(空行)

             [消息体]

 

 

因此SIPMessage这个概念元素还包括了更多的元素和概念;SIP Message中我们能抽

象出更通用的概念我们暂且叫它Message; 起始行的概念E文Request Line以及StatusLine又包括了很多消息头(这是包容的关系),SIPURL也包括消息头,等等,还有什么参数什么的元素呢;当我们在考虑和提炼这些概念和元素的时候,我们思考怎么抽象他们呢,它们又有什么基本的元素及其共性呢?他们之间的关系如何组织呢?Resiprocate的源码告诉了我们如何去设计和封装这些概念的上佳实现。在Resiprocate中一些RFC3261中定义元素的对应:

建议:利用CRC卡片的方式去记录理解Resiprocate中的大量的类及其关系。CRC:类、职责、协作。

部分设计的理解

OBSERVER/VISITOR/COMMAND/ITERATOR模式,工厂模式(大量容器的使用也是一种变体如:DialogSet),代理类句柄类(界面实现分离,隐藏实现…),……

大量的界面类(如AppXXX系列)是遵循大师BS“界面和实现分离”的原则吧;而句柄方式对对象的间接管理是老外的惯用伎俩啦,关于句柄设计从大师BS的著作到<>的Handle_Body论和<>的大段描述再到<>都有发挥和外延,感兴趣可以观之。

插播:

    源码中的大量Clone函数是模仿大师BS的虚拟构造函数一说,是原型模式的体现;源码中对同步的封装值得借鉴,其中有“资源开始即初始化”理论的体现;在DUM部分回调机制所遵循的著名“好莱坞原则”;句柄和代理的一个特点就是重载了operator->operator*等;源码中也非常注重效率如Sip Core部分中大量Hash表的建立

      T*operator->()

      {

         returnget();

      }

      const T* operator->() const

      {

         returnget();

      }

      T& operator->()

      {

         return*get();

      }

      constT& operator*() const

      {

         return*get();

     }

Handled::Handled(HandleManager&ham) :

   mHam(ham),

   mId(Handled::npos)

{

   mId = mHam.create(this);

}

Handled::Id

HandleManager::create(Handled*handled)

{

   mHandleMap[++mLastId] = handled;// typedef HashMapHandleMap;

   //HandleMap mHandleMap;

   returnmLastId;

}

1. SIP Stack分析


11 Resiprocate SIP Stack系统架构图示

 

 

 

12 FIFO流的走向图

 

13 Sending datagram


 

1.    4 Process Incoming UDP

 

 



2. Application/DUM

 

设计浅析

抽象接口:CLASSHANDLED ,CLASSInviteSessionHandler(诸如此类) ……

对象之源:CLASSHANDLED多态和控制的基础……

交互控制: CLASS Handle,CLASS HandleManager……

概念封装成类典型CLASS Dialog, CLASS DialogId, CLASSDialogSet, CLASSDialogSetId, CLASS InviteSession..

Utility工具类:CLASS BaseCreator , CLASS DestroyUsage,CLASS Profile……

流动之源:DialogUsageManager::process()Dialog::dispatch(const SipMessage& msg)……

状态机的位置:DialogUsageManager::incomingProcessDialogSet::dispatchDialog::dispatch

在整个Resiprocate大家族中事务层概念[1]的体现是TransactionUser类,而其真正的实现和管理类就是DialogUsageManager;从其:

class DialogUsageManager : publicHandleManager, public TransactionUser

能看出来;HandleManager点出了DialogUsageManager的管理功能的本质,并且管理各种对象(Handle是各类对象的句柄)。

在整个Resiprocate系统中不管是我们发出或者收到的SIPMessage都是放进了先进先出的队列然后不断轮询处理,这有点象Windows的消息系统,对应收发的消息DUM提供事件通知的机制。DUM利用事件回调模型,事件响应可以选择继承系列XXXHandler抽象接口,并向TU注册,以实现VISITOR模式;我在另外的文档里也提到这是Reactor(Dispatcher,Notifier)模式,应用程序开发者只负责实现具体事件处理程序,并在反应器上注册它们 ----“好莱坞原则”。

DialogUsageManager是sip事务层管理类,是一个大总管的角色;看其Makexxx系列的函数能明白它能发起一系列登陆、会话邀请的动作及其回复。
Dum定义了很多句柄管理类,通过它我们能得到真实的对象,从而完成操作,这在事件响应中非常有用。在Dum(DialogUsageManager)的类中基本上这样一条线(以INVITE为例):DialogUsageManager产生Dialog Set, Dialog Set产生Dialog, Dialog产生InviteSession; InviteSession又分Client InviteSession和Server InviteSession。

而上面的各个对象的PROCESS或者DISPATCH函数产生的各种状态的变化并触发相应事件。在DUM的IM/PRESENSE部分广泛使用SUBSCRIBE/NOTIFY的模式,目前协议的定义似乎参照成熟的设计模式。

个人一直比较喜欢这段论述:

session有两种含义,一种是传统意义上的RTP会话,一种是信令意义上的会话。SIP中的会话指后一种,在层次上它在dialog之上,也就是dialog可以看成session的组成单元。二者的分别主要基于目前出现的subscription应用,对于session和subscription可以共享一个dialog,dialog由基本的会话标识组成,如  Call-ID,From-Tag,To-Tag,以及一些目的target等共性单元组成。而session除了具备这些单元外,包含INVITE建立起的会话其他内容,例如INVITE引起的状态机处理内容、PRACK处理记录等内容。有一个最为重要的区别是:Session是完成了SDP的Offer-Answer过程,也就是此时双方可以进行双向的RTP传输了。而Dialog只是双方建立了联系,这个联系是通过Dialog Context来记录的。在Dialog状态下双方不一定可以作双向的RTP传输。所以必定是Dialog在前,而Session在后,但两者可以同时一起建立。 Session是基于SDP Message的交互,没有SDP的交互,就没有Session。而Dialog是基于请求消息中的Header Field进行交互。因此两者在层次上也是不一样的。

 

 

下图是DUM中各种对象实例间的关系表示

 

DUM中几个重要的类图:

   

 

 


 

 

 

 

 

3. RESIPROCATE SIP Core重要模块的简单介绍

SipStack模块

SipStack是Sip Stack Core的面向外界的接口;可以说它是SipStack Core的外覆类(wrapper)或者是界面类(以大师BS的观点来看),它是和外界交互的窗口和协议,具体的实现又分散到更具体的实现类和层次。

 

在整个的协议栈架构中SipStack处于承上启下的位置,它既面向DUM层(表现为向DialogUsageManager服务)又可以直接为外界(如UI)所用;列举其主要面向外界的接口函数:

 

/**

         Adds a TU to the TU selection chain. Tu's do not call receive or

          receiveAny, theSipStack will call postToTu on the appropriate

          Tu. Messages notassociated with a registered TU go into SipStack::mTuFifo.

      */

 voidregisterTransactionUser(TransactionUser&);

 

注释基本上已经讲的很清楚,我就补充其交互的细节吧。这个函数基本上能让我们看到TU和SipStack交互间其visitor/observer模式的影子。

void

SipStack::registerTransactionUser(TransactionUser&tu)

{

   mTuSelector.registerTransactionUser(tu);

}

回溯SipStack的成员变量:

/// Responsible for routing messages to thecorrect TU based on installed rules

     TuSelector mTuSelector;

把SipStack中的Transaction User角色独立出来交与一个独立的类管理是个不错的选择。

消息的流向分向两端,一路向下直达Transport层而去,一路向上提交Transaction User直至UI。

因此接口类Message中存在这个函数就不足为奇啦:

class Message

{

   。。。。。。

      voidsetTransactionUser(TransactionUser* t) { tu = t; }

      TransactionUser* getTransactionUser() { return tu; }

      TransactionUser* tu;     

};

正是setTransactionUser给予消息Transaction User归属。

而在TransactionState中我们将会看到SipMessage是如何兵分两路的:

static voidsendToTU(TransactionUser* tu, TransactionController& controller,TransactionMessage* msg);    

voidsendToWire(TransactionMessage* msg, boolretransmit=false);

现在我们先重点关注一下sendToTU:

void

TransactionState::sendToTU(TransactionUser*tu, TransactionController& controller, TransactionMessage* msg)

{

   msg->setTransactionUser(tu);  

   controller.mTuSelector.add(msg,TimeLimitFifo::InternalElement);

}

 

上面探索了一下SipStack和TU层的交互,跑远啦,让我们再回到SipStack看看这个界面类其他的迎来送往的窗口吧。

一、发送消息的两个重载函数:

 

/** Interface for the TU to send a message.  Makes a copy of the SipMessage.  Caller is responsible for deleting the memoryand may do so as soon as it returns. Loose Routing processing as per RFC3261 must be done before calling sendby the TU. See Helper::processStrictRoute @param msg SipMessage to send. @paramtu  TransactionUser to send from.*/

      voidsend(const SipMessage& msg,TransactionUser* tu=0);

     void send(std::auto_ptrmsg, TransactionUser* tu = 0);

再看了一下这两个函数体的实现比较简单,还是端上来吧:

void

SipStack::send(const SipMessage& msg, TransactionUser* tu)

{

   SipMessage* toSend = new SipMessage(msg);

   if (tu)

   {

      toSend->setTransactionUser(tu);

   }

   toSend->setFromTU();

   mTransactionController.send(toSend);

   checkAsyncProcessHandler();

}

 mTransactionController.send(toSend)这一段才是我们值得关注的地方,很显然这是消息的去向,现在我们不用管它去向哪儿,我们知道它被转交给mTransactionController去具体完成任务,就象在一条生产线上我们把消息交给了下游或者另一个直管部门去处理。SipMessage是我们投递出的邮件,从投向邮筒的那一瞬间,开始了它向各个驿站的传递;出于职责链上的各个类和对象是任务传递驿站的抽象和实现,是代码所表现出的和谐世界,当然如果是垃圾代码的话,那就是一个混乱的世界和杂物堆放的仓库。

checkAsyncProcessHandler()这是协议栈提供的异步消息处理的一种推荐方式,协议栈源码中只是实现了个大概,具体的异步处理细节需要根据具体情况而完成其实现类,此处再次体现了“封装变化的概念”的要诀。因为异步处理句柄是指向抽象基类的指针:

AsyncProcessHandler*mAsyncProcessHandler;

 

二、SendTo有好几个重载函数,看看注释再和Send比较一下就可耶。

/** this is only if you want to send to a destination not in theroute. You probably don't want to use it. */

      voidsendTo(std::auto_ptr msg, constUri& uri, TransactionUser* tu=0);

      voidsendTo(std::auto_ptr msg, constTuple& tuple, TransactionUser* tu=0);

      voidsendTo(const SipMessage& msg, const Uri& uri, TransactionUser* tu=0);

      voidsendTo(const SipMessage& msg, const Tuple& tuple,

                  TransactionUser* tu=0);

三、就如在Win32编程中有SendMessage和PostMessage一样,SipStack消息分发中也有一个Post。

  /**

          Makes the messageavailable to the TU at some later time - specified in

          seconds.

          Note:  TranasactionUser subclasses can just post to themselves.

          @param messageApplicationMessage to post

          @paramsecondsLater Number of seconds before message is to be posted.

          @param tu    TransactionUser to post to.

      */

      voidpost(conststd::auto_ptr message,

                unsigned int secondsLater,

                TransactionUser* tu=0);

      voidpostMS(conststd::auto_ptr message,

                  unsignedint ms,

                  TransactionUser* tu=0);

      voidpost(const ApplicationMessage& message);

      voidpost(const ApplicationMessage& message, unsigned intsecondsLater,

                TransactionUser* tu=0);

 

      /**

          Makes the messageavailable to the TU at some later time - specified in

         milli-seconds.  Makes a copy ofthe ApplicationMessage.  Caller is

          responsible fordeleting the memory and may do so as soon as it returns. 

          Note:  TransactionUser subclasses can just post tothemselves

          @param messageApplicationMessage to post

          @param ms      Number of milli-seconds before message isto be posted.

          @param tu      TransactionUser to post to.

      */

      voidpostMS(const ApplicationMessage& message, unsigned int ms,

                  TransactionUser* tu=0);

ApplicationMesage基本上是个结点类,因此可以自定义自己的消息类,然后自己管理;因为我们可以看到Post出去的消息更多的是交给TU去处理,TU层就在应用层隔壁。

四、下面的函数,非常容易看明白。

      /**

          Retrieve aSipMessage off the old TuFifo.  Callernow owns the memory.  Returns

          0 if nothingthere.  Since the addition ofTransactionUsers, this method

          isdeprecated.  This only looks into the oldTuFifo that is not associated

          with anyTransactionUser.

         Note:  Applications posting non-sip messages mustuse receive any.  If non

                 SipMessagesare on the Fifo, then they are just deleted.

          @deprecated

          @returns pointerto received SipMessage, 0 if nothing there.

      */

     SipMessage* receive();

      /**

          Retrieve a Messageoff the old TuFifo.  Caller now owns thememory.  Returns

          0 if nothingthere.  Since the addition ofTransactionUsers, this method

          isdeprecated.  This only looks into the oldTuFifo that is not associated

          with anyTransactionUser.

         Note:  Applications postingnon-sip messages must use receive any. If non

                 SipMessagesare on the Fifo, then they are just deleted.

          @deprecated

          @returns pointerto received Message, 0 if nothing there. May return

                   TransactionTerminated*,TimerMessage*, SipMessage* or derived

                  ApplicationMessage*

      */

     Message* receiveAny();

 

      /**

          Return true if thestack has new messages for the TU.  Sincethe addition

          of TransactionUsers,this method is deprecated.  This onlylooks into the

          old TuFifo that isnot associated with any TransactionUser.

          @deprecated

      */

     bool hasMessage() const;

 

五、几个成员变量的说明

 

/// if this object exists, it manages advanced security featues

      Security* mSecurity;

这是如果需要安全处理而用到的类。

      DnsStub*mDnsStub;

这是如果自己定义DNS处理的话所需要的DNS处理的存根和截取器。

      /// if this object exists, it get's notified whenApplicationMessage's get posted

      AsyncProcessHandler*mAsyncProcessHandler;

异步处理的基类指针对象,可以自定义异步处理的方式。

     /** fifo used to communicate between the TU (Transaction User) and stack

          Note:  since the introduction of multiple TU's thisFifo should no

          longer be used bymost applications - each TU now owns it's own Fifo. */

      TimeLimitFifo mTUFifo;

这个就不用说啦,看注释吧。

      /// Protection for AppTimerQueue

      mutableMutex mAppTimerMutex;

整个协议栈的同步都是集中利用了源码中实现的同步对象,非常不错,实现同步类的理论基础应该是基于大师BS的理论“资源开始即初始化”。

      /// All aspects of the Transaction State Machine / DNS resolver

      TransactionControllermTransactionController;

      /** store alldomains that this stack is responsible for. Controlled by

          addAlias andaddTransport interfaces and checks can be made with isMyDomain() */

      std::set mDomains;

      /// Responsiblefor routing messages to the correct TU based on installed rules

     TuSelector mTuSelector;

 

TransactionController模块

在SipStack中看到这样的描述:

/// All aspects of the Transaction StateMachine / DNS resolver

TransactionControllermTransactionController;

注意all aspects在我的理解中这又是一个界面类,SipStack是Sip Core整个庭院的外围的门面的话这又是进入庭院后第二道城墙;就如庭院高深的故宫和亭廊回环的苏州园林它们的实现中更有层次的递张以及庭院间的交互回环。

BS教导我们不要写“臃肿的界面”,SipStack这个大界面里面又套这个实现层次的界面是不是一种好的实践呢?我的理解是,应该是这样的。―――努力达到界面和实现分离。

现在就看看这个Transaction State Machine / DNS resolver界面类吧,在我的实现中,其实把DNSResolver部分清理掉啦,导致只是个TransactionState Machine的界面类吧。

如果仔细看看TransactionControl的函数也就是它的接口函数的话和SipStack就差不离,翻版而也,就是一道门进去还是一道门,就连造型都差不多,外面是拱洞型的里面还是;故宫和苏州园林的设计把古建筑的设计模式推向了极致。

还是列一下吧:

      void process(FdSet& fdset);

      unsigned int getTimeTillNextProcessMS();

      voidbuildFdSet(FdSet& fdset);

      // gracefulshutdown (eventually)

      voidshutdown();

      TransportSelector&transportSelector() { return mTransportSelector;}

      constTransportSelector& transportSelector() const{ return mTransportSelector; }

      boolisTUOverloaded() const;

     void send(SipMessage* msg);

再看一下几个重要的成员变量:

     // fifo used tocommunicate to the transaction state machine within the

      // stack. Notfor external use by the application. May contain, sip

     // messages (requests and responses), timers (used by state machines),

      // asynchronousdns responses, transport errors from the underlying

      // transports,etc.

      FifomStateMacFifo;

      // from thesipstack (for convenience)

      TuSelector& mTuSelector;

      // Used todecide which transport to send a sip message on.

      TransportSelector mTransportSelector;

      // stores allof the transactions that are currently active in this stack

      TransactionMap mClientTransactionMap;

     TransactionMap mServerTransactionMap;

重点和关键成员变量:

FifomStateMacFifo;

看看这个成员变量的价值:

void

TransactionController::send(SipMessage*msg)

{

   mStateMacFifo.add(msg);

}

 

消息的流动不可能到此为止,它要上传下递,如何实现的呢?

看看构造函数吧:

TransactionController::TransactionController(SipStack&stack) :

   mStack(stack),

   mRegisteredForTransactionTermination(false),

   mDiscardStrayResponses(true),

   mStateMacFifo(),

   mTuSelector(stack.mTuSelector),

   mTransportSelector(mStateMacFifo, stack.getSecurity(),stack.getDnsStub()),

   mTimers(mStateMacFifo),

   mShuttingDown(false),

   mStatsManager(stack.mStatsManager)

{

}

既然都点出来了,就继续往下看看,看看流向TransportSelector会怎样呢

TransportSelector::TransportSelector(Fifo&fifo, Security* security, DnsStub& dnsStub) :

   mDns(dnsStub),

   mStateMacFifo(fifo),

   mSecurity(security),

   mSocket( INVALID_SOCKET ),

   mSocket6( INVALID_SOCKET )

{

   memset(&mUnspecified.v4Address, 0, sizeof(sockaddr_in));

   mUnspecified.v4Address.sin_family =AF_UNSPEC;

}

再让我们看另外一支,只是一直在Process中处理的:

void

TransactionController::process(FdSet&fdset)

{

   if(mShuttingDown &&

       !mStateMacFifo.messageAvailable()&&        !mStack.mTUFifo.messageAvailable()&&

       mTransportSelector.isFinished())

   {

    。。。。。。 

   }

   else

   {

     。。。。。。

      while(mStateMacFifo.messageAvailable())

      {

         TransactionState::process(*this);

      }

   }

}

 

TransactionState的这个静态函数Process的参数居然是*this,也就是是说TransactionState能从TransactionControler得到它所要的内容,因此先让我们过去浏览一下先:

 

想了一下这儿放下暂且不表,我们到TransactionState部分再详细到来,但是我们知道下一步的流向将是哪儿。

 

再粗粗的看了一下TransactionControler我们发现内容并不多,Control者只是一个控制调节的角色,它是一个contact window;向外是联系的窗口,向内负责安排和分派其他包容的角色去实现。

 

在TransactionControler中有两个比较重要的成员变量:

// from the sipstack (for convenience)

      TuSelector& mTuSelector;

      // Used todecide which transport to send a sip message on.

      TransportSelector mTransportSelector;

      // stores allof the transactions that are currently active in this stack

      TransactionMap mClientTransactionMap;

     TransactionMap mServerTransactionMap;

 

可以看出它包含和管理的分别是三个层次的对象:

Transaction User、Transport、TransactionState

它对这几个对象的互动和控制基本就在Process里面完成。

 

并且为了以示关联,我们先看看mClientTransactionMap是在哪儿体现它的价值的:

void

TransactionState::add(const Data& tid)

{

   if (mMachine== ClientNonInvite || mMachine == ClientInvite || mMachine == ClientStale ||mMachine == Stateless )

   {

     mController.mClientTransactionMap.add(tid, this);

   }

   else

   {

     mController.mServerTransactionMap.add(tid, this);

   }

}

好啦,TransactionControler基本就介绍这么多,接下来是比较重要的一环。

 

TransactionState模块

和其他几个类不同,TransactionState继承自一个叫DnsHandler的接口

类(里面全是纯虚函数)。

 

class DnsHandler

{

   public:

      virtual~DnsHandler()=0;

     

      // call whendns entries are available (or nothing found)

     // this may be called synchronously with the call to lookup

      virtual void handle(DnsResult* result)=0;

      virtual void rewriteRequest(constUri& uri)=0;

};

看看注释就能了解这个接口说明它有部分要去解决Dns的问题,这也是那个叫Ares的源码部分戮力攻克的问题,可惜这部分我在移植时大刀阔斧的把它去掉了,因此这个功能角色在我这儿完全失去了它的意义,它的意义也就完全是TransactionState了。

 

TransactionState就是事务状态处理;协议栈事务状态机的处理就在这儿完成;按照RFC定义这个类主要定义了下面的类型和状态:

typedef enum

      {

         ClientNonInvite,

         ClientInvite,

         ServerNonInvite,

         ServerInvite,

         ClientStale,

         ServerStale,

         Stateless  // may not beneeded

      } Machine;

     

      typedef enum

      {

         Calling,

         Trying,

         Proceeding,

         Completed,

         Confirmed,

         Terminated,

         Bogus

      } State;

 

State主要针对的是INVITE客户端/服务端事务的状态,这部分状态机详细内容可以参照RFC关于SIP 事务层的介绍。

 

下面简单摘录了两幅来自RFC3261中文文档中INVITEKE客户/服务端状态转换图:

 

 

 

 

 

 

 

 

还是先来看看它的几个主要成员变量,这样方能看出它的职责和角色定位以及它和其他类及层次的关联及交互。

 

TransactionController& mController;

 

MachinemMachine;

StatemState;

bool mIsCancel;

     

 // Indicates thatthe message has been sent with a reliable protocol. Set

 // by theTransportSelector

 boolmIsReliable;

 

 //!rk! The contractfor this variable needs to be defined.

 SipMessage* mMsgToRetransmit;

 

 // Handle to the dnsresults queried by the TransportSelector

 DnsResult* mDnsResult;

// current selection from the DnsResult.e.g. it is important to send the

// CANCEL to exactly the same tuple as the original INVITE wentto.

TuplemTarget;

TuplemResponseTarget; // used to reply to requests

 

DatamId;

Data mToTag; // for failure responseson ServerInviteTransaction

TransactionUser* mTransactionUser;

 

代表状态机的那两个成员变量肯定是需要的啦;我们看到SipMessage*mMsgToRetransmit;会不会疑惑名字为啥叫这个呢?名字后面跟个Retransmit?其实我们每次传出去SipMessage的同时就要放到mMsgToRetransmit,这是为了保证重传的可能,这和Timer控制是有关系的。DnsResult* mDnsResult;是为了DNS的处理而来,只是在目前的系统中实在是多余了,不过为维护将来的变化或者DNS的独立处理留着这个接口还是可行的。Tuple mTarget;我一直不太明白这个类为什么叫TUPLE,在《C++设计新思维》以及其他设计模式的书中能看到这个名词,TUPLE应该是个小元件之类的东西;可惜我们这儿怎么看也就是是个目标地址并且主要是Socket地址的封装,可能我还没有完全领悟这个类型的妙处。

 

Data  mId;每个SipMessage最终都会产生个事务ID以标志它所产生它的事务,这个事务ID是唯一的,因此一个事务ID就可以代表一个TransactionState,这是个一一对应的关系;关于事务ID是如何产生RFC2543提供了借鉴的办法,我们可以在SipMessage::compute2543TransactionHash()看到详细的产生过程。

 

TransactionUser*mTransactionUser;指向本事务所关系的TU,而TransactionController&mController;就不用多讲啦,它们之间的关系是如此的密切,基本上TransactionState是受到TransactionController的管理的,TransactionController是TransactionState的聚合类。

 

 TransactionState是个动作型演员,它主要负责完成一系列动作;从一系列的Process函数名可见一斑

TransactionState有一系列叫Process的函数,Process者步骤进行也;Process的进行中就会产生一系列的状态转换。

 

从构造函数始我们就能看出TransacionState的关键和核心。

TransactionState(TransactionController&controller,

                       Machine m,

                       State s,

                       constData& tid,

                       TransactionUser* tu=0);

controller和tu代表与外界的关系,tid代表自身的标志,而m、s就是内部本身的状态变幻了。

static voidprocess(TransactionController& controller);

Process是就如开始说的一样,并且是一静态函数,被TransactionControler所调用;大致看一下其流程走向:

void

TransactionState::process(TransactionController&controller)

{

   TransactionMessage* message =controller.mStateMacFifo.getNext();

  。。。。。。

   SipMessage* sip = dynamic_cast(message);

  。。。。。。

   Datatid = message->getTransactionId();

   // This ensuresthat CANCEL requests form unique transactions

   if (sip&& sip->header(h_CSeq).method() == CANCEL)

   {

      tid+= "cancel";

   }

   TransactionState* state = 0;

   if(message->isClientTransaction()) state =controller.mClientTransactionMap.find(tid);

   else state =controller.mServerTransactionMap.find(tid);

   if (state) // found transaction for sip msg

   {

      switch(state->mMachine)  

      {

         caseClientNonInvite:

           state->processClientNonInvite(message);

            break;

         caseClientInvite:

           // ACK from TU will be Stateless

            assert (!(state->isFromTU(sip)&&  sip->isRequest()&& sip->header(h_RequestLine).getMethod() == ACK));

           state->processClientInvite(message);

            break;

         caseServerNonInvite:

           state->processServerNonInvite(message);

            break;

         case ServerInvite:

           state->processServerInvite(message);

            break;

         caseStateless:

           state->processStateless(message);

            break;

         caseClientStale:

           state->processClientStale(message);

            break;

         caseServerStale:

           state->processServerStale(message);

            break;

         default:

            return;

      }

   }

   else if (sip)  // new transaction

   {

      TransactionUser* tu = 0;     

      if (sip->isExternal())

      {

       

      }

      else

      {

        。。。。。。

      }

      if(sip->isRequest())

      {

         if(sip->isExternal()) // new sip msg from transport

         {

            if(sip->header(h_RequestLine).getMethod() == INVITE)

            {

               //!rk! This might be needlessly created. Design issue.

               TransactionState* state = new TransactionState(controller, ServerInvite,Trying, tid, tu);

               state->mMsgToRetransmit =state->make100(sip);

               state->mResponseTarget =sip->getSource(); // UACs source address

               //since we don't want to reply to the source port unless rport present

              state->mResponseTarget.setPort(Helper::getPortForReply(*sip));

               state->mState = Proceeding;

               state->mIsReliable =state->mResponseTarget.transport->isReliable();

               state->add(tid);

              

               if(Timer::T100 == 0)

               {

                 state->sendToWire(state->mMsgToRetransmit); // will get deleted when this is deleted

              }

               else

               {

                 。。。。。。

               }

            }

            elseif (sip->header(h_RequestLine).getMethod()== CANCEL)

            {

               TransactionState* matchingInvite=

                 controller.mServerTransactionMap.find(sip->getTransactionId());

               if(matchingInvite == 0)

               {

                 TransactionState::sendToTU(tu, controller, Helper::makeResponse(*sip,481));

                  deletesip;

                  return;

               }

               else

               {

                 。。。。。。

               }

            }

            elseif (sip->header(h_RequestLine).getMethod()!= ACK)

            {

               。。。。。。

            }

           

            //Incoming ACK just gets passed to the TU

            TransactionState::sendToTU(tu,controller, sip);

         }

         else // new sip msg from the TU

         {

            if(sip->header(h_RequestLine).getMethod() == INVITE)

            {

               TransactionState* state = new TransactionState(controller, ClientInvite,Calling, tid, tu);

               state->add(state->mId);

              state->processClientInvite(sip);

            }

            elseif (sip->header(h_RequestLine).getMethod()== ACK)

            {

                 TransactionState* state = new TransactionState(controller, Stateless, Calling,tid, tu);

               state->add(state->mId);

。。。。。。

               state->processStateless(sip);

            }

            elseif (sip->header(h_RequestLine).getMethod()== CANCEL)

            {

                   。。。。。。

            }

            else

            {

               TransactionState* state = new TransactionState(controller, ClientNonInvite,

                                                             Trying, tid, tu);

               state->add(tid);

              state->processClientNonInvite(sip);

            }

         }

      }

      else if (sip->isResponse()) //stray response

      {

         if(controller.mDiscardStrayResponses)

         {

            deletemessage;

         }

         else

         {

           TransactionState* state =

               newTransactionState(controller, Stateless, Calling,

                                   Data(StatelessIdCounter++), tu);

            state->add(state->mId);

            state->mController.mTimers.add(Timer::TimerStateless,state->mId, Timer::TS );

            state->processStateless(sip);

         }

      }

      else // wasn't a request or a response

      {

      }

   }

   else    {

      deletemessage;

   }

}

 

我精减大致的流程后我们可以看到TransactionState::Process的处理分成了几类,如:

sip->isExternal()

if (state)(其中又细分switch(state->mMachine)

if (sip->isRequest())

else if(sip->isResponse()) // stray response

 

isExternal()是代表是否是从外面通过Transport层接收的消息,因此是做为服务端的角色;

SipMessage分成两类,我们按照源码的注释来看是:

sip msg from transport

sip msg from the TU

当没有和消息对应的TransactionState对象时,我们还要生成新的TransactionState对象,然后根据Sip Message的方法进行处理,以一个新的INVITE为例:

if(sip->header(h_RequestLine).getMethod() == INVITE)

            {

               TransactionState* state = new TransactionState(controller, ClientInvite,Calling, tid, tu);

               state->add(state->mId);

              state->processClientInvite(sip);

            }

 

当我们对应消息已经是有TransactionState对应时,根据其状态机进行处理,如:

switch (state->mMachine)

      {

         caseClientNonInvite:

           state->processClientNonInvite(message);

            break;

。。。。。。

}

 

当没有对应状态机时我们还要根据其到底是if (sip->isRequest())

else if(sip->isResponse())产生新的状态机进行处理。

源码中针对CANCEL和ACK按照RFC的定义进行了详细处理。

接下来,看看往Transport和TU层是如何发的:

void

TransactionState::sendToWire(TransactionMessage*msg, bool resend)

{

  SipMessage* sip = dynamic_cast(msg);

   // !jf! forresponses, go back to source always (not RFC exactly)

   if (mMachine== ServerNonInvite || mMachine == ServerInvite || mMachine == ServerStale)

   {

      Tuple target(mResponseTarget);

      if(sip->hasForceTarget())

      {

         target =simpleTupleForUri(sip->getForceTarget());

         target.transport =mResponseTarget.transport;

      }

      else if (sip->header(h_Vias).front().exists(p_rport)&& sip->header(h_Vias).front().param(p_rport).hasValue())

      {

        target.setPort(sip->header(h_Vias).front().param(p_rport).port());

      }

      else

      {

      }

      if(resend)

      {

         mController.mTransportSelector.retransmit(sip,target);

      }

      else

      {

        mController.mTransportSelector.transmit(sip, target);

      }

   }

   else if (sip->getDestination().transport)

   {

     mController.mTransportSelector.transmit(sip, sip->getDestination()); // dns not used

   }

   else if (mDnsResult == 0 && !mIsCancel) // no dns query yet

   {

      assert(sip->isRequest());

      assert(!mIsCancel);

      mDnsResult =mController.mTransportSelector.createDnsResult(this);

      mController.mTransportSelector.dnsResolve(mDnsResult,sip);

   }

   else // reuse the last dns tuple

   {

      assert(sip->isRequest());

      assert(mTarget.getType() !=UNKNOWN_TRANSPORT);

      if(resend)

      {

         if(mTarget.transport)

         {

            mController.mTransportSelector.retransmit(sip,mTarget);

         }

         else

         {

         }

      }

      else

      {

        mController.mTransportSelector.transmit(sip, mTarget);

      }

   }

}

 

这期间需要进行DNS的处理然后确定目标地址即如mTarget者,然后交与mController.mTransportSelector.transmit

Transmit暂且不表。

再来看看

void

TransactionState::sendToTU(TransactionUser*tu, TransactionController& controller, TransactionMessage* msg)

{

   if (!tu)

   {

   }

   else

   {

   }

   msg->setTransactionUser(tu);  

   controller.mTuSelector.add(msg, TimeLimitFifo::InternalElement);

}

看看TransactionState把自己如何放到TransactionControler中去的:

void

TransactionState::add(const Data& tid)

{

   if (mMachine== ClientNonInvite || mMachine == ClientInvite || mMachine == ClientStale ||mMachine == Stateless )

   {

     mController.mClientTransactionMap.add(tid, this);

   }

   else

   {

     mController.mServerTransactionMap.add(tid, this);

   }

}

每个从上至下的消息从开始之初就打上了TU的烙印,而每个从下传到TU的它们是在

  tu= controller.mTuSelector.selectTransactionUser(*sip);

之后再打上去的,不用担心这个TU就是你SipStack最先提到的注册的那个TU。

我们看到TransactionState的很多动作最后又交给TransactionControler的一些成员来处理,毕竟TransactionState的角色比较单一,真正的管理者和统一管理各个角色的主还是TransactionControler。

 

接下来让我们看看两个Selector

TransportSelector模块:

 

我们看到SipStack伸向外界的函数中的:addTransport;它的最底层的最终实现就是TransportSelector的addTransport

看看TransportSelector::addTransport最外层函数的一些默认值,在实际应用中,我们基本上用的就是默认值。

void addTransport( TransportTypeprotocol,

                         int port=0,

                         IpVersion version=V4,

                         const Data& ipInterface = Data::Empty,

                         const Data& sipDomainname = Data::Empty, // only used

                                                                 // for TLS

                                                                 // based stuff

                         const Data& privateKeyPassPhrase = Data::Empty,

                         SecurityTypes::SSLTypesslType = SecurityTypes::TLSv1 );

 

因此实际中,我们的add动作是:

void

TransportSelector::addTransport(std::auto_ptr tAuto)

{

   Transport* transport = tAuto.release();  

  mDns.addTransportType(transport->transport(),transport->ipVersion());

   switch(transport->transport())

   {

      case UDP:

      case TCP:

      {

         Tuplekey(transport->interfaceName(), transport->port(),

                   transport->ipVersion(),transport->transport());

        

         assert(mExactTransports.find(key) ==mExactTransports.end() &&

               mAnyInterfaceTransports.find(key) == mAnyInterfaceTransports.end());

         if(transport->interfaceName().empty())

         {

            mAnyInterfaceTransports[key] =transport;

            mAnyPortAnyInterfaceTransports[key]= transport;//这是默认的,我们一般是这儿。

         }

         else

         {

            mExactTransports[key] = transport;

           mAnyPortTransports[key] = transport;

         }

      }

      break;

      。。。。。。

   }

 

   if(transport->shareStackProcessAndSelect())

   {

      //默认的也是这一块,因此后面的其他动作都从mSharedProcessTransports而来

      mSharedProcessTransports.push_back(transport);

   }

   else

   {

     mHasOwnProcessTransports.push_back(transport);

      transport->startOwnProcessing();

   }

}

 

DNS的处理在TransportSelector中得到了体现:

void

TransportSelector::dnsResolve(DnsResult*result,

                              SipMessage* msg)

{

   // Picking thetarget destination:

   //   - for request, use forced target if set

   //     otherwise use loose routing behaviour(route or, if none, request-uri)

   //   - for response, use forced target if set,otherwise look at via

 

   if(msg->isRequest())

   {

      // If this isan ACK we need to fix the tid to reflect that

      if(msg->hasForceTarget())

      {

         mDns.lookup(result,msg->getForceTarget());

      }

      else if (msg->exists(h_Routes) &&!msg->header(h_Routes).empty())

      {

         // put thisinto the target, in case the send later fails, so we don't

        // lose the target

        msg->setForceTarget(msg->header(h_Routes).front().uri());

         mDns.lookup(result,msg->getForceTarget());

      }

      else

      {

         mDns.lookup(result,msg->header(h_RequestLine).uri());

      }

   }

   else if (msg->isResponse())

   {

   }

   else

   {

      assert(0);

   }

}

DNS的处理应该大致能看个明白,因此DnsResult::lookup部分就应该完全改写,DNS的处理完成利用系统的DNS API函数而不用ares那个庞大的库。

往相应Transport层传送Sip Message的函数应该是非常重要的关节;看看吧:

// !jf! there may be an extra copy of a tuple here. can probablyget rid of it

// but there are some const issues.

void

TransportSelector::transmit(SipMessage*msg, Tuple& target)

{

   assert(msg);

   try

   {

      // !ah! YouNEED to do this for responses too -- the transport doesn't

      // !ah! knowit's IP addres(es) in all cases, AND it's function of the dest.

      // (imagine asynthetic message...)

 

      Tuple source;

      if(msg->isRequest())

      {

         // theremust be a via, use the port in the via as a hint of what

         // port tosend on

         source = determineSourceInterface(msg,target);

         // wouldalready be specified for ACK or CANCEL

         if(target.transport == 0)

         {

            if(target.getType() == TLS)

            {

               target.transport =findTlsTransport(msg->getTlsDomain());

               //target.transport= findTlsTransport(msg->header(h_From).uri().host());

            }

            else

            {

               target.transport =findTransport(source);

            }

         }

         if(target.transport) // findTransport may have failed

         {

            Via&topVia(msg->header(h_Vias).front());

            topVia.remove(p_maddr); // !jf! why do this?

            // insertthe via

            if(topVia.transport().empty())

            {

               topVia.transport() =Tuple::toData(target.transport->transport());

            }

            if(!topVia.sentHost().size())

            {

              msg->header(h_Vias).front().sentHost() = Tuple::inet_ntop(source);

            }

            if(!topVia.sentPort())

            {

              msg->header(h_Vias).front().sentPort() = target.transport->port();

            }

         }

      }

      else if (msg->isResponse())

      {

         // We assumethat all stray responses have been discarded, so we always

        // know the transport that the corresponding request was received on

         // and thishas been copied by TransactionState::sendToWire into target.transport

         assert(target.transport);

         if(target.transport->getTuple().isAnyInterface())

         {

            source =determineSourceInterface(msg, target);

         } else{

            source = target.transport->getTuple();

         }

      }else{

         assert(0);

      }

      if(target.transport)

      {

         // There isa contact header and it contains exactly one entry

         if(msg->exists(h_Contacts) && !msg->header(h_Contacts).empty())

        {

            for(NameAddrs::iterator i=msg->header(h_Contacts).begin(); i !=msg->header(h_Contacts).end(); i++)

            {

               NameAddr& contact = *i;

            // No host specified, so use the ip addressand port of the

               // transportused. Otherwise, leave it as is.

               if(contact.uri().host().empty())

               {

                  contact.uri().host() =Tuple::inet_ntop(source);

                  contact.uri().port() =target.transport->port();

                  if(target.transport->transport() != UDP)

                  {

                    contact.uri().param(p_transport) =Tuple::toData(target.transport->transport());

                  }

               }

            }

         }

         // Fix theReferred-By header if no host specified.

         if(msg->exists(h_ReferredBy))

         {

            if(msg->header(h_ReferredBy).uri().host().empty())

            {

              msg->header(h_ReferredBy).uri().host() = Tuple::inet_ntop(source);

              msg->header(h_ReferredBy).uri().port() = target.transport->port();

            }

         }

         if(msg->exists(h_RecordRoutes) &&!msg->header(h_RecordRoutes).empty())

         {

            NameAddr& rr =msg->header(h_RecordRoutes).back();

            if(rr.uri().host().empty())

            {

               rr.uri().host() =Tuple::inet_ntop(source);

               rr.uri().port() =target.transport->port();

               if(target.transport->transport() != UDP)

               {

                  rr.uri().param(p_transport) =Tuple::toData(target.transport->transport());

               }

            }

         }

         // Seedraft-ietf-sip-identity

         if(mSecurity && msg->exists(h_Identity) &&msg->header(h_Identity).value().empty())

         {

            DateCategory now;

            msg->header(h_Date) = now;

         }

         // poorman's outbound instead of using flow ids

      // assumes the registrar (for a proxyhas stored the connection id

         // (cid)

         staticExtensionParameter p_cid("cid");

         if(msg->isRequest() &&msg->header(h_RequestLine).uri().exists(p_cid))

         {

            unsignedlong cid =msg->header(h_RequestLine).uri().param(p_cid).convertUnsignedLong();

            target.connectionId = cid;

           msg->header(h_RequestLine).uri().remove(p_cid);

         }

         Data& encoded =msg->getEncoded();

         encoded.clear();

         DataStream encodeStream(encoded);

         msg->encode(encodeStream);

         encodeStream.flush();

         target.transport->send(target,encoded, msg->getTransactionId());

      }else{

target);

         mStateMacFifo.add(new TransportFailure(msg->getTransactionId()));

      }

   }

   catch(Transport::Exception& )

   {

      mStateMacFifo.add(new TransportFailure(msg->getTransactionId()));

      return;

   }

}

整个函数大意就是根据Sip RFC的定义判断和设置一下几个重要的Sip Message的头和头属性;如特别重要的VIACONTACTHOST值,因为只有VIACONTACTHOST值正确设置以后才能收发Sip Message正确。我感觉resiprocateDNS的处理部分实在是有点复杂化了,没必要这样。如这部分:

if (!topVia.sentHost().size())

            {

              msg->header(h_Vias).front().sentHost() = Tuple::inet_ntop(source);

            }

            if(!topVia.sentPort())

            {

              msg->header(h_Vias).front().sentPort() = target.transport->port();

            }

我们就这样改写啦:

if (!topVia.sentHost().size())

 {

charszIP[4*4];

         GetIP(szIP);

         msg->header(h_Vias).front().sentHost() = Data(szIP);

  }[2]

下面这段代码就是把Sip MessageTransport一脚踢去:

Data&encoded = msg->getEncoded();

encoded.clear();

DataStreamencodeStream(encoded);

msg->encode(encodeStream);

encodeStream.flush();

target.transport->send(target,encoded, msg->getTransactionId());

提前公开一下;其实这个send的最终归属是:

void

InternalTransport::transmit(const Tuple& dest, constData& pdata, const Data& tid)

{

   SendData* data = newSendData(dest, pdata, tid);

   mTxFifo.add(data);

}

TuSelector模块:

角色如其名。

我思考为什么增加Selector这一层,想起这句前辈使用的话“间接层能解决一切问题”。

TuSelector也可以说是为了TU和Sip Core之间实现OBSER/VISITOR所借助的管理类。

voidregisterTransactionUser(TransactionUser&);这个函数从外到内到了TuSelector才是最后一站。

把一个TransactionUser的列表管理封装成一个选择器是一种进化吗?

另外一个重要的函数应该是:

void

TuSelector::add(Message*msg, TimeLimitFifo::DepthUsage usage)

{

   if(msg->hasTransactionUser())

   {

      if(exists(msg->getTransactionUser()))

      {

         msg->getTransactionUser()->postToTransactionUser(msg,usage);

      }

      else

      {

         deletemsg;

      }

   }

   else

   {

      StatisticsMessage* stats = dynamic_cast(msg);

      if(stats)

      {

         stats->loadOut(mStatsPayload);

        stats->logStats(RESIPROCATE_SUBSYSTEM, mStatsPayload);

         deletemsg;

      }

      else

      {

         mFallBackFifo.add(msg, usage);

      }

   }

}

除了这个真正向TU传递消息的动作msg->getTransactionUser()->postToTransactionUser(msg,usage);

其他的都是基于辅助管理的原则而出现,如StatisticsMessage是SIP方法的一些属性和记录的管理。

Transport系列模块:

从抽象类Transport开始途经节点类InternalTransport再到UdpTransport或者另一支


 Transport -àInternalTransport -àTcpBaseTransport  -àTcpTransport


我们应该明白这是整个协议栈的最底层传输层Transport层的封装了。

对Socket编程有些了解对这部分的代码都不会感到陌生,并且会感觉极其简单简洁。

因此我只是简单介绍一些可以。

Socket mFd; // this is a unix filedescriptor or a windows SOCKET

Fifo mTxFifo; // owned by thetransport

Fifo& mStateMachineFifo; //passed in

加红点标注的两个先进先出队列要详加注意;

看看下面:

classTransactionController中的:

// fifo used to communicate to the transaction state machinewithin the

     // stack. Not for external use by the application. May contain, sip

      // messages(requests and responses), timers (used by state machines),

     // asynchronous dns responses, transport errors from the underlying

      // transports,etc.

      Fifo mStateMacFifo;

classTransportSelector中的:

Fifo& mStateMacFifo;

这三个StateMacFifo其实一脉相承啊,看看这三个类对象的构造产生便能明白,这其中引用传递功不可没;可以这样理解从Transport层接收和从上层TU send下去的Sip Message都放到这儿啦。

Fifo mTxFifo是对最终传输出去数据的管理:

void

InternalTransport::transmit(const Tuple& dest, constData& pdata, const Data& tid)

{

   SendData* data = newSendData(dest, pdata, tid);

   mTxFifo.add(data);

}

Transport::Process最终都对这两个队列进行两个方向的处理:

}

void

UdpTransport::process(FdSet&fdset)

{

  。。。。。。。

      std::auto_ptr sendData =std::auto_ptr(mTxFifo.getNext());

。。。。。。

      constsockaddr& addr = sendData->destination.getSockaddr();

      int count= sendto(mFd,

                        sendData->data.data(), sendData->data.size(), 

                         0, // flags

                         &addr,sendData->destination.length());

     。。。。。。

   if (fdset.readyToRead(mFd) )

   {

      char*buffer = MsgHeaderScanner::allocateBuffer(MaxBufferSize);     

      Tuple tuple(mTuple);

      socklen_t slen = tuple.length();

      int len =recvfrom( mFd,

                          buffer,

                          MaxBufferSize,

                          0 /*flags */,

                         &tuple.getMutableSockaddr(),

                          &slen);

   。。。。。。。

      SipMessage* message = new SipMessage(this);

   。。。。。。。

     mMsgHeaderScanner.prepareForMessage(message);

。。。。

      stampReceived(message);

      mStateMachineFifo.add(message);

}

}

总结

上面列举的这些模块基本是Sip Core的骨干了,详细的细节还是遗漏了很多,如果能看到骨干的话也是这些文字的最终目的了。

还有许多辅助的类和功能模块还是没有提及,实在是没有这么充足的精力和极大的热情啦,期待大家去补充吧;只是还是挂一漏万的提一下。

可以从源码中看到Sip Core封装了SipMessage和Sip Head这样的类;特别是Sip Head和Param的产生使用了大量的宏,这些宏其实不难理解,和MFC曾经使用的宏的方式异曲同工;Head和Method类中使用了一些Hash表;还有它们的Parse及Encode也值得注意和借鉴;这儿非常重要而没有提到的估计应该是

classMsgHeaderScanner了,正是这个类把源源不断从远端收到的一段段Buffer变成了一个个Sip Message,它的有限状态机实在值得一观。这其中还大量使用了模板,还使用了模板偏特化技术,当然并不是最华丽的那种,离LOKI等库还远。

最后以没提到的一段代码结束吧:

class ParameterTypes

{

      。。。。。。       

      staticType getType(const char*start, unsigned intlength);

      typedefParameter* (*Factory)(ParameterTypes::Type, ParseBuffer&, const char*);

      staticFactory ParameterFactories[MAX_PARAMETER];

      staticData ParameterNames[MAX_PARAMETER];

};



[1]也许是事务用户层

[2]感觉复杂度没区别的样子