• 利用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 的大牛兄弟。

  • 相关阅读:
    kubernetes对象之secrets
    kubernetes对象之cronjob
    Kubernetes对象之ReplicaSet
    详谈kubernetes更新-2
    详谈kubernetes滚动更新-1
    设计模式-抽象工厂模式
    深入JVM-Class装载系统
    设计模式-工厂方法模式
    设计模式-单例模式
    设计模式-6大设计原则
  • 原文地址:https://www.cnblogs.com/xalion/p/8379518.html
Copyright © 2020-2023  润新知