自研H323协议栈设计

一、 研制背景

当前MCU、终端所用的协议栈为开源H323Plus(原OpenH323)以及Opal(Open Phone Abstract Layer),这两种协议栈有以下弊端:
1. 协议栈代码较庞大,70万行代码,很难在短时间内全部了解清楚;
2. 协议栈基于面向对象的技术设计实现,如C++的模板、设计模式在其中,反而对协议理解产生障碍,且调试起来也较复杂;
3. 协议栈大量使用动态内存分配,在使用过程中,由于多线程析构不当或是插件调用不当,经常出现线程死锁、空指针异常、内存泄漏三类现象;
4. 协议栈中信令处理、媒体处理(包含媒体采集与显示回放)、业务逻辑绑定非常紧,无法实现信令媒体的完全解耦。
体现在产品中,问题如下:
1. MCU在H.225正常链接挂断时,H.245的TCP连接仍处于半连接状态,资源没有回收,增加了程序出错的风险;
2. MCU偶尔会出现断会、切换卡顿、结束会议慢三种异常现象,初步分析与线程死锁、空指针异常、内存泄漏有关;
3. 终端的协议栈应用程序(CallCore)发生过无故退出,且该进程所绑定的媒体处理(包含采集编码发送与接收解码显示)一旦发生异常,协议栈应用程序会死锁成为僵尸进程,错误复现率低,很难排查出故障。
为了解决以上问题,准备自己模仿编写H323协议栈。

二、 研制需求

为了规避之前开源协议栈的各种问题,自研H323协议栈在设计上需求如下:
1. 协议栈代码预估在10000行左右,除了引用ITU-T的定义类文件及asn1的parser程序外,代码大部分是学习asterisk的ooh323实现,代码照抄的部分经过剪裁,且最后使用的代码每行每句都进行理解与消化,并在函数名后注释函数流程;
2. 协议栈用C语言编写,不再基于面向对象技术,信令过程本质上是握手协商交互过程,面向过程即可满足需求;C语言实现的另一个好处是可以大概估算出内存占用大小,从而在程序初始化时就把内存分配好,C++由于其编译模型在不同平台上(Windows、Linux)的差异,较难估算内存占用情况(如虚函数表的内存占用);
3. 协议栈实现尽量用静态变量、栈内存或是其他事先分配好内存,少用动态分配的堆内存,避免分配释放带来的程序异常;
4. 协议栈实现上仅实现信令,并给媒体处理、外围控制提供回调接口,其中媒体处理回调函数的内部实现建议用父子进程的IPC方式,从而实现信令、媒体处理的完全解耦;外围控制回调函数的内部实现建议用socket的IPC方式,从而实现信令、业务逻辑的完全解耦。代码编写上尽量避免循环依赖,A模块被B模块调用,B模块被C模块调用,C模块则不能被A再调用,否则ABC就应该设计在一个模块中实现。

三、 设计思路

(一)、功能描述
从功能上来说,信令协议在多媒体通信处理框架中位置如下黄色部分所示:
自研H323协议栈设计_第1张图片

多媒体通信处理框架中每个部分说明如下:
(1). Socket,多媒体通信都是基于IP的TCP或UDP协议实现,所以最后都是通过Socket完成收发;
(2). Encrypt/Unencrypt,对信令的加密,如H.235、TLS;
(3). Encrypt/Unencrypt,对媒体的加密,如SRTP、ZRTP;
(4). Pack/Unpack,对信令的传输适配格式化,如ASN.1、XML;
(5). Pack/Unpack,对媒体的打包封装,如RTP\RTCP 、RFC3984、RFC7798;
(6). Signal Protocol,信令协议,如H.323、SIP;
(7). Media Codec,媒体编码协议,如H.264、H.265;
(8). Call Session,对呼叫链路、逻辑信道、媒体能力等抽象数据的封装;
(9). Call Application,基于呼叫的多媒体应用程序。
自研协议栈目前仅实现1\4\6\8部分。
(二)、设计想法
信令协议栈本质上是一个网络程序,从网络程序设计角度考虑分为网络层和业务逻辑层。
网络层是由2个tcp listen socket以及多个读写tcp socket组成(目前暂没考虑RAS),如下表所示:
自研H323协议栈设计_第2张图片
业务逻辑层即是H.323协议本身,就是根据当前状态来发消息,或者收消息改变当前状态。
自研H323协议栈设计_第3张图片

四、 程序实现

(一)、 模块设计
自研H323协议栈按照c语言.h/.c的同名称文件为一个模块,v0.1版本模块划分如下:
(1).ASN模块,实现ASN1的格式转译;
(2).Trace模块,实现基本的日志功能;
(3).Type模块,定义基本枚举类型,如主从确定、能力交换、挂断原因、呼叫模式,抽象定义呼叫选项结构体,抽象定义并实现定时器结构体以及定时器相关操作;
(4).Socket模块,跨平台封装Windows和Linux在Socket的的API;
(5).Capability模块,抽象定义能力、优先级、能力参数(Generic\H264)、H323端点能力集,接收通道发送通道的回调函数,并实现能力集添加/删除/合并/检验等功能;
(6).LogicalChannel模块,抽象定义逻辑通道结构体,并实现逻辑通道的增加/删除/查找/清除等功能;
(7).Channels模块,实现H225、H245以及消息通道Channel在Socket层的功能,实现了消息处理引擎,且全局定时器列表也在消息处理引擎维护;
(8).CallSession模块,定义并实现呼叫会话的全部相关信息,具体包括抽象定义呼叫状态、信道状态(H245)、媒体信息、呼叫通道、呼叫参数、会话的回调接口,并实现呼叫在创建/结束/清除过程中会话参数的设置、会话的Alias/e164/h323id的设置、会话的能力集的添加等功能;
(9).H323Endpoint模块,定义并实现一个H323端点的相关信息,具体包括抽象定义端点的数据结构,实现了H323端点初始化、销毁、端口范围设置、日志级别设置、端点的Alias/e164/h323id的设置、H225回调函数、H323回调函数、端点的呼叫能力集添加等功能,端点的作用域要大于会话的作用域,即端点的Alias/e164/h323id和能力集会包含会话的Alias/e164/h323id和能力集;
(10).H323Protocol模块,定义并实现RAS、Q931、H245的消息结构体、H225与H245消息处理回调接口,并实现所有H225、H245呼叫流程的协议交互;
(11).StackCmds模块,定义并实现外围控制消息结构体,实现外部消息的封装以及与消息处理引擎的交互。
(二)、 主要结构体设计
(1). CHRH323EndPoint
一个进程对应一个H323Endpoint实体,该实体可以表征MCU、终端、网守等等,结构体设计如下:

OOCTXT ctxt; /**内存上下文 */
OOCTXT msgctxt; /**消息相关的内存上下文*/
CHRH323Ports tcpPorts; /**tcp端口范围*/
CHRH323Ports udpPorts; /**udp端口范围*/
CHRH323Ports rtpPorts; /**rtp端口范围*/
int termType; /* 实体类型,50 终端,60 GK,70 带MC的终端, 120 MCU*/
int callType; /**呼叫类型 incoming/outgoing*/
struct chrH323EpCapability *myCaps; /**能力集*/
CHRH225MsgCallbacks h225Callbacks; /**H225回调函数*/
CHRH323CALLBACKS h323Callbacks;  /** H323回调函数*/
CHRSOCKET *listener; /** 1720 */
CHRH323CallData *callList; /**呼叫列表*/
CHRCallMode callMode; /**呼叫模式audio/audiorx/audiotx/video */
ASN1UINT callEstablishmentTimeout; /**呼叫建立定时器超时时间*/
ASN1UINT msdTimeout; /**主从确定定时器超时时间*/
ASN1UINT tcsTimeout; /**能力交换定时器超时时间*/
ASN1UINT logicalChannelTimeout; /**逻辑通道建立超时时间*/
ASN1UINT sessionTimeout; /**会话定时器超时时间*/
struct chrGkClient *gkClient; /**gk相关参数*/
CHRSOCKET cmdListener; /**命令字监听socket*/
CHRSOCKET cmdSock; /**新命令字连接socket*/
int cmdPort; /** 命令字监听端口 */
enum Q931InformationTransferCapability bearercap;/**Q931能力集*/

(2). CHRH323CallData
一个呼叫对应一个呼叫参数,一个进程可以有多个呼叫,由结构体本身的双向链表来增删遍历,结构体设计如下:

    OOCTXT               *pctxt; /** 上下文指针*/
    char                 callToken[20]; /** token: ooh323c_call_1 */
    char                 callType[10]; /** 呼叫类型incoming/outgoing */
    CHRCallMode           callMode; /** 呼叫模式audio/ video*/
    ASN1USINT            callReference; /**呼叫参考*/
    char                 ourCallerId[256]; /**本地Id*/
    char                 localIP[20];/** 本地IP */
    char                 *remoteDisplayName; /**远端名称*/
    struct CHRAliases     *remoteAliases; /**远端别名*/
    struct CHRAliases     *ourAliases; /**本端别名*/
    H225CallIdentifier   callIdentifier;/**H225呼叫id */
    char                 *callingPartyNumber; /**主叫名称*/
    char                 *calledPartyNumber; /**被叫名称*/
    H225ConferenceIdentifier confIdentifier; /**H.323 conf Id */
    CHRCallState          callState; /**呼叫状态*/
    CHRCallClearReason    callEndReason; /**失败原因*/
    CHRH323Channel*       pH225Channel; /**H225通道指针*/
    unsigned             h245ConnectionAttempts; /**H245交互次数*/
    CHRH245SessionState   h245SessionState; /**H245会话状态*/
    CHRMediaInfo          *mediaInfo; /**媒体类型*/
    CHRH323Channel*       pH245Channel; /**H245通道指针*/
    CHRSOCKET             *h245listener; /**H245监听socket*/
    int                  *h245listenport; /**H245监听端口*/
    char                 remoteIP[20];/**远端IP */
    int                  remotePort; /**远端端口 */
    int                  remoteH245Port; /**远端H245端口 */
    CHRMasterSlaveState   masterSlaveState;   /**主从确定状态 */
    ASN1UINT             statusDeterminationNumber; /**主从确定次数 */
    CHRCapExchangeState   localTermCapState; /**本地能力交换状态 */
    CHRCapExchangeState   remoteTermCapState; /**远端能力交换状态 */
    struct chrH323EpCapability* ourCaps; /**本端能力 */
    struct chrH323EpCapability* remoteCaps;  /**远端能力*/
    struct chrH323EpCapability* jointCaps; /**合并后能力 */
    ASN1UINT8            remoteTermCapSeqNo; /**远端能力序号*/
    ASN1UINT8            localTermCapSeqNo; /**本端能力序号*/
    CHRCapPrefs           capPrefs; /**能力优先级*/
    CHRLogicalChannel*    logicalChans; /**逻辑信道*/
    int                  noOfLogicalChannels; /**逻辑信道号码*/
    unsigned             nextSessionID; /* session id 1 audio,2 video,3 data */
    DList                timerList; /**定时器列表*/
    ASN1UINT             msdRetries;/**主从确定重试次数*/
    void                 *usrData; /**用户可传递的自定义数据*/
    struct CHRH323CallData* next; /**下一个呼叫*/
    struct CHRH323CallData* prev; /**上一个呼叫*/

(3). CHRH323Channel
一个H225或H245的信令连接封装成了一个H323Channel

    CHRSOCKET sock;      /**Channel对应的sock*/
    int      port;      /**Channel对应的端口*/
    DList    outQueue;  /**待发送的消息的消息队列*/

(4). CHRLogicalChannel
一个媒体通道封装成一个LogicalChannel,一个呼叫一般有4个LogicalChannel,分别是audio transmit,audio receiver,video transmit,video receiver,自身可组成单链表便于遍历

    int  channelNo; /**通道序号*/
    int  sessionID; /** session id 1 audio,2 video,3 data */
    enum CHRCapType type; /**能力类型*/
    char dir[10];  /* receive/transmit */
    char remoteIP[20]; /**远端IP*/
    int  remoteMediaPort; /**远端rtp端口号*/
    int  remoteMediaControlPort; /**远端rtcp端口号*/
    int  localRtpPort; /**本地rtp端口号*/
    int  localRtcpPort; /**本地rtcp端口号*/
    char localIP[20]; /**本地IP*/
    CHRLogicalChannelState state; /**逻辑通道状态*/
    struct chrH323EpCapability *chanCap; /**实体能力*/
    struct CHRLogicalChannel *next; /**下一个通道*/

(5). CHRMediaInfo
每个媒体信息可以封装成一个MediaInfo,自身可组成单链表便于遍历

    char   dir[15]; /** 媒体传输方向transmit/receive*/
    int   cap; /** 能力编号*/
    int   lMediaPort;  /** rtp端口*/
    int   lMediaCtrlPort; /** rtcp端口*/
    char  lMediaIP[20]; /** 媒体流IP*/
    struct CHRMediaInfo *next;

(6). CHRCapParams/CHRH264CapParams/ CHRGenericCapParams
能力集描述

    int txframes;  /**transmission每一包的帧数 */
    int rxframes;  /** reception每一包的帧数 */
    OOBOOL silenceSuppression; /**静音检测*/
    unsigned maxBitRate; /**最大比特率,单位为100 bits/sec */
    unsigned profile; /**H264参数 档次*/
    unsigned constaint; /**H264参数 xx-yy-zz中的yy*/
    unsigned level; /**H264参数 级别*/
    unsigned char send_pt; /**H264参数 发送payloadtype*/
    unsigned char recv_pt; /**H264参数 接收payloadtype*/

(三)、 流程设计
(1). 应用程序初始化流程
自研H323协议栈设计_第4张图片
(2). 消息处理引擎主循环流程
自研H323协议栈设计_第5张图片
(3). 呼叫流程
呼叫流程图及函数说明,假设主叫为A,被叫为B
自研H323协议栈设计_第6张图片

1.setup
A:chrH323MakeCall_helper
/**设置setup的呼叫参数,包括Alias、IP、呼叫模式*/
chrSendH225MsgtoQueue
/**将setup消息发送至pH225Channel的消息队列*/
B:chrOnReceivedSetup
/*从Q931 UUIE中获取远端Alias,远端IP,呼叫模式(fast start 或 tunneling)*/
h225Callbacks.onReceivedSetup
/**回调h225收到setup函数*/
2. callProceeding
B: chrSendCallProceeding/*发送callProceeding消息*/
A: chrOnReceivedCallProceeding/*从Q931 UUIE中获取H245的IP和端口*/
3.alerting connect
B: chrSendAlerting /**发送Altering消息*/
chrSendConnect/**发送Altering消息*/
chrAcceptCall/**被叫端接收呼叫*/
h225Callbacks.onBuiltConnect/**回调h225connect函数*/
A: chrOnReceivedAlerting
/*从Q931 UUIE中获取振铃信息,其实没什么有用的信息*/
/**回调h323振铃函数*/
chrOnReceivedSignalConnect
/*从Q931 UUIE中获取connect信息,创建H245连接*/
h323Callbacks.onAlerting
chrCreateH245Connection/* 接下来调用chrSendTermCapMsg */
h225Callbacks.onReceivedConnect
h323Callbacks.onCallEstablished
/**回调h225收到connect与h323呼叫建立函数*/

4. TCS
A: chrCreateH245Connection /*主叫创建H245连接*/
chrSendTermCapMsg/*主叫发送TCS*/
B: chrOnReceivedTerminalCapabilitySet/*被叫接收TCS*/
5. MSD
A: chrCreateH245Connection/*主叫创建H245连接,此处和4执行一次*/
chrSendMasterSlaveDetermination/*主叫发送MSD*/
B: chrHandleMasterSlave(CHRMasterSlaveDetermination)/*被叫接收MSD*/
6. TCS
B: chrAcceptH245Connection /*被叫接收H245连接*/
chrSendTermCapMsg/*被叫发送TCS*/
A: chrOnReceivedTerminalCapabilitySet/*主叫接收TCS*/
7. TCSAck
A: chrH245AcknowledgeTerminalCapabilitySet/*主叫发送TCSAck*/
chrSendH245Msg
B: chrOnReceivedTerminalCapabilitySetAck/*被叫接收TCSAck*/
8. MSD TCSAck
B: chrSendMasterSlaveDetermination/*被叫发送MSD*/
chrH245AcknowledgeTerminalCapabilitySet/*被叫发送TCSAck*/
chrSendH245Msg
A: chrOnReceivedTerminalCapabilitySetAck/*主叫接收TCSAck*/
chrHandleMasterSlave/*主叫接收MSD*/
9. MSDAck
A: chrSendMasterSlaveDeterminationAck/*主叫发送MSDAck*/
B: chrHandleMasterSlave (CHRMasterSlaveAck) /*被叫接收MSDAck*/
10. OLC(g711u) OLC(h264)
B: chrOpenLogicalChannel /*被叫发送OLC*/
A: chrHandleOpenLogicalChannel /*主叫接收OLC*/
11. OLC(g711u) OLC(h264)
A: chrOpenLogicalChannel /*主叫发送OLC*/
B: chrHandleOpenLogicalChannel /*被叫接收OLC*/
12. OLCAck
B: chrHandleOpenLogicalChannel_helper/*被叫处理OLC*/
   chrAddNewLogicalChannel/*音频逻辑通道添加*/
   chrSendH245Msg/*被叫发送OLCAck*/
   startReceiveChannel/**音频A->B接收回调函数*/
A: chrOnReceivedOpenLogicalChannelAck/*主叫接收OLCAck*/
startTransmitChannel/**音频A->B发送回调函数*/
13. OLCAck OLCAck
A: chrHandleOpenLogicalChannel_helper/*主叫处理OLC*/
   chrAddNewLogicalChannel/*音频逻辑通道添加*/
   chrSendH245Msg /*主叫发送OLCAck*/
   startReceiveChannel/**音频B->A接收回调函数*/
   chrHandleOpenLogicalChannel_helper/*主叫处理OLC*/
   chrAddNewLogicalChannel/*视频逻辑通道添加*/
   chrSendH245Msg /*主叫发送OLCAck*/
   startReceiveChannel/**视频B->A接收回调函数*/
B: chrOnReceivedOpenLogicalChannelAck/*被叫接收OLCAck*/
 startTransmitChannel/**音频B->A发送回调函数*/
   chrOnReceivedOpenLogicalChannelAck/*被叫接收OLCAck*/
   startTransmitChannel/**视频B->A发送回调函数*/
14. OLCAck
B: chrHandleOpenLogicalChannel_helper/*被叫处理OLC*/
   chrAddNewLogicalChannel /*视频逻辑通道添加*/
   chrSendH245Msg /*被叫发送OLCAck*/
   startReceiveChannel/**视频A->B接收回调函数*/
A: chrOnReceivedOpenLogicalChannelAck/*主叫接收OLCAck*/
   startTransmitChannel/**视频A->B发送回调函数*/
15. RTP
四个回调函数,可以通过调用四个线程、进程、RPC来实现,从而实现了信令、媒体的完全解耦
chrAudioStartReceiveChannel
chrAudioStartTransmitChannel
chrVideoStartReceiveChannel
chrVideoStartTransmitChannel
16. ESC
应用层触发挂断
A: chrEndCall
chrCleanCall
chrCloseH245Listener/*主叫准备关闭H245监听*/
chrCloseH225Connection/*主叫准备关闭H225连接*/
17. releaseComplete
A: chrSendReleaseComplete/*主叫发送H225releasecomplete消息*/
   chrSendEndSessionCommand/*主叫发送H245endsession消息*/
   chrSendH245Msg /*目前此处为了避免半连接,h245的tcp直接断了,所以抓包看不到endsession这条h245命令*/
   chrOnSendMsg
   chrCloseH245Connection/*主叫关闭H245连接*/
   chrSocketClose/*主叫关闭socket*/
B: chrOnReceivedReleaseComplete/*被叫接收H225releasecomplete消息*/
   chrHandleH245Command/*被叫接收H245endsession消息*/
   chrCloseH245Connection/*被叫关闭H245连接*/
   chrSocketClose/*被叫关闭socket*/

(四)、部分技术细节
(1). 定时器实现
定时器关注的是:
(1). 定时器是否注册,或者说是否启动;
(2). 超时时间是多少;
(3). 到时间了要干什么事情。
结构体定义如下

typedef int(*CHRTimerCbFunc)(void *data);
typedef struct _CHRTimer {
    struct timeval expireTime, timeout; /* 绝对时间和相对时间 */
    void*        cbData;
    OOBOOL       reRegister;
    CHRTimerCbFunc timeoutCB;
} CHRTimer;

协议栈需要维护一个定时器列表(应用程序也可以维护一个),每个定时器在创建后需要添加到列表中,需要创建、删除、超时检查、时间比对、定时操作、定时器重置等函数实现,详见代码chrType.h。目前协议栈每个呼叫有7个定时器,分别是:

CHR_CALLESTB_TIMER 呼叫建立定时器,默认60s
CHR_MSD_TIMER 主从确定定时器,默认30s
CHR_TCS_TIMER  能力交换定时器,默认30s
CHR_OLC_TIMER 打开逻辑通道定时器,默认30s
CHR_CLC_TIMER 关闭逻辑通道定时器,默认30s
CHR_RCC_TIMER 请求关闭通道定时器,默认30s
CHR_SESSION_TIMER 会话超时定时器,默认15s
CHR_H245CONNECT_TIMER H245连接定时器,默认2s

(2). 状态机的实现
由于信令协议无论对于主叫端还是被叫端都是一个状态扭转的过程,故通过简单的状态机来实现,其中外部触发来源于chrCallSession.c的API,设计如下:

1. CHRH323CallData* chrCreateCall(char *type, char *callToken, void *usrData);/*创建呼叫*/
2. int chrEndCall(CHRH323CallData *call); /*挂断呼叫*/
3. int chrCleanCall(CHRH323CallData *call); /*清除呼叫,正常挂断和异常挂断都需要进该函数*/

内部状态包括呼叫状态与H.245状态,设计如下:

typedef enum 
{
    CHR_CALL_CREATED,               /*1.呼叫创建 */
    CHR_CALL_WAITING_ADMISSION,   /*2. 呼叫认证等待,GK启用时才有用*/
    CHR_CALL_CONNECTING,           /*3. 呼叫连接中 */
    CHR_CALL_CONNECTED,            /*4. 呼叫已连接 */
    CHR_CALL_PAUSED,                 /*5. 呼叫暂停 一般不会用hold on*/
    CHR_CALL_CLEAR,                  /*6. 呼叫清除 */
    CHR_CALL_CLEAR_RELEASERECVD, /*7. 呼叫清除且收到了release命令 */
    CHR_CALL_CLEAR_RELEASESENT,   /*8. 呼叫清除且发送了release命令 */
    CHR_CALL_CLEARED                /*9. 呼叫已清除 */
} CHRCallState;
typedef enum 
{
    CHR_H245SESSION_IDLE, /*1. 空闲*/
    CHR_H245SESSION_PAUSED, /*2. 暂停*/
    CHR_H245SESSION_ACTIVE, /*3. 活跃*/
    CHR_H245SESSION_ENDSENT,  /*4. 结束且已发送*/
    CHR_H245SESSION_ENDRECVD, /*5. 结束且已接收*/
    CHR_H245SESSION_CLOSED /*6. 关闭*/
} CHRH245SessionState;

协议栈状态扭转实现发送消息前和接收消息后几个Switch Case结合定时器与状态变更共同来实现的,函数设计如下:

1. chrHandleH2250Message /*收到H225消息后的分拣,再进入OnReceiveXX的处理函数中,之后回调应用程序注册函数*/
    收到SetupMsg: 
        chrOnReceivedSetup ;
        h225Callbacks.onReceivedSetup ;
        chrSendCallProceeding ;
        chrH323CallAdmitted ;
    收到CallProceedingMsg: 
        chrOnReceivedCallProceeding ;
    收到AlertingMsg:
        chrOnReceivedAlerting ;
        h323Callbacks.onAlerting 
    收到ConnectMsg:
        chrOnReceivedSignalConnect ;
        h323Callbacks.onReceivedConnect ;
        h323Callbacks.onCallEstablished ;
    收到InformationMsg: 暂无处理
    收到ReleaseCompleteMsg: 暂无处理
    收到ProgressMsg: 暂无处理
    收到StatusMsg: 暂无处理
    收到StatusEnquiryMsg: 暂无处理
    收到SetupAckMsg: 暂无处理
    收到NotifyMsg: 暂无处理
2. chrHandleH245Message/*收到H245消息后的分拣*/
(1). 收到请求消息 :
        收到TCS:
        状态变化:CHR_H245SESSION_IDLE -> CHR_H245SESSION_ACTIVE;
            chrOnReceivedTerminalCapabilitySet ;
            chrSendTermCapMsg ;
        收到MSD:
            chrHandleMasterSlave ;
        收到OLC:
            chrHandleOpenLogicalChannel ;
        收到CLC:
            chrOnReceivedCloseLogicalChannel ;
        收到requestChannelClose:
            chrOnReceivedRequestChannelClose ;
        收到roundTripDelayRequest:
            chrOnReceivedRoundTripDelayRequest ;
    (2). 收到响应消息 :
        收到MSDAck:
/*删除 CHR_MSD_TIMER 定时器*/
            chrHandleMasterSlave ;
        收到MSDreject:
/*删除 CHR_MSD_TIMER 定时器*/
            chrHandleMasterSlaveReject ;
        收到TCSAck:
            /*删除 CHR_TCS_TIMER 定时器*/
            chrOnReceivedTerminalCapabilitySetAck ;
        收到TCSReject:
            /*删除 CHR_TCS_TIMER 定时器*/
        收到OLCAck:           
            /*删除 CHR_OLC_TIMER 定时器*/
            chrOnReceivedOpenLogicalChannelAck ;
        收到OLCReject:
            /*删除 CHR_OLC_TIMER 定时器*/
            chrOnReceivedOpenLogicalChannelRejected ;
        收到CLCAck:
            /*删除 CHR_CLC_TIMER 定时器*/
            chrOnReceivedCloseChannelAck ;
        收到requestChannelCloseAck:
            /*删除 CHR_RCC_TIMER 定时器*/
chrOnReceivedRequestChannelCloseAck ;
        收到requestChannelCloseReject:
            /*删除 CHR_RCC_TIMER 定时器*/
            chrOnReceivedRequestChannelCloseReject ;
    (3). 收到命令消息 :
        chrHandleH245Command(call, command);
    (4). 收到指令消息 :
        暂无处理
3. chrOnSendMsg
    发送Setup:
        /*启动 CHR_CALLESTB_TIMER 定时器;
        h323Callbacks.onOutgoingCall;
    发送CallProceeding:
        /*无操作
    发送Alert:
        h323Callbacks.onAlerting;
    发送Connect:
        h323Callbacks.onCallEstablished;
    发送ReleaseComplete:
        /*状态变化 如果当前呼叫是CHR_CALL_CLEAR_RELEASERECVD则转成CHR_CALL_CLEARED,否则是CHR_CALL_CLEAR_RELEASESENT;
        /*如果当前呼叫是CHR_CALL_CLEAR_RELEASESENT 且H245是CHR_H245SESSION_IDLE状态,则创建CHR_SESSION_TIMER 定时器;
        /*如果当前H245是CHR_H245SESSION_CLOSED状态则呼叫状态为CHR_CALL_CLEARED;
    发送Facility:
        /*无操作*/
    发送MasterSlaveDetermination:
        /*启动 CHR_MSD_TIMER 定时器; */
    发送MasterSlaveAck:
        /*无操作*/
    发送MasterSlaveReject:
        /*无操作*/
    发送MasterSlaveRelease:
        /*无操作*/
    发送TerminalCapabilitySet:
        /*如果H245是CHR_H245SESSION_IDLE 或CHR_H245SESSION_PAUSED则转成CHR_H245SESSION_ACTIVE; */
        /*启动 CHR_TCS_TIMER 定时器; */
    发送TerminalCapabilitySetAck:
        /*无操作*/
    发送TerminalCapabilitySetReject:
        /*无操作*/
    发送OpenLogicalChannel:
        /*启动 CHR_OLC_TIMER 定时器; */
    发送OpenLogicalChannelAck:
        /*无操作*/
    发送OpenLogicalChannelReject:
        /*无操作*/
    发送EndSessionCommand:
        /*CHR_H245SESSION_ACTIVE转成CHR_H245SESSION_ENDSENT; */
        /*启动 CHR_SESSION_TIMER 定时器; */
    发送CloseLogicalChannel:
        /*启动 CHR_SESSION_TIMER 定时器; */
    发送CloseLogicalChannelAck:
        /*无操作*/
    发送RequestChannelClose:
        /*启动 CHR_RCC_TIMER 定时器; */
    发送RequestChannelCloseAck:
        /*无操作*/

(3). Socket跨平台封装
首先将Windows的socket定义规范成posix,再显式载入dll为函数指针赋值;

select:
typedef int (WSAAPI * LPFN_SELECT)(
    _In_ int nfds,
    _Inout_opt_ fd_set FAR * readfds,
    _Inout_opt_ fd_set FAR * writefds,
    _Inout_opt_ fd_set FAR *exceptfds,
    _In_opt_ const struct timeval FAR * timeout
);
static LPFN_SELECT select;
ws32 = LoadLibrary("WS2_32.DLL");
select = (LPFN_SELECT)GetProcAddress(ws32, "select");

其次,将一些简单的socket原子操作封装成一个函数,并做好错误检查

例chrSocketCreate()
socket(AF_INET, SOCK_STREAM, 0);
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof (on));
setsockopt(sock, SOL_SOCKET, SO_LINGER, (const char*)&linger, sizeof (linger));

五、 程序实例与实验结果
(1). 单路被叫测试
自研H323协议栈设计_第7张图片
被叫端口信息
(2). 单路主叫测试
自研H323协议栈设计_第8张图片
主叫端口信息
(3). 多路被叫测试
自研H323协议栈设计_第9张图片
自研H323协议栈设计_第10张图片
六、 下一步计划
(1). 终端的实现
基本思路是:自研H323+DVRRDK视频处理+Gstreamer音频处理,目前正在开发中。
(2). 多路呼叫发生器的实现
基本思路是:自研H323+rtp_receiver+rtp_transmitter,目前已完成多路被叫的实现,可以用来测试MCU的性能;
(3). MCU的实现
基本思路是:自研H323+视频rtp_broadcast+音频rtp_audio_mixer,目前还在设计规划中。

你可能感兴趣的:(通信信令)