前营中间件(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,就可以了,我们看一下,它对应的函数类型定义:
  typedefint (__stdcall *LPONRECVBUFF)(LPCONTEXTAPI lpContextAPI, unsignedint uiSessionID, unsignedint uiFuncNo, unsignedshort usRecvBuffHandle, unsignedshort usSendBuffHandle);
  它有五个参数, lpContextAPI参数提供了许多函数操作,它的声明可以参数头文件"ZContextAPI.h",我们可以看到,它提供了许多函数,此处我们先介绍两个函数:
  virtualint DetachBuffer(unsignedshort usBufferHandle, void* lpBuffer, unsignedlong& ulBuffSize) = 0;
  virtualint AttachBuffer(unsignedshort usBufferHandle, void* lpBuffer, unsignedlong ulBuffSize) = 0;
  DetachBuffer函数是把数据从缓冲区中取出来, AttachBuffer函数是把数据写入缓冲区.此处,我们先讲一个我们系统对消息内存的管理模式.
  在我们系统中,我们把中间件保存消息所用到的内存进行统一管理,每块内存保存一个消息,每块内存都有一个unsignedshort数字作为每块内存数据的唯一标志,而把与此内存相关的操作都都进行了封装,以防止人为的损坏消息数据,而DetachBuffer和AttachBuffer操作就是分别从缓冲区中取数据和把数据写入缓冲区,他们的第一个参数usBufferHandle就是缓冲区的标识.
  现在我们也知道了回调函数,第四,五个参数的意思了, usRecvBuffHandle是传入缓冲区的标识, usSendBuffHandle是输出缓冲区的标识,而uiSessionID是会话号,我们一般用不到, uiFuncNo是功能号.
  下面,我们就可以写出功能处理函数
  int__stdcall OnRecvDeal(LPCONTEXTAPI lpContextAPI, unsignedint uiSessionID, unsignedint uiFuncNo, unsignedshort usRecvBuffHandle, unsignedshort usSendBuffHandle)
  {
  int i(0);
  unsignedlong 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 赵海杰 创建
  **/
  typedefint (__stdcall *GETQYFUNCINFO)(int index, LPQYFUNCINFO lpQYFuncInfo);
  /**
  * 函数名称:在运行之前被调用
  * 函数功能:在运行前被调用,给客户做准备工作,如果此函数调用失败,
  * 参数列表:
  * @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 qy_func_info
  {
  FUNC_TYPE FuncType; // 接口类型
  unsignedint 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
  };
  constchar* lpFuncName; // 功能名称
  constchar* 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; } 这样,中间件循环分别参数index为0,1调用两次后,把两个功能信息取出,再用2为index的值调用时就返回-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文件夹中,然后打开配置文件,在中添加一条就行了,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、付费专栏及课程。

余额充值