转:RealThinClient LinkedObjects Demo解析
这个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解析的更多相关文章
- IOS CoreData 多表查询demo解析
在IOS CoreData中,多表查询上相对来说,没有SQL直观,但CoreData的功能还是可以完成相关操作的. 下面使用CoreData进行关系数据库的表与表之间的关系演示.生成CoreData和 ...
- Ionic Demo 解析
Ionic Demo 解析 index.html 解析 1.引入所需要的类库 <link rel="manifest" href="manifest.json&qu ...
- android报表图形引擎(AChartEngine)demo解析与源码
AchartEngine支持多种图表样式,本文介绍两种:线状表和柱状表. AchartEngine有两种启动的方式:一种是通过ChartFactory.get***View()方式来直接获取到view ...
- Android蓝牙联机Demo解析
写在前面: 手游的双人对战实现方式有很多,比如: 联网对战(需要一个服务器负责转发客户端请求,各种大型手游的做法) 分屏对战(手机上下分屏,典型的例子就是切水果的双人对战) 蓝牙联机对战(通过蓝牙联机 ...
- 人脸识别Demo解析C#
概述 不管你注意到没有,人脸识别已经走进了生活的角角落落,钉钉已经支持人脸打卡,火车站实名认证已经增加了人脸自助验证通道,更别提各个城市建设的『智能城市』和智慧大脑了.在人脸识别业界,通常由人脸识别提 ...
- SpringBoot使用activiti自定义流程demo解析
环境搭建[这里直接讲解自定义流程] 集成 Activiti Modeler 下载源码 我这里选用的是 Activiti 5.23.0 版本的页面,下载 zip,解压 Activiti 5.23.0 源 ...
- Tensorflow 的Word2vec demo解析
简单demo的代码路径在tensorflow\tensorflow\g3doc\tutorials\word2vec\word2vec_basic.py Sikp gram方式的model思路 htt ...
- Flux Demo解析
最近学习了阮一峰老师的博文 "Flux入门教程",博文中详细介绍了Flux框架和Controller view模式,并提供了Demo,受益匪浅. 现特参考阮老师的Demo,绘制了一 ...
- SpringMVC小demo解析
第一次实际接触SpringMVC,之前在教程网站上看得是概念性的. SpringMVC是属于Java框架SSM中的一环 在做了一个小demo后发现原来编程如此简单. 首先建立动态网页项目(Dynami ...
随机推荐
- UIkit框架之UIPickerView
1.继承链:UIview:UIResponder:NSObject 2.获取uipicker view的属性 (1)@property(nonatomic, readonly) NSIntegernu ...
- 原生javascript加载运行
原生javascript加载运行 (function(){ //TODO sometings }()); 在要运行相应代码的位置加入script标签,创建函数并自执行; 关于window.onload ...
- 福州月赛2057 DFS
题意:告诉你族谱,然后Q条查询s和t的关系,妈妈输出M,爸爸输出F: 题目地址:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=78233# ...
- JavaScript取子串方法slice,substr,substring对比表
在程序语言中,字符串可以说是最常用的一种类型,而在程序中对字符串的操作也是十分频繁.当程序语言自带多种字符串操作的方法时,用该语言编程程序时就有很多的便利性,提高开发的效率.但是当方法过多,甚至目的相 ...
- Git 一次性 pull push 所有的分支
/********************************************************************************* * Git 一次性 pull pu ...
- Jrebel6.3.3破解,配置图文教程
JRebel是个很好的开发工具,我在网上找了好久都没有找到很详细的教程,破解与配置教程千篇一律,步骤不详细.编写这篇教程,综合网络上的知识,加上了自己的理解与详细图文步骤. 安装 一般最新的插件是没有 ...
- Java笔记2-数据类型,变量,Java运算符
我们编写软件,目的是为了高效的操作(增,删,改,查)数据. 数据类型 1.基本类型(8种)byte 字节型 -128~127short 短整型 -32768~32767int 整型 -21474836 ...
- c数据结构栈的基本操作(字符逆序输出)
线性栈 输入字符,再输出 #include "stdafx.h" #include<stdlib.h> #include<malloc.h> #define ...
- 如何在一个网站或者一个页面规划JS
规划主要分为两部分:1.JS的分层,2.Js的规划 1.JS的分层(功能) 1-1.底层的库 : jquery 1-2.组件(ui) : 比如拖拽等,模块之间没有必然的联系,可以重复利用 1-3. ...
- python 杨辉三角
前提:端点的数为1. 每个数等于它上方两数之和. 每行数字左右对称,由1开始逐渐变大. 第n行的数字有n项. 第n行数字和为2n-1. 第n行的m个数可表示为 C(n-1,m-1),即为从n-1个不同 ...