一、 研制背景

当前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就应该设计在一个模块中实现。

三、 设计思路

(一)、功能描述
从功能上来说,信令协议在多媒体通信处理框架中位置如下黄色部分所示:

多媒体通信处理框架中每个部分说明如下:
(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),如下表所示:

业务逻辑层即是H.323协议本身,就是根据当前状态来发消息,或者收消息改变当前状态。

四、 程序实现

(一)、 模块设计
自研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). 应用程序初始化流程

(2). 消息处理引擎主循环流程

(3). 呼叫流程
呼叫流程图及函数说明,假设主叫为A,被叫为B

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命令*/chrOnSendMsgchrCloseH245Connection/*主叫关闭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). 单路被叫测试


(2). 单路主叫测试


(3). 多路被叫测试


六、 下一步计划
(1). 终端的实现
基本思路是:自研H323+DVRRDK视频处理+Gstreamer音频处理,目前正在开发中。
(2). 多路呼叫发生器的实现
基本思路是:自研H323+rtp_receiver+rtp_transmitter,目前已完成多路被叫的实现,可以用来测试MCU的性能;
(3). MCU的实现
基本思路是:自研H323+视频rtp_broadcast+音频rtp_audio_mixer,目前还在设计规划中。

自研H323协议栈设计相关推荐

  1. kali ip查询_UDP/IP硬件协议栈设计(一):缘起

    算算也该准备毕业了,正好毕业设计中需要使用UDP进行PC与FPGA之间的通信,而直接实现UDP硬件协议栈想想也并不复杂,"兵马未动.粮草先行",等完成UDP之后再开始毕设课题的研究 ...

  2. crc32校验算法_UDP/IP硬件协议栈设计(三):校验

    前面已经完成了协议的解析分类,但是不能对分类好的数据帧再进行相关的校验计算,白白浪费时间,为降低延时,所以需要在分类的同时完成校验计算. 校验,就是通过增加冗余位来验证数据完整性的一个操作,对于谁是真 ...

  3. 谈谈我对协议栈设计和架构的理解

    转载于:http://www.52rd.com/bbs/Archive_Thread.asp?SID=54542&TID=2 因为工作的关系,有幸接触到一种不那么复杂的2G通信技术的协议栈(终 ...

  4. LWIP协议栈设计与实现笔记:

    LWIP协议栈设计与实现笔记 一.进程模型:采用何种方法把系统分成不同的进程. TCP/IP协议族的每一个协议作为一个独立的进程存在.此模型必须符合协议的每一层,同时必须指定协议之间的通讯点. 优势: ...

  5. 为何TCP/IP协议栈设计成沙漏型的

    前几天有人回复我的一篇文章问,为何TCP/IP协议栈设计成沙漏型的.这个问题问得好! 我先不谈为何它如此设计,我一个80后根本就没有资格去评论上世纪80年代已经臻于成熟的一个设计,我只是说一下目前的趋 ...

  6. 比开源快30倍的自研SQL Parser设计与实践

    简介: SQL作为一种领域语言,最早用于关系型数据库,方便管理结构化数据:SQL由多种不同的类型的语言组成,包括数据定义语言,数据控制语言.数据操作语言:各数据库产品都有不同的声明和实现:用户可以很方 ...

  7. 诊断(UDS)协议栈设计-总体架构设计

    1 概述 车辆总线协议(VBP)组件是指符合ISO14229-1.ISO15765-3.ISO15765-2等一系列汽车标准协议的集合,并支持OSEK NM.AUTOSAR NM等网络管理协议. 诊断 ...

  8. Netty自娱自乐之协议栈设计

    ---恢复内容开始--- 俺工作已经一年又6个月了,想想过的真快,每天写业务,写业务,写业务.......然后就是祈祷着,这次上线不要出现线上bug.继续这每天无聊的增删改查,学习学习一下自己感兴趣的 ...

  9. 网络协议栈设计与实现:(1) 综述

    l  前言: 大三的上学期学了计算机网络,这门课和编译原理,操作系统,计算机组成原理并称为计算机专业最重要的四门课,也是了解整个现代计算机基础的课程. 就计算机网络来说,我们学来学去也不过就是网络的体 ...

最新文章

  1. 使用 HTML5 时如何改进移动 Web 应用开发
  2. golang中图片转base64_golang base64编码
  3. BAT可真拿抖音一点儿办法也没有
  4. C# webBrowser禁止在新窗口打开,强制在本窗口打开
  5. windows(64位)下使用curl命令
  6. react学习(57)--map赋值
  7. docker pull 下载一半_Docker三个重要的基本操作,镜像,容量,仓库
  8. 揭晓 2020 年增长最快的技术职位,PHP 成为潜力股!
  9. 11.Excel vba开发-根据已有名称,创建新建表格
  10. http-proxy-middleware
  11. 单片机外部晶振-XTAL和EXTAL引脚
  12. 深入浅出了解Unet
  13. win10如何更改桌面字体的大小
  14. 华为麒麟1020流片成功 高通尴尬了 联发科面临出局的危险
  15. ULua与Unity交互原理
  16. vue+vant搭建移动端框架
  17. mysql中set成为中文_mysql中set name gbk
  18. QLable中显示圆形边框
  19. AI面试必备/深度学习100问1-50题答案解析
  20. 程序员辞职回老家山洞写代码,二年敲了45万行

热门文章

  1. 如何用示波器测量串口
  2. matlab simulink博客,MATLAB Simulink仿真数据记录
  3. jython 简单入门
  4. bzoj2150 部落战争
  5. 笔记本拆c面_【神舟战神笔记本K680C使用总结】C面|做工|插槽|温度|指纹_摘要频道_什么值得买...
  6. 计算机英语课的总结,英语优质课心得体会
  7. 南开大学计算机考研资料汇总
  8. postgre数据库 例如oracle的nvl()函数
  9. 屌爆了的两个在线编辑网站runjs和jsbin
  10. 数据结构(四)图 —— 编程作业 07 :公路村村通