zoukankan      html  css  js  c++  java
  • [DIOCP3/MyBean/QDAC开源项目] DataModule-DB例子基于MyBean的插件实例<三层数据库方案>

    【说明】

    这个例子答应大家很久了,一直没有时间弄,现在正式结合MyBean插件可以很方便的在客户端共享操作连接,执行数据库的各项工作,屏蔽了底层的通信解码器编码等工作,直接传递Variant,给了开发者足够的领活和自由。

    【服务端使用技术】

    diocp3:担当底层的通信任务。

    qworker/iocpTask:担当业务逻辑的处理工作,diocp3接受数据解码后用qworker/iocpTask将数据包投递出来,这样不用占用通信线程。

    qmsgpack:负责将传递的将variant数据打包到流,从流中解码成variant

    dataModule:对应连接的对象,方便进行开发。

    【客户端】

    myBean:制作基于MyBean框架的插件,可以在MyBean的框架模块中直接使用。

    RawTcpClient:用于和服务端进行通信,阻塞的tcp客户端,类似IdTcpClient的精简版本,操作容易。

    【DEMO使用】

    说 明:客户端依赖diocp_bean.dll插件与服务器进行数据交换,DIOCP_DBDEMO.dll是演示窗体插件的宿主。

    存放路径:MyBeansamplesdiocp-DBDEMO

    启动服务:SERVER_EXEdiocp3Server.exe  <点击start按钮启动服务>

    image

    服务器是基于DIOCP3的。可以在DIOCP3项目中找到源码[diocp3samplessocket-CoderDataModuleDEMO]

    启动客户端:打开 [simpleConsole.exe], 依次点击下面的按钮就可以看到效果了

    image

    【DEMO说明】

    服务端处理:

        服务端基于DIOCP3通信,该DEMO做了简单的封装,使用户可以专注于逻辑的实现,和客户端交互使用variant类型进行。

    dmMain,是和客户端连接想对应的一个对象[和客户端的连接是一对一的关系]。

    image

    我在里面稍微做了下逻辑处理的演示:

    function TdmMain.Execute(pvCmdIndex: Integer; var vData: OleVariant): Boolean;
    begin
      case pvCmdIndex of
        0:
          begin
            // 返回服务端时间给客户端
            vData := Now();
            Result := true;
          end;
        1:  // 查询数据
          begin
            // vData 认为是传入的SQL语句
            //   执行后, vData为查询的数据,可以用于对ClientData.Data的赋值
    
            qryMain.Close;
            qryMain.SQL.Clear;
            qryMain.SQL.Add(vData);
            qryMain.Open;
    
            vData := dspMain.Data;
            Result := true;
            qryMain.Close;
          end;
        2:
          begin
            // vData 为执行的语句
            conMain.BeginTrans;
            try
              qryMain.Close;
              qryMain.SQL.Clear;
              qryMain.SQL.Add(vData);
              qryMain.ExecSQL;
              conMain.CommitTrans;
    
              VarClear(vData);
              
              Result := true;
    
    
            except
              conMain.RollbackTrans;
              raise;
            end;
          end;
      end;
    end;

       vData,是客户端传递过来的参数,也是返回给客户端的数据。vData是OleVariant可以容纳任何的数据<配合qmsgPack可以达到任何的数据格式要求>, 如果还达不到你的格式要求,你可以在MyClientContext的dataReceived函数做些修改,下面代码的处理过程依次是:

    解压收到的数据->QmsgPack解包->取出客户端传入的参数->调用dmMain.Execute处理逻辑->qmsgPack编码数据->压缩数据->回传数据到客户端

    procedure TMyClientContext.dataReceived(const pvDataObject: TObject);
    var
      lvMsgPack:TQMsgPack;
      lvStream :TStream;
      lvStream2:TMemoryStream;
      vData:OleVariant;
      lvResult:Boolean;
    begin
      lvMsgPack := TQMsgPack.Create;
      try
        try
          if FdmMain = nil then FdmMain := TdmMain.Create(nil);
    
          lvStream := TStream(pvDataObject);
          lvStream.Position := 0;
    
          // upZip
          TZipTools.unCompressStreamEX(lvStream);
    
          lvStream.Position := 0;
          
          // unpack
          lvMsgPack.LoadFromStream(lvStream);
    
          // get param
          vData := lvMsgPack.ForcePath('cmd.data').AsVariant;
    
          // invoke dataModule function
          lvResult := FdmMain.Execute(lvMsgPack.ForcePath('cmd.index').AsInteger, vData);
    
          // write result info
          lvMsgPack.Clear;
          lvMsgPack.ForcePath('__result.result').AsBoolean := lvResult;
          lvMsgPack.ForcePath('__result.data').AsVariant := vData;
        except
          on E:Exception do
          begin
            lvMsgPack.Clear;
            lvMsgPack.ForcePath('__result.result').AsBoolean := false;
            lvMsgPack.ForcePath('__result.msg').AsString := e.Message;
          end;
        end;
    
        lvStream.Size := 0;
        lvMsgPack.SaveToStream(lvStream);
    
        lvStream.Position := 0;
    
        // zipStream
        TZipTools.compressStreamEX(lvStream);
        lvStream.Position := 0;
    
        // send to client
        self.writeObject(lvStream);
      finally
        lvMsgPack.Free;
      end;
    
    end;

    客户端处理:

       编译的 DLL 放到和EXE同一个目录下面就可以进行自动加载。

       客户端diocp_bean工程中

    library diocp_bean;
    
    uses
      SysUtils,
      Classes,
      mybean.core.beanFactoryForNoVcl,
      uRemoteServerDIOCPImpl in 'ServiceuRemoteServerDIOCPImpl.pas';
    
    {$R *.res}
    
    begin
      beanFactory.RegisterBean('diocpRemoteSvr', TRemoteServerDIOCPImpl).Singleton := true;
    end.

    注册的diocpRemoteSvr插件实现了IRemoteServer接口和IRemoteServerConnector接口,而且该插件为单件模式。其他任何地方调用都只会产生一个连接实例。

    type
      IRemoteServer = interface(IInterface)
        ['{20B5F070-461C-41F4-AA0C-E500A36E18E4}']
    
        /// <summary>
        ///   执行远程动作
        /// </summary>
        function Execute(pvCmdIndex: Integer; var vData: OleVariant): Boolean; stdcall;
      end;
    
      IRemoteServerConnector = interface(IInterface)
        ['{65931F56-07BA-42F8-BD5C-7409053F5B2C}']
        procedure setHost(pvHost: PAnsiChar);
        procedure setPort(pvPort:Integer);
        procedure open;
      end;

    演示操作窗体:<服务端是13K的成语记录>

    image

    相应按钮代码:

    constructor TfrmMain.Create(AOwner: TComponent);
    begin
      inherited;
      // 通过注册的插件ID获取单实例的远程连接操作接口
      FRemoteSvr := TMyBeanFactoryTools.getBean('diocpRemoteSvr') as IRemoteServer;
    end;
    
    procedure TfrmMain.btnConnectClick(Sender: TObject);
    begin
      // 打开远程连接,如果打开过可以不用打开,其他插件中可以直接使用
      (FRemoteSvr as IRemoteServerConnector).setHost(PAnsiChar(AnsiString(edtHost.Text)));
      (FRemoteSvr as IRemoteServerConnector).setPort(StrToInt(edtPort.Text));
      (FRemoteSvr as IRemoteServerConnector).open;
      ShowMessage('open succ!');
    end;
    
    procedure TfrmMain.btnOpenClick(Sender: TObject);
    var
      vData:OleVariant;
      l : Cardinal;
    begin
      vData := mmoSQL.Lines.Text;
    
      l := GetTickCount;
    
      // 在远程打开SQL
      if FRemoteSvr.Execute(1, vData) then
      begin
        self.cdsMain.Data := vData;
        Self.Caption := Format('query: count:%d, time:%d',
          [self.cdsMain.RecordCount, GetTickCount - l]);
      end;
    end;

    PS: 其他功能大家自己去实现,该文章写了好几个中午,希望对大家有用。

    由于qdac开源项目 >=D2007,所以D7下面不能编译服务端,和diocp_bean.项目(你可以在D2007中编译好diocp_bean和服务端工程,其他插件可以在d7中完成)

    -----------------------------------------------------------------------------------------------------

    MyBean 轻量级配置开源框架 开源地址

    https://git.oschina.net/ymofen/delphi-framework-MyBean

    DIOCP3开源地址

    https://github.com/ymofen/diocp3

    qdac项目信息

    官方网站:http://www.qdac.cc

    官方QQ群:250530692

    QDAC项目网址:http://sourceforge.net/p/qdac3

    在线源码浏览:http://sourceforge.net/p/qdac3/code/HEAD/tree/

    SVN检出地址:

    http://svn.code.sf.net/p/qdac3/code/
    svn://svn.code.sf.net/p/qdac3/code/

  • 相关阅读:
    CND库(Bootstrap 、React、Vue、Angular.js、JQuery)
    使用线性渐变实现滚动进度条
    设置鼠标光标样式
    百度地图API部分功能
    网页的三种布局(固定布局,流式布局,弹性布局)
    这一次带你彻底了解远程Cookie操作
    js获取各种高度和宽度
    点击按钮,回到页面顶部的5种写法
    Chrome默认字体大小
    CSS Grid 网格布局
  • 原文地址:https://www.cnblogs.com/DKSoft/p/3957761.html
Copyright © 2011-2022 走看看