zoukankan      html  css  js  c++  java
  • 使用http.sys,让delphi的多层服务真的飞起来【第二部】

    这是本专题的续集,没读过第一部的看这里:
    http://bbs.2ccc.com/topic.asp?topicid=548153

    之所以要搞第二部是因为第一部跟贴太多,读起来不方便,浪费大家的时间。

    今天咱们聊的主题是:Delphi的DataSnap实质分析
    先说DataSnap中文应该翻译成什么,我个人的译法是:数据快照。DataSnap是后来名字,原来叫MIDAS, Multi-tier Distributed Application Services Suite( 多 层 分 布 式 应 用 程 序 服 务 包) 的 缩 写。大家不要被这么多介绍DataSnap的资料弄晕了,其实原理非常简单。
    要把DataSnap搞明白,必须先把客户端的TClientDataset控件搞明白,不会,找度娘。下面简称CDS。
    CDS有两个OleVarient属性,一个叫Data,一个叫Delta。Delphi的多层框架全靠这哥俩。Data用于客户端从服务器端获取数据,Delta用于客户端将修改的数据保存到服务器端。
    那么,这就简单了,服务器端只要能实现输出Data,接收Delta,三层应用就搞起来了。服务器端这项工作安排给谁呢?TDatasetProvider,下面简称DP。
    DP有一个Data属性,也是OleVariant类型,还有一个ApplyUpdate方法,接受Delta作为输入参数。

    明白了这个道理,我们完全可以抛开Delphi那个复杂的DataSnap不理,自己来构建简单可靠而且高效的多层框架。
    服务器端自然用mORMot来稿,用THttpApiServer+DP,充分发挥http.sys的威力,站在巨人的肩膀上,呼风唤雨。
    客户端我们用Delphi10.2.3来做,支持PC/Android/iOS, 用CDS+TNetHTTPRequest来做。TNetHTTPRequest为XE8新增控件,使用操作系统内置http与https,不需要indy与openssl。追求极致的还可以用更底层的THTTPClient控件。
    不考虑手机的,继续用Delphi7做PC应用的铁粉,用CDS+THttpClientSocket(mORMot自带)。

    Data与Delta都是Variant,无法在网络上传输,我们需要这俩变成字符串,先来两个函数:

    {$IFNDEF UNICODE}
    type
      RawByteString = AnsiString;
    {$ENDIF}

    function VariantArrayToString(const V: OleVariant): RawByteString;
    var
      P: Pointer;
      Size: Integer;
    begin
      Result := '';
      if VarIsArray(V) and (VarType(V) and varTypeMask = varByte) then begin
        Size := VarArrayHighBound(V, 1) - VarArrayLowBound(V, 1) + 1;
        if Size > 0 then begin
          SetLength(Result, Size);
          P := VarArrayLock(V);
          try
            Move(P^, Result[1], Size);
          finally
            VarArrayUnlock(V);
          end;
        end;
      end;
    end;

    function StringToVariantArray(const S: RawByteString): OleVariant;
    var
      P: Pointer;
    begin
      Result := NULL;
      if Length(S) > 0 then begin
        Result := VarArrayCreate([0, Length(S) - 1], varByte);
        P := VarArrayLock(Result);
        try
          Move(S[1], P^, Length(S));
        finally
          VarArrayUnlock(Result);
        end;
      end;
    end;

    看明白了吗?Data与Delta内部存储的都是字节流。变成字节流以后有两种传送方式,一种是以Stream方式,ContentType设置成application/octet-stream;另一种将字节流base64编码成纯文本。大数据流可以加入压缩/减压机制,保密数据可以加入加密/解密机制。

    base64编码与解码,Delphi自带,单元名为EncdDecd。里面有EncodeString与DecodeString两个函数。


    为了让大家把原理搞懂,我们先抛开网络传输层,将CDS与DP放在一个屋子里让他俩亲热一把。

    unit DP2CDSMain;

    interface

    uses
    Forms, DBClient, DB, Provider, ADODB, Controls, Grids, DBGrids, ComCtrls,
    Classes, StdCtrls;

    type
    TForm2 = class(TForm)
    ServerData: TADODataSet;
    Button1: TButton;
    ClientData: TClientDataSet;
    ServerDataSetProvider: TDataSetProvider;
    PageControl1: TPageControl;
    TabSheet2: TTabSheet;
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    procedure Button1Click(Sender: TObject);
    private
    { Private declarations }
    public
    { Public declarations }
    end;

    var
    Form2: TForm2;

    implementation

    uses SysUtils, Variants, EncdDecd;

    {$R *.dfm}

    {$IFNDEF UNICODE}
    type
    RawByteString = AnsiString;
    {$ENDIF}

    function VariantArrayToString(const V: OleVariant): RawByteString;
    var
    P: Pointer;
    Size: Integer;
    begin
    Result := '';
    if VarIsArray(V) and (VarType(V) and varTypeMask = varByte) then begin
    Size := VarArrayHighBound(V, 1) - VarArrayLowBound(V, 1) + 1;
    if Size > 0 then begin
    SetLength(Result, Size);
    P := VarArrayLock(V);
    try
    Move(P^, Result[1], Size);
    finally
    VarArrayUnlock(V);
    end;
    end;
    end;
    end;

    function StringToVariantArray(const S: RawByteString): OleVariant;
    var
    P: Pointer;
    begin
    Result := NULL;
    if Length(S) > 0 then begin
    Result := VarArrayCreate([0, Length(S) - 1], varByte);
    P := VarArrayLock(Result);
    try
    Move(S[1], P^, Length(S));
    finally
    VarArrayUnlock(Result);
    end;
    end;
    end;

    procedure TForm2.Button1Click(Sender: TObject);
    var
    vDataIn, vDataOut: OleVariant;
    cDataIn, cDataOut: RawByteString;
    begin
    ClientData.Close;
    vDataIn := ServerDataSetProvider.Data;
    cDataIn := VariantArrayToString(vDataIn);

    //模拟网络传送
    cDataOut:=cDataIn;

    vDataOut := StringToVariantArray(cDataOut);
    ClientData.Data := vDataOut;
    end;

    end.

    我这里转载了一篇文章细说CDS的用法:
    https://www.cnblogs.com/c5soft/p/9121775.html

    没仔细看过手机版的CDS,不知道是不是完全实现了PC版的功能,用过的朋友多发帖。

    说点题外话,CDS鼠标右键菜单有一项“Assign Local Data...”,可以将相同窗体上的任何TDataset的数据复制到CDS中,如何实现的呢?我猜想就是用到了DP, 应该是这样写的:

    var DP:TDatasetProvider;
    begin
      DP:=TDatasetProvider.Create;
      DP.Dataset=ADODataset1;
      ClientDataset1.Data:=DP.Data;
      DP.Free
    end;

  • 相关阅读:
    msfvenom生成不同类型shell
    (CVE-2021-21972) VM vCenter任意文件上传漏洞复现
    linux新建普通账户并添加密码
    Linux监控平台、安装zabbix、修改zabbix的admin密码
    LVS DR模式搭建、keepalived+lvs
    负载均衡集群相关、LVS介绍、LVS调度算法、LVS NAT模式搭建
    集群相关、用keepalived配置高可用集群
    MySQL主从、环境搭建、主从配制
    Tomcat配置虚拟主机、tomcat的日志
    Tomcat介绍、安装jdk、安装Tomcat、配置Tomcat监听80端口
  • 原文地址:https://www.cnblogs.com/c5soft/p/9123923.html
Copyright © 2011-2022 走看看