利用Delphi-cross-socket 庫提升kbmmw 跨平台開發


以前我寫過了,通過httpsys 提升windows 下,delphi 多層應用。隨着delphi 10.2 對linux 的支持,很多人也想在linux 下

發布kbmmw 服務器,但是官方僅通過indy 支持 linux。剛好國內有大牛開源了Delphi 跨平台 Socket 通訊庫.

通過這個可以直接讓kbmmw 服務器高速的運行在在windows、linux、Mac 上。

delphi-cross-socket 的開源地址為:https://github.com/winddriver/Delphi-Cross-Socket

下面是官網的部分介紹。

Delphi 跨平台 Socket 通訊庫

作者: WiNDDRiVER(soulawing@gmail.com)
重要更新(2017.08.22)

    代碼重構, 做了大量修改, 詳見源碼
    增加了幾個新的 interface, 用法詳見 demos
        ICrossSocket
        ICrossSslSocket
        ICrossServer
        ICrossSslServer

特性

    針對不同平台使用不同的IO模型:
        IOCP

        Windows

        KQUEUE

        FreeBSD(MacOSX, iOS...)

        EPOLL

        Linux(Linux, Android...)

    支持極高的並發
        Windows

        能跑10萬以上的並發數, 需要修改注冊表調整默認的最大端口數

        Mac

        做了初步測試, 測試環境為虛擬機中的 OSX 10.9.5, 即便修改了系統的句柄數限制, 最多也只能打開32000多個並發連接, 或許 OSX Server 版能支持更高的並發吧

    同時支持IPv4、IPv6

    零內存拷貝

已通過測試

    Windows
    OSX
    iOS
    Android
    Linux

建議開發環境

    要發揮跨平台的完整功能請使用Delphi 10.2 Tokyo及以上的版本
    最低要求支持泛型和匿名函數的Delphi版本, 具體是從哪個版本開始支持泛型和匿名函數的我也不是太清楚

 

大家可以下載對應的源碼,並使用delphi 測試它附帶的例子程序。

今天簡單說一下如何利用delphi-cross-socket 實現kbmmw 的跨平台開發。

 

 

首先我們做一個kbmmw transport.

聲明如下。

unit kbmMWCrossScoketServerTransport;

{.$define httpsyslog}

interface

uses
  Classes, Sysutils,
  kbmMWCustomTransport,kbmMWServer,kbmMWGlobal, variants,
  kbmMWHTTPUtils,kbmMWExceptions,
  {$ifdef httpsyslog}
       kbmMWLog,
  {$endif}
  Net.CrossSocket,
  {$IFDEF __SSL__}
  Net.CrossSslSocket,
  {$IFDEF POSIX}
  Net.CrossSslDemoCert,
  {$ENDIF}
  {$ENDIF}
  Net.SocketAPI;

type

  TProtServer = class(TkbmMWServer);
  TxalionTransport=class(TkbmMWCustomServerTransport);

  Txalioninfo=class(TkbmMWServerTransportInfo);

  Txalionserver = class
  private
         FServer:Tkbmmwserver;
         FTransport: TkbmMWCustomServerTransport;

         FSocket: {$IFDEF __SSL__}TCrossSslSocket{$ELSE}TCrossSocket{$ENDIF};


   procedure OnReceived(Sender: TObject; AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer);

  public
    constructor Create;
    destructor Destroy; override;


  end;

 TkbmMWCustomCrossScoketServerTransport = class(TkbmMWCustomServerTransport)
  private
    { Private declarations }


      FCrossScoketServer: TxalionServer;

      Fhost:string;
      Fport:integer;
     // Fssl:boolean;
      Fversion:string;

  public
    // @exclude
    constructor Create(AOwner:TComponent); override;
    // @exclude
    destructor Destroy; override;

  public
     class function IsSerializedTransport:boolean; override;
     class function IsConnectionlessTransport:boolean; override;

 
     procedure Listen; override;
     procedure Close; override;
  

    function IsListening:boolean; override;

  published

    { 服務器 ip    例如   127.0.0.1}
    property Host:string read Fhost write Fhost;


    property Port:integer read Fport write Fport;


    Property Version:string read Fversion;


  end;

{$IFDEF LEVEL16}
  [ComponentPlatformsAttribute({$IFDEF LEVEL26}pidLinux64 or{$ENDIF}
                               {$IFDEF LEVEL23}pidiOSDevice64 or {$ENDIF}
                               {$IFDEF LEVEL18}pidiOSSimulator or pidiOSDevice or {$ENDIF}
                               {$IFDEF LEVEL19}pidAndroid or {$ENDIF}
                               pidWin32 or pidWin64
                               {$IFDEF LEVEL17} or pidOSX32{$ENDIF})]
{$ENDIF}

  TkbmMWCrossScoketServerTransport= class(TkbmMWCustomCrossScoketServerTransport)
  published
    { Published declarations }

    property Crypt;
    property Compression;
    property StreamFormat;
    property VerifyTransfer;
    property TransportStateOptions;
    property FormatSettings;
    property Plugin;
    property Params;
    property StringConversion;
    property NodeID;
    property ClusterID;
  end;
 {$I CrossSocketversion.inc}

{$ifdef httpsyslog}
 var

  filelogmgr:TkbmMWLocalFileLogManager;

{$endif}

 

實現部分如下:

implementation

function kbmMWCrossScoketPutVerificationHeader(Size:integer):Tbytes;
var
   s:string;
   c:byte;
   i:integer;
const
   EOL=#10#13;
begin
          s:=format('KBMMW X%0.8x ',[Size]); // 16 bytes.
          c:=0;
          for i:=KBMMW_STRINGCHAROFS to 16-KBMMW_STRINGCHARLENOFS do
              c:=c xor ord(s[i]);
          s:=s+format('X%0.2x'+EOL,[c]);    // 1+2+CR+LF = 5 bytes.
        setlength(result,length(s));
        for i := 1 to length(s) do
           result[i-1]:=ord(s[i]);

end;



function kbmMWCrossScoketGetVerificationHeader(buff:pointer;len:integer):integer;
var
   s,s1:string;
   c,c1:byte;
   i:integer;
   pb:pbyte;
const
   SINVDATA = 'Invalid data';
begin
     Result:=0;                             // Satisfy compiler.
     c1:=0;
     s:='';
     try

       pb:=buff;
        for I := 0 to 20 do
          s:=s+chr((pb+i)^);

             // Extract checksum.
             try
                s1:=copy(s,17,3);
                c1:=strtoint(s1);
             except
                kbmMWRaiseException(KBMMW_ERR_TRANSPORT_INVALIDDATA,SINVDATA);
             end;

             // Calculate checksum.
             c:=0;
             for i:=KBMMW_STRINGCHAROFS to 16-KBMMW_STRINGCHARLENOFS do
                 c:=c xor ord(s[i]);

             // Match?
             if c<>c1 then
                exit;
                //kbmMWRaiseException(KBMMW_ERR_TRANSPORT_INVALIDDATA,SINVDATA);

             // Extract size.
             s1:=copy(s,7,9);
             try
                Result:=strtoint(s1);
             except
               exit;

               // kbmMWRaiseException(KBMMW_ERR_TRANSPORT_INVALIDDATA,SINVDATA);
             end;

     except
         kbmMWDebugDumpMemory(mwdlAdvanced,mwdtTransport,kbmMWDebugWhere,'Exception during iocp verification',
          PByte(s),ByteLength(s));
        raise;
     end;
end;





constructor Txalionserver.Create;
begin
  inherited;
   FSocket :=
    {$IFDEF __SSL__}
    TCrossSslSocket
    {$ELSE}
    TCrossSocket
    {$ENDIF}
    .Create(0);
  //FSocket.OnConnected := OnConnected;
  FSocket.OnReceived := OnReceived;
end;

destructor Txalionserver.Destroy;
begin
   FSocket.Free;
  inherited;
end;





procedure Txalionserver.OnReceived(Sender: TObject; AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer);


var

  headlen:integer;// 校驗為 21 ,不校驗為4

   retl,i, j,inlen:integer;

   cStreamClass:TkbmMWCustomTransportStreamClass;
   OutStream:IkbmMWCustomResponseTransportStream;
   InStream:IkbmMWCustomRequestTransportStream;
   stamp:int64;
   AInfo:ikbmMWServerTransportInfo;//IkbmMWCustomTransportInfo;
   indata:pbyte;
   retdata:Tbytes;

   sr,query,spath,s:String;
   vt:word;

   bsize:tbytes;

   rethead:Tbytes;
   inStreamFormat:string;
begin


     inStreamFormat:=TxalionTransport(FTransport).StreamFormat;
     if   (inStreamFormat<>'STANDARD')  then
     begin
         raise Exception.Create('Only support STANDARD StreamFormat.');
     end;
     //           begin

                   if TxalionTransport(FTransport).VerifyTransfer then
                      begin
                       headlen:=21;

                       if kbmMWCrossScoketGetVerificationHeader(abuf,alen)<>(alen-headlen) then  //21為校驗的頭

                          begin
                            AConnection.SendBytes(Tencoding.UTF8.GetBytes('Invalid data'));
                             exit;
                          end;

                      end
                      else
                        begin
                          headlen:=4;
                        end;


       stamp:=TkbmMWTiming.GetTimeUS;
       aInfo:=TkbmMWServerTransportInfo.Create;
       cStreamClass:=TxalionTransport(FTransport).FControllerClass.CheckGetStreamClass(mwmtResponse);
       OutStream:=TkbmMWCustomResponseTransportStream(cStreamClass.Create(FTransport,AInfo));


       cStreamClass:=TxalionTransport(FTransport).FControllerClass.CheckGetStreamClass(mwmtRequest);
       InStream:=TkbmMWCustomRequestTransportStream(cStreamClass.Create(FTransport,AInfo));
       InStream.RemoteLocation:=AConnection.PeerAddr;// Client.PeerIP;

       inlen:=alen-headlen;

         try
              if inlen>0 then
                    begin
                      InStream.DataStream.Clear;
                      InStream.DataStream.SetSize( inlen);
                      indata:=pbyte(abuf);
                      inc(indata,headlen);
                      move(indata^,InStream.DataStream.Memory^,inlen);
                    end;



                TProtServer(FServer).InternalServeStream(FTransport,InStream,OutStream);
                TProtServer(FServer).UpdateGlobalStats(InStream,OutStream,stamp);

                 // Send response back.

                retl:=OutStream.DataStream.Size;
                if TxalionTransport(FTransport).VerifyTransfer then
                    begin
                     rethead:=kbmMWCrossScoketPutVerificationHeader(retl);
                     AConnection.SendBytes(rethead);

                   //  client.Send(rethead[0],headlen);
                    end
                    else
                      begin

                          SetLength(bSize,4);
                          bSize[3]:=(retl shr (8+8+8)) and 255;
                          bSize[2]:=(retl shr (8+8)) and 255;
                          bSize[1]:=(retl shr (8)) and 255;
                          bSize[0]:=retl and 255;

                          AConnection.SendBytes(bsize);
                          //client.Send(bsize[0],4);
                      end;


                  setlength(retdata,retl);
                  move(OutStream.DataStream.Memory^,retdata[0],retl);
                  AConnection.SendBytes(retdata);

                  except
                     on E: Exception do
                     begin
                      {$ifdef httpsyslog}
                         Log.Warn('Find error.'+e.Message );
                         log.Warn('info:'+TkbmMWPlatformMarshal.memory2String(indata,inlen));
                      {$endif}

                       AConnection.SendBytes(Tencoding.UTF8.GetBytes(e.Message));

                   //    client.Send(e.Message);

                     end;
                  end;

end;
{ TkbmMWCustomhttpsysServerTransport }

procedure TkbmMWCustomCrossScoketServerTransport.Close;
begin

     if state=mwtrstDisconnected then   exit;

     FCrossScoketServer.FSocket.DisconnectAll;


//      Fiocpserver.fiocpServer.Stop;
      SetState(mwtrstDisconnected);
end;

constructor TkbmMWCustomCrossScoketServerTransport.Create(AOwner: TComponent);
begin
  inherited;

   FCrossScoketServer:= TxalionServer.Create;

   Fhost:='0.0.0.0';
   Fport:=3000;

   self.VerifyTransfer:=True;

   fversion:=sysversion;


end;

destructor TkbmMWCustomCrossScoketServerTransport.Destroy;
begin
   if FCrossScoketServer<>nil then
      freeandnil(FCrossScoketServer);
  inherited;
end;


class function TkbmMWCustomCrossScoketServerTransport.IsConnectionlessTransport: boolean;
begin
           Result:=False;
end;



function TkbmMWCustomCrossScoketServerTransport.IsListening: boolean;
begin
          Result:=(state=mwtrstListening);
end;

class function TkbmMWCustomCrossScoketServerTransport.IsSerializedTransport: boolean;
begin
           Result:=true;
end;

procedure TkbmMWCustomCrossScoketServerTransport.Listen;

begin
  inherited;


    if state=mwtrstListening then   exit;

     FCrossScoketserver.FServer:=server;
     FCrossScoketserver.FTransport:=self;
     FCrossScoketserver.FSocket.Listen(Fhost,Fport);
     SetState(mwtrstListening);

end;


{$ifdef httpsyslog}
initialization
     filelogmgr:=TkbmMWLocalFileLogManager.Create('./kbmmwlog.txt'); // ,一個是日志文件
     filelogmgr.FileOptions:=[mwlfoDeleteOldLog];//:=true; // 刪除老的日志文件
     filelogmgr.FlushInterval:=0; // 寫文件時間間隔,0 為立即寫文件
     Log.LogManager:=filelogmgr; //
     log.Info('Start logging!');

{$endif}

 

把以上代碼注冊成控件並安裝。

打開kbmmw 自帶的例子 ,位置如圖:

然后把原來的indy Transport 換成 我們新作的Transport.

 

 服務端就ok了,我們可以編譯運行,如圖:

 

由於這是標准的tcp 傳輸協議,我們在客戶段就直接使用indy 的代碼(官方自帶的)。

打開后,直接運行。

 

 

連接服務器,正常工作。windows 上沒有任何問題。

在linux 下,我們可以參照這一篇 文章,把indy transport

換成 delphi-cross-socket transport 就可以了,其他的都不用動。就可以實現Linux 下運行的kbmmw server 了。

一下子直接跨三個平台。太爽了。

 

在此再次感謝開源delphi-cross-socket 的大牛兄弟。

 


免責聲明!

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



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