前营中间件(3) - 组件的开发

下面,我们通过一个简单的例子来介绍此系统的使用.

我们实现一个简单功能,在前台输入一个整数,后台对此进行平方操作后返回.下面我们先从服务端的组件开发进行介绍,同时会介绍相应的配置的修改和系统的测试.

组件开发:

一个组件需要开发成什么样子?我们首先想象一样,如果我们想要提供一个功能,需要知道什么?我们首先要知道给什么请求提供?怎么处理这个请求?处理这个请求是否还需要什么样的资源?我们把这几个问题解决掉了,还需要把这些信息告诉中间件,让中间件能够正确根据请求包的信息,正确处理请求.下面我们就来做此组件,最好结合”QYFuncDef.h”头文件一起来看下面的流程:

,我们以功能号来区别不同的请求,因为我们需要先为我们的功能分配一个功能号100(功能号需要大于等于100,不能重复);

,分配好功能号,我们还要写一个函数,这个函数计算传入数的平方,这时问题出来了,我们如何取得前台传入的数据呢?又如何把结果传给中间件,以便于中间件把结果返回给前台,而且中间件提供我们的函数,这个函数的格式是什么?如果此函数格式没有进行约定,那么中间件是无法调用此函数的,是的,我们的中间件提供了回调函数的格式声明,而且支持多种不同的回调函数格式,我们看一下”QYFuncDef.h”文件中的FUNC_TYPE结构

enum FUNC_TYPE

{

    PT_NONE              = 0,       // 无意义,目前没有使用

    PT_BUFF              = 1,       // 缓冲区模式,裸数据处理

    PT_BUFF_PARAM     = 2,       // 缓冲区模式,带有传入参数,此参数在OnPrepare时传入

    PT_PACK              = 3,       // 打包模式,打包器需要在配置中进行配置

    PT_PACK_PARAM     = 4,       // 打包模式,带有参数传入

    PT_BUFF_DB        = 5,       // 缓冲区模式,带有数据源句柄的传,数据源在配置中配置,使用的数据源名称由qy_func_info结构传入

    PT_BUFF_PARAM_DB  = 6,       // 缓冲区模式,带有参数传入和数据源句柄

    PT_PACK_DB        = 7,       // 打包模式,和数据源句柄

    PT_PACK_PARAM_DB  = 8,       // 打包模式,带有参数传入和数据源句柄

};

此结构中,是目前所支持的函数类型表示,它们分别对区于此头文件上的八个函数类型声明,由于我们只是进行平方的计算,所以不需要数据库,不需要任何资源,也不需要打包器,只用最简单的函数类型PT_BUFF,就可以了,我们看一下,它对应的函数类型定义:

typedef int (__stdcall *LPONRECVBUFF)(LPCONTEXTAPI lpContextAPI, unsigned int uiSessionID, unsigned int uiFuncNo, unsigned short usRecvBuffHandle, unsigned short usSendBuffHandle);

它有五个参数, lpContextAPI参数提供了许多函数操作,它的声明可以参数头文件ZContextAPI.h,我们可以看到,它提供了许多函数,此处我们先介绍两个函数:

virtual int DetachBuffer(unsigned short usBufferHandle, void* lpBuffer, unsigned long& ulBuffSize) = 0;

virtual int AttachBuffer(unsigned short usBufferHandle, void* lpBuffer, unsigned long ulBuffSize) = 0;

DetachBuffer函数是把数据从缓冲区中取出来, AttachBuffer函数是把数据写入缓冲区.此处,我们先讲一个我们系统对消息内存的管理模式.

在我们系统中,我们把中间件保存消息所用到的内存进行统一管理,每块内存保存一个消息,每块内存都有一个unsigned short数字作为每块内存数据的唯一标志,而把与此内存相关的操作都都进行了封装,以防止人为的损坏消息数据,DetachBufferAttachBuffer操作就是分别从缓冲区中取数据和把数据写入缓冲区,他们的第一个参数usBufferHandle就是缓冲区的标识.

现在我们也知道了回调函数,第四,五个参数的意思了, usRecvBuffHandle是传入缓冲区的标识, usSendBuffHandle是输出缓冲区的标识,uiSessionID是会话号,我们一般用不到, uiFuncNo是功能号.

下面,我们就可以写出功能处理函数

int __stdcall OnRecvDeal(LPCONTEXTAPI lpContextAPI, unsigned int uiSessionID, unsigned int uiFuncNo, unsigned short usRecvBuffHandle, unsigned short usSendBuffHandle)

{

    int i(0);

    unsigned long ulBuffSize(0);

    ulBuffSize = sizeof(int);

    lpContextAPI->DetachBuffer(usRecvBuffHandle, &i, ulBuffSize);

    i = i * i;

    lpContextAPI->AttachBuffer(usSendBuffHandle, &i, sizeof(int));

    return 0;

}

,我们已经写好了业务处理功能函数,那么就需要把此函数告诉中间件,在此之前,我们先介绍一个合格组件的导出函数,如果想要中间件正确加载我们所写的组件,我们需要在组件中提供三个导出函数,三个导出函数的名称和定义都在QYFuncDef.h.

/**

 * 函数名称:取接口信息

 * 函数功能:从DLL中取接口信息,在DLL实现一个与此函数声明一致的函数,循环调用。

 * 参数列表:

 *         @index        :   序号,上层会从开始调用,依次加,直到函数返回-1为止。

 *         @lpQYFuncInfo :   返回的功能信息

 *         @Result           :   0:说明,返回一个正常的信息,-1,说明信息已经取完

 * 修改记录:

 *            20091112   赵海杰     创建

 **/

typedef int (__stdcall *GETQYFUNCINFO)(int index, LPQYFUNCINFO lpQYFuncInfo);

 

/**

 * 函数名称:在运行之前被调用

 * 函数功能:在运行前被调用,给客户做准备工作,如果此函数调用失败,

 * 参数列表:

 *         @lpParam      :   此参数传出,系统会保留,在特定的模式调用下,会传出此参数

 *         @lpContextAPI :   接口,一些操作函数

 *         @Result           :   0:成功

 * 修改记录:

 *            20091202   赵海杰     创建

 **/

typedef int (__stdcall *ONPREPARE)(LPCONTEXTAPI lpContextAPI, void*& lpParam);

 

/**

 * 函数名称:在停止之后被调用,

 * 函数功能:在停止后调用,给客户做清除资源的时间

 * 参数列表:

 *         @lpParam      :   此参数传出,系统会保留,在特定的模式调用下,会传出此参数

 *         @lpContextAPI :   接口,一些操作函数

 *         @Result           :   0:说明,返回一个正常的信息,-1,说明信息已经取完

 * 修改记录:

 *            20091202   赵海杰     创建

 **/

typedef int (__stdcall *ONUNPREPARE)(LPCONTEXTAPI lpContextAPI, void* lpParam);

 

/**

 * DLL中实现的函数导出名称为如下:

 **/

const char QY_FUNC_NAME[] ="GetQYFuncInfo";

const char QY_FUNC_PREPAR[] ="OnPrepare";

const char QY_FUNC_UNPREPAR[] ="OnUnPrepare";

GetQYFuncInfo是取组件功能处理函数的信息,等下我们会详细讲

OnPrepare是在中间件启动时会被运行,客户可以在此函数中进行资源的申请操作,如果需要给业务功能函数传递参数,可以把一个有效的资源通过lpParam参数传入.这样,这个参数会在回调函数中传中间件原样传出.如果此函数返回不为0,那么加载将会失败.

OnUnPrepare函数是供客户释放资源,它会在中间件停止时被调用.

目前OnPrepare,OnUnPrepare我们用不着,我们直接返回0就可以了, GetQYFuncInfo函数有两个函数,每二个函数是一个结构,我们看一下它的定义:

typedef struct qy_func_info

{

    FUNC_TYPE            FuncType;            // 接口类型

    unsigned int         uiFuncNo;            // 功能号

    union      // 函数指针

    {

       LPONRECVBUFF             lpOnRecvBuff;

       LPONRECVBUFFPARAM        lpOnRecvBuffParam;

#ifdef __SQL

       LPONRECVBUFFDB              lpOnRecvBuffDB;

       LPONRECVBUFFPARAMDB         lpOnRecvBuffParamDB;

#endif // __SQL

#ifdef ZHJ_PACK_H

       LPONRECVPACK             lpOnRecvPack;

       LPONRECVPACKPARAM        lpOnRecvPackParam;

#ifdef __SQL

       LPONRECVPACKDB              lpOnRecvPackDB;

       LPONRECVPACKPARAMDB         lpOnRecvPackParamDB;

#endif // __SQL

#endif // ZHJ_PACK_H

    };

    const char*              lpFuncName;              // 功能名称

    const char*              lpDataSrc;           // 数据源名称

   

} QYFUNCINFO, *LPQYFUNCINFO;

我们可以看到,它的参数主要就是回调函数类型,功能号,回调函数指针,功能名称和数据源名称,回调函数类型,就是我们上面介绍的八种,功能号是我们自己分配的,回调函数指针是我们的业务处理函数,功能名称是我们为功能处理起的名称,数据源名称是指此功能还需要的数据库是哪个.GetQYFuncInfo函数中,我们需要把此结构填充后返回中间件,中间件通过此结构中的信息来获知组件所能处理的功能.由于每个组件不止只处理一个请求,所以GetQYFuncInfo函数第一个参数,就是为了取多个功能信息而用的,中间件会循环调用此函数,调用时, index参数会从0开始,每调用一次就递加一,直接返回非零值为止.所以我们在编写GetQYFuncInfo函数时,可以采用以下模式:

int __stdcall GetQYFuncInfo(int index, LPQYFUNCINFO lpQYFuncInfo)

{

    switch(index)

    {

    case 0:

       lpQYFuncInfo->lpOnRecvBuff = Onxxxxx1;

       lpQYFuncInfo->uiFuncNo = xxx;

       lpQYFuncInfo->FuncType = PT_BUFF;

       lpQYFuncInfo->lpFuncName = xxxxx;

lpQYFuncInfo-> lpDataSrc= xxxxx;

       break;

    case 1:

       lpQYFuncInfo->lpOnRecvBuff = Onxxxx2;

       lpQYFuncInfo->uiFuncNo = xxx;

       lpQYFuncInfo->FuncType = PT_BUFF;

       lpQYFuncInfo->lpFuncName = xxxxx;

lpQYFuncInfo-> lpDataSrc= xxxxx;

       break;

    default:

       return -1;

    }

    return 0;

}

这样,中间件循环分别参数index0,1调用两次后,把两个功能信息取出,再用2index的值调用时就返回-1,这时中间件就知道已经把所有的功能信息取完了,当然,如果有更多的功能信息,只需要把case标签向下写下去就可以了,但中间不能跳跃.

好了,我们写下我们的先三个导出函数吧:

 

int __stdcall OnPrepare(LPCONTEXTAPI lpContextAPI, void*& lpParam)

{

    return 0;

}

 

int __stdcall OnUnPrepare(LPCONTEXTAPI lpContextAPI, void* lpParam)

{

    return 0;

}

int __stdcall GetQYFuncInfo(int index, LPQYFUNCINFO lpQYFuncInfo)

{

    switch(index)

    {

    case 0:

       lpQYFuncInfo->lpOnRecvBuff = OnRecvDeal;

       lpQYFuncInfo->uiFuncNo = 100;

       lpQYFuncInfo->FuncType = PT_BUFF;

       lpQYFuncInfo->lpFuncName = 计算平方值;

lpQYFuncInfo-> lpDataSrc= NULL;

       break;

    default:

       return -1;

    }

    return 0;

}

,现在我们所有的函数,都写好了,下面就是写生成目标了,先创建一个空的DLL项目,然后新建一个CPP,把上面四个函数写进去,包含必要的头文件

#include "ZContextAPI.h"

#include "QYFuncDef.h"

然后在再添加一个def文件,写入

LIBRARY   

EXPORTS

    GetQYFuncInfo

    OnPrepare

OnUnPrepare

然后编绎生成,就可以了.

,组件生成好了,我们把此组件复制到中间件目录下面的com文件夹中,然后打开配置文件,<Functions> </Functions>中添加一条<Function dll="s_test.dll"/>就行了,s_test.dll为生成dll为生成组件的名称.然后把配置文件保存.我们可以用命令行启动中间件,假设配置文件的名称为Qianying.xml,那么启动的命令就如下:

ZQianying.exe -f Qianying.xml

启动后,我们可以输入P,然后在输出的信息中,有功能函数信息中,找到我们所添加的组件,如果加载失败,会在启动时输出加载文件失败的字样.

好了,到此为止,我们已经做了一个简单的组件了.下次,我们会介绍相应客户端的开发.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值