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 thenClasses.RegisterClass(aROMessageClass);
end;procedure RegisterTransportChannelClass(aTransportChannelClass : TROTransportChannelClass);
beginif _TransportChannels.IndexOf(aTransportChannelClass.ClassName)<0 then begin_TransportChannels.AddObject(aTransportChannelClass.ClassName, TObject(aTransportChannelClass));if Classes.GetClass(aTransportChannelClass.ClassName) = nil thenClasses.RegisterClass(aTransportChannelClass);end;
end;procedure RegisterProxyClass(const anInterfaceID : TGUID; aProxyClass : TROProxyClass);
var s : string;
begins := GUIDToString(anInterfaceID);if _ProxyClasses.IndexOf(s)<0then _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;}initializationRegisterMessageClass(TROBinMessage);{unit uROWinInetHttpChannel;}initializationRegisterTransportChannelClass(TROWinInetHTTPChannel);{unit FirstSample_Intf;}initialization//第一个函数是接口ID,第二个是接口的实现类RegisterProxyClass(IFirstSampleService_IID, TFirstSampleService_Proxy);

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

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

{unit FirstSampleClientMain;}constructor TFirstSampleClientMainForm.Create(aOwner: TComponent);
begininherited;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;
beginresult := inherited QueryInterface(IID, Obj);if (result <> S_OK) then begin//通过接口ID查询到接口实现类的引用proxyclass := FindProxyClass(IID, TRUE);if (proxyclass=NIL) then Exitelse beginCheckCanConnect(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;
beginresult := NIL;s := GUIDToString(anInterfaceID);idx := _ProxyClasses.IndexOf(s);if (idx>=0)then result := TROProxyClass(_ProxyClasses.Objects[idx])else beginif not Silentthen 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);
beginCreate(aRemoteService.Message, aRemoteService.Channel);fCloneMessage := aRemoteService.CloneMessage;
end;constructor TROProxy.Create(const aMessage: IROMessage;const aTransportChannel: IROTransportChannel);
begininherited 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);
beginNamesBox.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;
begintry__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;
beginresult := 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封闭得相当好,所以了解客户端调用服务端接口的过程,关键是意识到

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

转载于:https://www.cnblogs.com/erp-system/p/3473030.html

RemObjects(一)客户端远程调用服务端接口过程相关推荐

  1. xfire客户端获取xcf服务端接口解析问题

    1.java.lang.ClassCastException: org.apache.xerces.dom.DocumentImpl cannot be cast to java.lang.Strin ...

  2. 接口-服务端接口客户端接口

    记得刚工作就开始纠结接口这个问题,纠结到现在,总算是从头到尾摸索了一遍. 首先,服务端接口,是指我这个系统作为服务,然后其他的系统来调我.首先,接口,双方会定义一定的规范,即我这个系统,和调用我这个系 ...

  3. SpringBoot 服务端接口公网远程调试,并实现 HTTP 服务监听

    前后端分离项目中,在调用接口调试时候,可以通过cpolar内网穿透将本地服务端接口模拟公共网络环境远程调用调试,本次教程以Java服务端接口为例. 1. 本地环境搭建 1.1 环境参数 JDK1.8 ...

  4. app微信支付-java服务端接口 支付/查询/退款

    app微信支付-java服务端接口 支付-查询-退款 个人看微信的文档,看了很多前辈的写法,终于调通了,在这里做一下记录. 首先来定义各种处理类(微信支付不需要特殊jar包,很多处理需要自己封装,当然 ...

  5. 移动端与PHP服务端接口通信流程设计(基础版)

    为什么80%的码农都做不了架构师?>>>    针对 --->非开放性平台 --->公司内部产品 接口特点汇总: 1.因为是非开放性的,所以所有的接口都是封闭的,只对公司 ...

  6. 架构设计:远程调用服务架构设计及zookeeper技术详解(上篇)

    一.序言 Hadoop是一个技术生态圈,zookeeper是hadoop生态圈里一个非常重要的技术,当我研究学习hadoop的相关技术时候,有两块知识曾经让我十分的困惑,一个是hbase,一个就是zo ...

  7. Web Service 客户端,调用服务方法

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 只是最简单的调用web  service  服务,至于要传什么参数全看到业务了. 以下是最简单的调用 ...

  8. 远程过程调用失败_Java开发大型互联网RPC远程调用服务实现之问题处理方案

    引言 RPC(Remote Procedure Call Protocol)--远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.RPC协议假定某些传输协议 ...

  9. EJB3.0学习笔记---理解远程调用服务端和本地调用服务端的区别

    项目目的:理解远程调用服务端和本地调用服务端的区别 1.异常:       javax.ejb.EJBException: Local and Remote Interfaces cannot hav ...

最新文章

  1. inline函数返回值_C++知识补充-指针,const,函数指针,指针数组,运算符重载
  2. June:Datawhale开源学习小程序升级啦!
  3. Saltstack笔记
  4. 【动态规划专题】数字三角形模型
  5. uva-10152-乌龟排序
  6. 设计模式:享元(FlyWeight)模式
  7. 201119阶段二sqlite3
  8. 1818国民经济核算
  9. ZAB 协议和Paxos 算法
  10. 自定义控件常用方法总结
  11. pandas基础知识---2
  12. 先学python还是r-r语言和python学哪个?
  13. 查看mysql 当前锁级别_mysql innodb下的锁及隔离级别
  14. 分享一款上班摸鱼神器,再也不怕领导突然出现在身后了~
  15. drcom linux最新版,Drcom-client.org 上线暨新版 PUM v1.0 发布
  16. java全文检索word中的内容_搜索引擎时对WORD,EXCEL,PDF,POWERPOINT文件全文检索的总结...
  17. 我的面试心得与总结:BAT、网易、蘑菇街
  18. python特征提取方法_大师兄的Python机器学习笔记:特征提取
  19. CSS相对地址与绝对地址
  20. C语言求满足条件的xyz,c++编程,已知有式子:xyz+yzz=532, 其中x、y、z为数字,编写程序输出所有满足条件的x、y和z。...

热门文章

  1. 微信页面在浏览器打开
  2. HTML5七夕情人节表白网页(幻化3D相册) HTML+CSS+JavaScript 求婚示爱代码 520情人节告白代码 程序员表白源码 3D旋转相册 js烟花代码 css爱心表白
  3. JAVA小tips--Scanner.nextLine()与Scanner.next()及其他的差别
  4. C++11 FAQ中文版
  5. ORAN专题系列-15:2020年最新O-RAN商业和技术进展深入观察-2
  6. 少儿编程教育培训机构加盟
  7. OpenGL 超级宝典笔记 —— 纹理高级(一)
  8. c语言学生成绩管理系统设计分析,参阅:C语言学生成绩管理系统设计
  9. 怎么样把亚马逊EC2的帐号清除干净
  10. Microsoft Word 教程:如何在 Word 中插入艺术字?