標籤:style blog http color os io 使用 ar for
【說明】
這個例子答應大家很久了,一直沒有時間弄,現在正式結合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按鈕啟動服務>
伺服器是基於DIOCP3的。可以在DIOCP3項目中找到源碼[diocp3\samples\socket-Coder\DataModuleDEMO]
啟動用戶端:開啟 [simpleConsole.exe], 依次點擊下面的按鈕就可以看到效果了
【DEMO說明】
服務端處理:
服務端基於DIOCP3通訊,該DEMO做了簡單的封裝,使使用者可以專註於邏輯的實現,和用戶端互動使用variant類型進行。
dmMain,是和用戶端串連想對應的一個對象[和用戶端的串連是一對一的關係]。
我在裡面稍微做了下邏輯處理的示範:
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的成語記錄>
相應按鈕代碼:
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/
[DIOCP3/MyBean/QDAC開源項目] DataModule-DB例子基於MyBean的外掛程式執行個體<三層資料庫方案>