• 使用信元流(TLVStream)规范、简化模块(C/C++)间交互


    原创  使用信元流(TLVStream)规范、简化模块(C/C++)间交互 收藏

    问题描述:
    在软件开发过程中,一般会对复杂的现实世界进行抽象,并且采用分而治之的策略,将大系统分解为子系统,将子系统分解为一个个的模块。模块间的通信一般采用函数调用的方式,这样会产生一些问题:
       1. 模块间的接口增多会导致模块间紧密耦合,不利于模块的重用、调试、维护。
       2. 接口函数参数会很复杂,容易出现很庞大,功能面面俱到的结构体。
       3. 不利于扩展,新增功能要新增接口函数,或者修改接口参数。


    信元流的定义
    信元流是一种处理数据的方式。它对现实世界的数据进行抽象,形成一个一个的信元,然后由这些信元以一定的方式组合起来,形成信元流,来传递数据。下面信元流的实现。
    信元的实现
    信元由三部分组成,分别是信元标识(Tag),信元长度(Length),信元值(Value),即TLV格式。下面是一个信元的c定义:
    typedef struct __ELEMENT_ST

         {

              ELE_TAG tag;

              ELE_LEN len;

              ELE_VALUE value;

         }ELEMENT_ST;

    信元流的实现:
    信元流分为两部分,信元流头部(head)和体部(body)。head部分用于记录信元流的整体描述,比如,信元流的起始模块,目的模块,以及信元流的消息类型等等。当然,也可以根据需要自己进行扩展。body部分包括信元流中信元的总大小,即ELE_MSG_BODY中body的长度。

            typedef struct __ELEMENT_STREAM_ST
            {
                MOD_ID src_mod;
                MOD_ID des_mod;
                ELE_MSG_TYPE type;
                ELE_MSG_BODY body;
            }ELEMENT_STREAM_ST;
            typedef struct __ELE_MSG_BODY
            {
                ELE_MSG_LEN len;
                char body[MAX_ELE_MSG_BODY_LEN];
            } ELE_MSG_BODY;

    构造信元流
    定义好结构体后,下面定义两个函数,分别向信元体中添加信元和删除信元。
       //pbody信元体的指针。这里没有使用信元流的指针是为了让函数的重用性更好,用户可以自己定义信元流。
            //tag:添加信元的TAG值,len:添加信元的长度。pvale:添加信元的值。
            int AddElementToStreamBody(ELE_MSG_BODY *pbody, ELE_TAG tag, ELE_LEN len, void *pvalue);
            //pbody信元体的指针。 //tag:获取信元的TAG值,buf_len:pbuf的长度。pbuf:目标缓冲区,要把信元VALUE的值写入吃缓冲区。
            int GetElementFromStreamBody(ELE_MSG_BODY *pbody, ELE_TAG tag, int buf_len, void *pbuf);
    信元流的body是一个缓冲区,信元在里面顺序排列。信元在body中顺序不影响它的获取。添加和获取的方法比较简单,不再赘述。


    一个信元流的实例
    下面来举一个具体的例子来介绍一下信元流的使用,以及它的优点。
    假如由两个模块A和B。A模块负责处理业务逻辑,B模块负责处理呼叫控制。A调用B的一个接口发起呼叫,接口如下。
            typedef struct __MAKE_CALL_ST
            {
                char caller_num[MAX_NUM_LEN];//主叫号码
                char called_num[MAX_NUM_LEN];//被叫号码
            }MAKE_CALL_ST;
            int MakeCall(MAKE_CALL_ST *pcall_info);

    后面需求有更改,某些情况下药携带主叫的callid信息。结构体会变成:
        typedef struct __MAKE_CALL_ST
        {
            char caller_num[MAX_NUM_LEN];//主叫号码
            char called_num[MAX_NUM_LEN];//被叫号码
            CALL_ID caller_callid;
        }MAKE_CALL_ST;
    某些情况下又需要携带主叫的SDP信息,结构体会变成:
        typedef struct __MAKE_CALL_ST
        {
            char caller_num[MAX_NUM_LEN];//主叫号码
            char called_num[MAX_NUM_LEN];//被叫号码
            CALL_ID caller_callid;
            SDP_INFO call_sdp;
        }MAKE_CALL_ST;
    随着需求的增加,这个结构体会越来越大,并且,其中的成员在某些情况下要使用,某些情况下又不使用,造成模块间的冗余数据。
    当然,你也可以采用其他的方法,比如,再多定义几个接口和结构体。但是这样的话接口和结构体的数量会呈爆炸式的增长。
    使用信元流可以很好的解决这个问题。把号码,callid,sdp等全部定义成信元,需要的时候塞进去,不需要的话就不添加。另外还有一个好处就是,一个模块可以对外只公布一个接口,来收取信元流,然后在根据信元流的类型进行分别处理。这样,一个模块对外就只有一个接口了,模块间的耦合性会降低。


    一点改进
    上面定义的信元流的格式在实际使用的过程中还是碰到了一些问题,最突出的就是,信元流的大小是固定死的。这种情况下,如果信元信息很小,会导致空间浪费,效率降低;如果信元信息很多,信元流的空间又不够。

    可以对上面的这种方案进行一下优化,把信元的定义更改为:

    typedef struct __ELEMENT_ST

         {

              ELE_TAG tag;

              ELE_LEN len;

              ELE_VALUE value;

              ELEMENT_ST  *pnext_ele;//下一个信元流

         }ELEMENT_ST;

    将信元流的定义更改为:

            typedef struct __ELEMENT_STREAM_ST
            {
                MOD_ID src_mod;
                MOD_ID des_mod;
                ELE_MSG_TYPE type;
                ELEMENT_ST  *pfirst_ele;//第一个信元流
            }ELEMENT_STREAM_ST;

    将信元流和信元更改为动态申请的内存。这样既可以提高效率,有没有了大小的限制。

    需要增加两个接口,来申请和释放信元流。

    唯一不好的地方时,动态申请的内存需要程序员记得释放,否则会内存泄露。不过还有一个方法,即增加一个申请信元流的函数,如下
            ELEMENT_STREAM_ST *GetEleStream()
            {
                static     ELEMENT_STREAM_ST *pstream = NULL;
                if (NULL != pstream)
                {
                    FreeEleStream(pstream);   
                    pstream = NULL;
                }
                pstream = AllocteEleStream();
                return pstream;
            }

    这样的话,通过函数GetEleStream获取的信元流,只在函数范围内有效,退出函数后,立即无效。

  • 相关阅读:
    Python爬虫常用之HtmlParser
    Python异常基础
    python开头——文件声明 详解
    if __name__ == '__main__' 详解
    python基础提高演练(名片管理系统的开发)
    python函数基础
    python中的运算符归类
    python中的if判断语句
    python中变量命名的基本规则,标识符和关键字
    python中变量的基本使用
  • 原文地址:https://www.cnblogs.com/chgaowei/p/1581993.html
Copyright © 2020-2023  润新知