这个Demo源码实现比较怪,有点拗脑,原因估是作者想把控件的使用做得简单,而封装太多。

这里说是解析,其实是粗析,俺没有耐心每个实现点都查实清楚,看源码一般也就连读带猜的。

这个Demo表达出的意义,在于在HTTP访问方式下,很方便的实现了客户端和服务端相互的主动通讯能力,这在需要实时消息交互,控制交互,数据互传上非常有意义,非常给力。

一、Demo工作过程

1. 实现功能:实现了上传文件到HTTP服务端的能力,大文件上传成功率极高,并且始终保持非常少的内存占用量。

2. 工作过程:客户端和服务端的TRtcLinkedModule控件需建立通讯连接,在Demo中有两种方式,客户端先创建TRtcLinkedModule对象和服务端先创建TRtcLinkedModule对象,但无论哪一端先创建,都会引发对端也创建对象,从而使两对象的通讯连接建立。完成建立后,服务端把文件存储路径推送给客户端,客户端选择文件后,调用服务端Upload处理流程(这儿只能说是处理流程,而不是函数,因为TRtcLinkedModule对象的远程调用总是只触发对端的OnCallMethod事件,通过在Param中使用字符串标识,如“Upload”告诉服务端使用哪一个处理流程)开始上传,服务端在Upload中先判断是否文件已存在,不存在的话,直接调用客户端的“Get”流程让其上传数据,且每次“Get”调用只要求客户端发送10K数据,客户端准备好数据后调用服务端的“Put”把数据送到服务端并写入文件,“Put”过程又再调用“Get”,如此往复,直至完成全部数据上传。

二、实现原理和关键函数

1. TRtcLinkedModule控件

(1)此控件可放置在Form、DataModule、Frame这类容器上,一个容器只能有一个该控件!放置了该控件的模块即成为通讯模块,可以在通讯需要时由远端发起实例创建请求而创建(除了放控件外,当然还有几行代码的工作要做,下面讲述)。

(2)控件的字符串属性RemoteClass设置为远端的模块名字,这样当要远端创建Link模块进行通讯时,RTC会把此名字作为标识发送创建请求到远端,远端则根据名字创建正确的模块。

(3)成员函数CallMethod(const xMethodName: string)直接触发对端的OnCallMethod事件,事件中根据xMethodName确定处理方式,看起来一般用于数据块或复杂控制上。

(4)成员函数SetProp(const xPropName: string; xNow: boolean)、SetEvent(const xEventName: string)直接触发对端的OnSetProp和OnSetEvent事件,用于简单的短消息交互或控制交互。

(5)成员函数Broadcast(const xChannel, xMethodName: string)在本地模块内群播消息,xChannel为群播管道,管道由成员函数Subscribe(const xChannel: String)建立。

(6)控件使用相当简单,以上属性设置后就完成,不用管如何和http通信控件啊或ClientModule等远调控件进行连接,扔上就可用了(这也是让人比较疑惑的地方,后面有解析它是如何和http通信控件关联的)。

2. 函数RegisterRtcObjectConstructor(const xClassName:String; xProc:TRtcObjectConstructor):

(1)必需在initialization块执行,必需和UnregisterRtcObjectConstructor配对使用
   (2)这个函数用于指定一个能被远端调用的构造函数,用于能让远端在需要时创建TRtcLinkedModule对象及模块
   (3)函数的xClassName在TRtcLinkedModule控件中的RemoteClass中指定,xProc为一个procedure MakeObject(Sender:TObject; Param:TRtcObjectCall);类型的静态函数,这个函数就是为什么在需要时就能创建远端Link模块的关键了,创造需求到来时,根据RegisterRtcObjectConstructor函数的注册信息,找到xProc函数调用,OK,Link模块对象生成了,原理很简单吧。
   (4)另一个替代方案为可以在TRtcClientModule或TRtcServerModule控件中的OnObjectCreate事件中进行创建工作,但这需要两端使用此Module控件进行通讯。

函数意义:远端发送创建LinkObject通迅对象命令,本地端根据远端发来的xClassName字串在ObjectManager中找到注册的指定构造函数并调用。所以程序需要使用ActivateObjectManager创建好对象管理器。

3. 函数ActivateObjectManager(xCreate:boolean=True),TRtcDataServer和TRtcClientModule成员函数。

(1)在使用TRtcLinkedModule控件进行通讯前必需调用该函数,否则不能和远端通信,也不能正常发起远端模块创建请求。

(2)RtcClientModule1.ObjectLinks为“ol_Manual”必需手动调用,设置为“ol_AutoClient”或“ol_AutoBoth”则RTC会在后台自动调用。

忍不住插句对于远程函数调用架构的一点看法:

RemObject套件用的是看起来很符合常规理解的函数调用,让你感觉不到这是远程函数,为实现这样一个使用感受,RO用了比较复杂的技术来实现,不可否认,这样相当有含金量,不是谁都可以写得出来的,这种技术曾如此让我赞服并学习。而RTC的远程调用全部用字符串标识方式,如此的简单,实现起来不需要复杂的技术,大多有点经验的程序员基于TCP之类底层都可以做出来,而但多年后的现在,我才发现这种简单的美和高效,字符串可以描述从简单到极其复杂的构造,网页啊,xml啊这不都是这样描述的么,因而其灵活度可以说没有羁绊,要实现什么能实现什么如何实现只在乎用者一心。RTC的作者在编程思想境界上一定已是十分的修为深厚了。

三、奇怪的地方

按道理,TRtcLinkedModule控件要能进行通讯,必定要有设置通讯方式的地方,但找完TRtcLinkedModule的属性和Demo的源码,都没有发现有设置的地方,那它是怎么完成通讯的呢?

先看看TRtcLinkedModule的构造函数里都干了什么:

 1 constructor TRtcLinkedModule.Create(AOwner: TComponent);
2 begin
3 inherited Create(AOwner);
4 if not (csDesigning in ComponentState) then
5 begin
6 // This will only work if there is an active Object Manager for the current thread
7 FParam:=TRtcObjectCall.Create(GetRtcObjectManager);
8 if assigned(AOwner) then
9 FOLink:=TRtcBasicObjectLink.Create(AOwner,FParam.Manager)
10 else // If no Owner, we are the Object container
11 FOLink:=TRtcBasicObjectLink.Create(self,FParam.Manager);

看到了GetRtcObjectManager,这是个全局函数,根据线程ID获取对象管理器,ObjectManager估计是个全局的、唯一的单例模式的对象,懒得看代码了,猜的,哈哈。

ObjectManager估计是个LinkObject的管理器,用于Link控件发送的请求到来时找到正确的模块及正确的Link控件,然后调用对应的函数,现在的疑问是,ObjectManager怎么知道把请求发送到对应的Form和Link去呢?上面代码中FOLink:=TRtcBasicObjectLink.Create(AOwner,FParam.Manager),看到传入AOwner了!还有Manager,干啥呢?继续跟下去看看TRtcObjectLink.Create是什么:

 1 constructor TRtcObjectLink.Create(xOwner:TObject;xManager:TRtcObjectManager);
2 begin
3 inherited Create;
4 if xOwner=nil then
5 raise ERtcObjectLinks.Create('TRtcObjectLink.Create called with xOwner=nil');
6 if xManager=nil then
7 raise ERtcObjectLinks.Create('TRtcObjectLink.Create called with xManager=nil');
8 FSubs:=nil;
9 FOwner:=xOwner;
10 FManager:=xManager;
11 if FManager.CreatingObjectID<>0 then // remote constructor
12 begin
13 FOID:=FManager.CreatingObjectID;
14 FManager.CreatingObjectID:=0;
15 FCreator:=False;
16 end
17 else
18 begin
19 FOID:=FManager.GetNextObjectID;
20 FCreator:=True;
21 end;
22 FRemoteDestroyed:=False;
23 FManager.AddObject(FOID,self,FOwner);
24 end;

最后一句:FManager.AddObject(FOID,self,FOwner);很明显了,这儿TRtcObjectLink的对象标识FOID和自身对象指针Self及所在的窗口FOwner被传入,三个的对应关系都有了,ObjectManager要找到正确的模块那是很容易的事。而TRtcObjectLink对象内嵌于TRtcLinkedModule控件对象中,是组合关系,具体函数和事件调用是TRtcObjectLink的实现。

现在还有个问题是,Link对象是怎么得到通讯能力的?

注意到,RtcClientModule1.ActivateObjectManager(True);这个调用,RtcClientModule1是连接到TRtcHttpClient对象的,因此具备http通讯能力,再看看RtcClientModule1和ObjectManager是什么关系:

 1 procedure TRtcClientModule.ActivateObjectManager(xCreate:boolean=True);
2 begin
3 if FRelease then Exit;
4
5 if ObjectLinks=ol_None then
6 raise ERtcObjectLinks.Create('ActivateObjectManager: ObjectLinks = ol_None')
7 else if not assigned(FObjectManager) then
8 if xCreate then
9 begin
10 FObjectManager:=TRtcClientObjectManager.Create(False);
11 FObjectManager.BroadcastGroup:=String(FObjManSesName);
12 FObjectManager.OnDataReady:=DoObjectManagerDataReady;
13 end;
14
15 if not assigned(FObjectManager) then
16 raise ERtcObjectLinks.Create('ActivateObjectManager failed')
17 else
18 begin
19 SetRtcObjectManager(FObjectManager);
20 FObjectManager.ExecuteBroadcast(nil);
21 end;
22 end;

噢,原来这家伙发现没有ObjectManager的话就创建一个并持有,并且用SetRtcObjectManager送到一个全局对象fObjManagers中,这个对象是个列表,而GetRtcObjectManager正是从这个fObjManagers中获取,现在真相大白了,原来就是TRtcHttpClient控件收到请求转给TRtcClientModule对象,TRtcClientModule对象的私有成员FObjectManager保存着ObjectManager的对象引用,ObjectManager对象根据保存的Owner、Link对象关系找到正确的模块,调用Link对象相关函数。嗯,不过具体代码没有细看,以上真相有较大猜测成份,不过我相信道理上应对的,实质性实现上,请看到这篇文章的有心网友指出,批评指正,共同进步。

转:RealThinClient LinkedObjects Demo解析的更多相关文章

  1. IOS CoreData 多表查询demo解析

    在IOS CoreData中,多表查询上相对来说,没有SQL直观,但CoreData的功能还是可以完成相关操作的. 下面使用CoreData进行关系数据库的表与表之间的关系演示.生成CoreData和 ...

  2. Ionic Demo 解析

    Ionic Demo 解析 index.html 解析 1.引入所需要的类库 <link rel="manifest" href="manifest.json&qu ...

  3. android报表图形引擎(AChartEngine)demo解析与源码

    AchartEngine支持多种图表样式,本文介绍两种:线状表和柱状表. AchartEngine有两种启动的方式:一种是通过ChartFactory.get***View()方式来直接获取到view ...

  4. Android蓝牙联机Demo解析

    写在前面: 手游的双人对战实现方式有很多,比如: 联网对战(需要一个服务器负责转发客户端请求,各种大型手游的做法) 分屏对战(手机上下分屏,典型的例子就是切水果的双人对战) 蓝牙联机对战(通过蓝牙联机 ...

  5. 人脸识别Demo解析C#

    概述 不管你注意到没有,人脸识别已经走进了生活的角角落落,钉钉已经支持人脸打卡,火车站实名认证已经增加了人脸自助验证通道,更别提各个城市建设的『智能城市』和智慧大脑了.在人脸识别业界,通常由人脸识别提 ...

  6. SpringBoot使用activiti自定义流程demo解析

    环境搭建[这里直接讲解自定义流程] 集成 Activiti Modeler 下载源码 我这里选用的是 Activiti 5.23.0 版本的页面,下载 zip,解压 Activiti 5.23.0 源 ...

  7. Tensorflow 的Word2vec demo解析

    简单demo的代码路径在tensorflow\tensorflow\g3doc\tutorials\word2vec\word2vec_basic.py Sikp gram方式的model思路 htt ...

  8. Flux Demo解析

    最近学习了阮一峰老师的博文 "Flux入门教程",博文中详细介绍了Flux框架和Controller view模式,并提供了Demo,受益匪浅. 现特参考阮老师的Demo,绘制了一 ...

  9. SpringMVC小demo解析

    第一次实际接触SpringMVC,之前在教程网站上看得是概念性的. SpringMVC是属于Java框架SSM中的一环 在做了一个小demo后发现原来编程如此简单. 首先建立动态网页项目(Dynami ...

随机推荐

  1. 自己动手编译apache-tomcat-6.0.41-src源码

    第一步:下载apache-tomcat-6.0.41-src 第二步:阅读BUILDING.txt.了解所需要的步骤. In order to build a binary distribution ...

  2. UML学习笔记2

    4.协作图 它跟顺序图区别:前者强调时间,后者强调空间.两者可以转换 5.状态图 主要用于时间建模 6.活动图 7.构件图

  3. Java基础小总结

    1,Java事件处理机制 (1)三部分:事件源.事件(处理)对象.实现事件监听器: (2)事件处理程序:可以通过ActionEvent e,e.getSource确定是哪个事件触发了,然后通过类似JB ...

  4. 【LeetCode OJ】Flatten Binary Tree to Linked List

    Problem Link: http://oj.leetcode.com/problems/flatten-binary-tree-to-linked-list/ The problem is ask ...

  5. CentOS6 下rsync服务器配置

    一.rsync 简介 Rsync(remote synchronize)是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件,也可以使用 Rsync 同步本地硬盘中的不同目录. Rsy ...

  6. 认识web前端

    对于一个只是浅尝辄止c语言.学过汇编语言的我,思考了半年终于在这一天入了坑,学习web前端. web前端,看着这个名字好高大上,其实我目前的理解就是写页面,是各种图片动画文字在一个页面上呈现,再一点能 ...

  7. redis 集群环境搭建-redis集群管理

    集群架构 (1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽. (2)节点的fail是通过集群中超过半数的节点检测失效时才生效. (3)客户端与redi ...

  8. WCF: 没有终结点在侦听可以接受消息的 这通常是由于不正确的地址或者 SOAP 操作导致的。

    问题:     由于我这里的wcf服务是采用“BasicHttpBinding”的方式,即安全绑定模式,客户端在引用这个服务后所生成的终结点配置(endpoint )就变成了<endpoint ...

  9. LeetCode "Integer Break"

    A typical CS style DP based solution: class Solution(object): def __init__(self): self.hm = {} def i ...

  10. 【SVN】自动备份SVN仓库

    仓库的位置为:C:\xxx\SVNRepo\ MyCommonUtils MyStudyProject SVN仓库备份.bat '参考连接:http://www.uml.org.cn/pzgl/201 ...