zoukankan      html  css  js  c++  java
  • RemObjects(一)客户端远程调用服务端接口过程

    RemObjects SDK 是高度封装的产物,对OOP发挥极致。

    本文将以RemObjects SDK最简单的DEMO——FirstSample为例,

    介绍客户端是如何完成远程调用服务端接口的全过程。

    也理解为什么可以通过不同传输通道(TCP/HTTP...),不同消息格式(二进制,SOAP...) 与服务端进行通讯

    客户端就这三个RO控件,是如何完成一个完整的调用过程的呢?

    在程序启动的时候,RO已经完成了一系列动作,

    先了解一个Delphi主程序代码的执行顺序

    程序启动 -->

    执行 initialization 处的代码 (在主程序运行前运行并且只运行一次)-->

    {工程文件}

    begin
      Application.Initialize;
      Application.CreateForm(TForm, Form1);
      Application.Run;
    end.                        -->

    执行 finalization 处的代码  (在程序退出时运行并且只运行一次)

    也就initialization处的代码是最先运行的,当我们把这三个控件摆上的时候,

    会自动加入 uROClient,uROBinMessage,uROWinInetHttpChannel,

    同时为加调用服务端接口,手动把接口声明文件也加入FirstSample_Intf

    而这三个文件都有initialization ,它们做了什么呢?

    {unit uROClient;}
    
    procedure RegisterMessageClass(aROMessageClass : TROMessageClass);
    begin
      _MessageClasses.Add(aROMessageClass);
      if Classes.GetClass(aROMessageClass.ClassName) = nil then
        Classes.RegisterClass(aROMessageClass);
    end;
    
    procedure RegisterTransportChannelClass(aTransportChannelClass : TROTransportChannelClass);
    begin
      if _TransportChannels.IndexOf(aTransportChannelClass.ClassName)<0 then begin
        _TransportChannels.AddObject(aTransportChannelClass.ClassName, TObject(aTransportChannelClass));
        if Classes.GetClass(aTransportChannelClass.ClassName) = nil then
          Classes.RegisterClass(aTransportChannelClass);
      end;
    end;
    
    procedure RegisterProxyClass(const anInterfaceID : TGUID; aProxyClass : TROProxyClass);
    var s : string;
    begin
      s := GUIDToString(anInterfaceID);
      if _ProxyClasses.IndexOf(s)<0
        then _ProxyClasses.AddObject(GUIDToString(anInterfaceID), TObject(aProxyClass))
    end;
    
    initialization
      _MessageClasses := TClassList.Create;   //TClassList 只是给 TList 起个别名
      _ExceptionClasses := TClassList.Create;
    
      _ProxyClasses := TStringList.Create;
      _ProxyClasses.Duplicates := dupError;
      _ProxyClasses.Sorted := TRUE;
    
      _TransportChannels := TStringList.Create;
      _TransportChannels.Duplicates := dupError;
      _TransportChannels.Sorted := TRUE;
    
      ... ...
    

    这里初始化了3个列表,将分别用于存储 消息格式类,代理类,传输通道类

    而三个全局函数只是将相应的对象添加到列表

    {unit uROBinMessage;}
    
    initialization
      RegisterMessageClass(TROBinMessage);
    
    {unit uROWinInetHttpChannel;}
    
    initialization
      RegisterTransportChannelClass(TROWinInetHTTPChannel);
    
    {unit FirstSample_Intf;}
    
    initialization
     //第一个函数是接口ID,第二个是接口的实现类
      RegisterProxyClass(IFirstSampleService_IID, TFirstSampleService_Proxy);
    

    这样一来,程序启动的时候就已经完成了一系列操作

    接下来到了主窗体创建时执行的代码

    {unit FirstSampleClientMain;}
    
    constructor TFirstSampleClientMainForm.Create(aOwner: TComponent);
    begin
      inherited;
      fFirstService := (RORemoteService as IFirstSampleService);
    end;
    

    也许对初学者来讲有点不可思议, 对象RORemoteService与接口IFirstSampleService之间根本不存在任何关系,居然可以 as ?
    这是因为 as 操作会先调用接口查询 QueryInterface,看下TRORemoteService的QueryInterface函数是如何实现的

    {unit uRORemoteService;}
    
    function TRORemoteService.QueryInterface(const IID: TGUID; out Obj): HResult;
    var 
        proxyclass : TROProxyClass;
        proxy : TROProxy;
    begin
      result := inherited QueryInterface(IID, Obj);
    
      if (result <> S_OK) then begin
        //通过接口ID查询到接口实现类的引用
        proxyclass := FindProxyClass(IID, TRUE);
    
        if (proxyclass=NIL) then Exit
        else begin
      	  CheckCanConnect(false);
        //创建接口实现对象
          proxy := proxyclass.Create(Self);
          proxy.GetInterface(IID, Obj);
          result := S_OK;
        end;
      end;
    end;
    

     其中的 FindProxyClass 定义在 ROClient

    {unit uROClient;}
    
    function FindProxyClass(const anInterfaceID : TGUID; Silent : boolean = FALSE) : TROProxyClass;
    var idx : integer;
        s : string;
    begin
      result := NIL;
      s := GUIDToString(anInterfaceID);
      idx := _ProxyClasses.IndexOf(s);
      if (idx>=0)
        then result := TROProxyClass(_ProxyClasses.Objects[idx])
      else begin
        if not Silent
          then RaiseError(err_UnknownProxyInterface, [s])
      end;
    end;
    

    所以 fFirstService := (RORemoteService as IFirstSampleService); 就获取了代理类的实现接口

    而代理类做了些什么呢? 

    proxy := proxyclass.Create(Self);  //在TRORemoteService的QueryInterface函数

    proxyclass是一个TROProxyClass对象,而TROProxyClass= class of TROProxy;也就是TROProxy类引用

    看下TROProxy的构造函数

    {unit uROClient;}
    
    constructor TROProxy.Create(const aRemoteService: IRORemoteService);
    begin
      Create(aRemoteService.Message, aRemoteService.Channel);
      fCloneMessage := aRemoteService.CloneMessage;
    end;
    
    constructor TROProxy.Create(const aMessage: IROMessage;
      const aTransportChannel: IROTransportChannel);
    begin
      inherited Create;
      fMessage := pointer(aMessage);
      fTransportChannel := pointer(aTransportChannel);
      fCloneMessage := False;
    end;
    

    至此,一个 TRORemoteService对象 将

    代理类 TFirstSampleService_Proxy,消息格式 TROBinMessage,传输通道TROWinInetHTTPChannel 联合在一起,

    程序启动时执行的 fFirstService := (RORemoteService as IFirstSampleService); 就已经完成了这么多

    接下来看接口函数的调用

    {unit FirstSampleClientMain;}
    
    procedure TFirstSampleClientMainForm.GetButtonClick(Sender: TObject);
    begin
      NamesBox.Items.CommaText := fFirstService.Nicknames(eFullname.Text);
    end;
    

    在客户端,接口IFirstSampleService的实现是在TFirstSampleService_Proxy类实现的,

    而fFirstService := (RORemoteService as IFirstSampleService);已经完成了对象的创建,

    所以fFirstService.Nicknames(eFullname.Text);实际调用的是TFirstSampleService的Nicknames函数

    function TFirstSampleService_Proxy.Nicknames(const FullName: UnicodeString): UnicodeString;
    begin
      try
        __Message.InitializeRequestMessage(__TransportChannel, 'FirstSample', __InterfaceName, 'Nicknames');
        __Message.Write('FullName', TypeInfo(UnicodeString), FullName, []);
        __Message.Finalize;
    
        __TransportChannel.Dispatch(__Message);
    
        __Message.Read('Result', TypeInfo(UnicodeString), result, []);
      finally
        __Message.UnsetAttributes(__TransportChannel);
        __Message.FreeStream;
      end
    end;
    

     __Message是TROProxyr的一个属性,

    {unit uROClient;}
    
    function TROProxy._GetMessage: IROMessage;
    begin
      result := IROMessage(fMessage);
    end;
    

    从代码可以看出,其实就是代理类创建时从RORemoteService.Message传入,也就是窗体上的 ROBinMessage,

    同理 __TransportChannel 就是窗体上的 ROWinInetHTTPChannel 。

    TROBinMessage的基类是TROMessage,而Write,Read在TROMessage是虚函数

    {unit uROClient;}
    
    {TROMessage}
    
    procedure Write(const aName : string; aTypeInfo : PTypeInfo; const Ptr; Attributes : TParamAttributes); virtual;
    procedure Read(const aName : string; aTypeInfo : PTypeInfo; var Ptr; Attributes : TParamAttributes); virtual;
    

    所以实际调用的是TROBinMessage的Write 和 Write

    而TROBinMessage的Write 和 Write实现了二进制格式的读写,此处略出实现代码

    TROSOAPMessage的Write 和 Write实现了SOAP格式的读写

    所以当TRORemoteService 绑定的 Message 是 TROBinMessage 时,消息就会按 二进制格式读写,

    当TRORemoteService 绑定的 Message 是 TROSOAPMessage时,消息就会按 SOAP格式读写,

    相同的道理,TROTransportChannel 的子类 TROIndyUDPChannel, TROWinInetHTTPChannel

    实现了在不同方式连接时的实现过程,这部分实际是调用了底层通讯

    客户端TROMessage的Write函数将函数名及参数按照一定格式打包

    TROTransportChannel的Dispatch把TROMessage发送到服务端并等待返回,

    服务端将返回结果写入TROMessage

    客户端TROMessage的Read函数再解包把结果读取

    这就是客户端调用服务端接口的基本过程

    由于RemObject SDK封闭得相当好,所以了解客户端调用服务端接口的过程,关键是意识到

    代理类,消息格式类,通道传输类 是如何被联结在一起的

  • 相关阅读:
    Docker简介安装与下载
    ActiveMq安装以及简单的测试
    HashMap源码解析<一>put()方法
    SQL语句查询练习题
    珍藏的数据库SQL基础练习题答案
    MySQL习题及答案
    Hadoop学习1
    数据库简单的实际应用
    数据库基础练习选择题
    数据库练习题
  • 原文地址:https://www.cnblogs.com/erp-system/p/3473030.html
Copyright © 2011-2022 走看看