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

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

组件开发:

一个组件需要开发成什么样子?我们首先想象一样,如果我们想要提供一个功能,需要知道什么?我们首先要知道给什么请求提供?怎么处理这个请求?处理这个请求是否还需要什么样的资源?我们把这几个问题解决掉了,还需要把这些信息告诉中间件,让中间件能够正确根据请求包的信息,正确处理请求.下面我们就来做此组件,最好结合”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,就可以了,我们看一下,它对应的函数类型定义:

typedefint (__stdcall *LPONRECVBUFF)(LPCONTEXTAPI lpContextAPI, unsigned int uiSessionID, unsigned int uiFuncNo, unsigned short usRecvBuffHandle, unsigned short

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

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

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

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

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

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

int__stdcall OnRecvDeal(LPCONTEXTAPI lpContextAPI, unsigned int uiSessionID, unsigned int uiFuncNo, unsigned short usRecvBuffHandle, unsigned short
{
    int
    unsigned long
    ulBuffSize = sizeof(int);
    lpContextAPI->DetachBuffer(usRecvBuffHandle, &i, ulBuffSize);
    i = i * i;
    lpContextAPI->AttachBuffer(usSendBuffHandle, &i, sizeof(int));
    return
}

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

/**
 * 函数名称:取接口信息
 * 函数功能:从DLL中取接口信息,在DLL实现一个与此函数声明一致的函数,循环调用。
 * 参数列表:
 *         @index        :   序号,上层会从开始调用,依次加,直到函数返回-1为止。
 *         @lpQYFuncInfo :   返回的功能信息
 *         @Result           :   0:说明,返回一个正常的信息,-1,说明信息已经取完
 * 修改记录:
 *            20091112   赵海杰     创建
 **/
typedefint (__stdcall *GETQYFUNCINFO)(int
 
/**
 * 函数名称:在运行之前被调用
 * 函数功能:在运行前被调用,给客户做准备工作,如果此函数调用失败,
 * 参数列表:
 *         @lpParam      :   此参数传出,系统会保留,在特定的模式调用下,会传出此参数
 *         @lpContextAPI :   接口,一些操作函数
 *         @Result           :   0:成功
 * 修改记录:
 *            20091202   赵海杰     创建
 **/
typedefint (__stdcall *ONPREPARE)(LPCONTEXTAPI lpContextAPI, void*& lpParam);
 
/**
 * 函数名称:在停止之后被调用,
 * 函数功能:在停止后调用,给客户做清除资源的时间
 * 参数列表:
 *         @lpParam      :   此参数传出,系统会保留,在特定的模式调用下,会传出此参数
 *         @lpContextAPI :   接口,一些操作函数
 *         @Result           :   0:说明,返回一个正常的信息,-1,说明信息已经取完
 * 修改记录:
 *            20091202   赵海杰     创建
 **/
typedefint (__stdcall *ONUNPREPARE)(LPCONTEXTAPI lpContextAPI, void* lpParam);
 
/**
 * DLL中实现的函数导出名称为如下:
 **/
constchar QY_FUNC_NAME[] ="GetQYFuncInfo";
constchar QY_FUNC_PREPAR[] ="OnPrepare";
constchar QY_FUNC_UNPREPAR[] ="OnUnPrepare";

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

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

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

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

typedefstruct
{
    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
{
    switch(index)
    {
    case
       lpQYFuncInfo->lpOnRecvBuff = Onxxxxx1;
       lpQYFuncInfo->uiFuncNo = xxx;
       lpQYFuncInfo->FuncType = PT_BUFF;
       lpQYFuncInfo->lpFuncName = “xxxxx”;
lpQYFuncInfo-> lpDataSrc= “xxxxx”;
       break;
    case
       lpQYFuncInfo->lpOnRecvBuff = Onxxxx2;
       lpQYFuncInfo->uiFuncNo = xxx;
       lpQYFuncInfo->FuncType = PT_BUFF;
       lpQYFuncInfo->lpFuncName = “xxxxx”;
lpQYFuncInfo-> lpDataSrc= “xxxxx”;
       break;
    default:
       return
    }
    return
}

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

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

int__stdcall OnPrepare(LPCONTEXTAPI lpContextAPI, void*& lpParam)
{
    return
}
 
int__stdcall OnUnPrepare(LPCONTEXTAPI lpContextAPI, void* lpParam)
{
    return
}
int__stdcall GetQYFuncInfo(int
{
    switch(index)
    {
    case
       lpQYFuncInfo->lpOnRecvBuff = OnRecvDeal;
       lpQYFuncInfo->uiFuncNo = 100;
       lpQYFuncInfo->FuncType = PT_BUFF;
       lpQYFuncInfo->lpFuncName = “计算平方值”;
lpQYFuncInfo-> lpDataSrc= NULL;
       break;
    default:
       return
    }
    return
}

四,现在我们所有的函数,都写好了,下面就是写生成目标了,先创建一个空的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,然后在输出的信息中,有功能函数信息中,找到我们所添加的组件,如果加载失败,会在启动时输出加载文件失败的字样.

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