[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是演示窗體插件的宿主。

存放路徑:MyBean\samples\diocp-DBDEMO

啟動服務:SERVER_EXE\diocp3Server.exe  <點擊start按鈕啟動服務>

image

服務器是基於DIOCP3的。可以在DIOCP3項目中找到源碼[diocp3\samples\socket-Coder\DataModuleDEMO]

啟動客戶端:打開 [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 'Service\uRemoteServerDIOCPImpl.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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM