`
neo
  • 浏览: 265581 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

PHPRPC for Delphi 中自定义类型传输详解

阅读更多
转自 http://coolcode.cn/?action=show&id=317

PHPRPC for Delphi 中除了支持基本数据类型、容器类型传递以外,同样也支持自定义类型的传递,而能够进行传递的自定义类型的基类就是 TPHPObject。这个类是在 PHPRPC 单元中定义的。下面我们就对这个类进行一下深入的剖析。

首先第一个问题,为什么要以 TPHPObject 作为 PHPRPC 传输的自定义类型的基类,而不是 TPersistent 呢?

因为 PHPRPC 支持传输的数据类型除了对象类型外,还有基本数据类型,为了可以使它们用同一种方式传输,最简单的方式就是把他们都变成 Variant 类型。而 TPersistent 类是不能转换成 Variant 类型的,而且 TPersistent 提供的持久化方式也与 PHPRPC 所支持的序列化方式不同,因此才没有使用 TPersistent,而是通过定义一个新的类 TPHPObject 作为 PHPRPC 支持的可传输自定义类型的基类。

TPHPObject 有一个无参 Create 方法,因为在反序列化该对象时,是需要调用这个无参构造方法来创建对象的。另外,它还有一个继承自 TComponent 的带有 AOwner 参数的 Create 方法。一般情况下,无需覆盖这两个方法,除非你需要初始化一些数据。

该类中提供的其它方法如果没有特殊需求,一般也都不需要覆盖,只需要定义需要序列化的属性就可以了,例如:

Delphi/Pascal代码

00.TUser = class(TPHPObject)   
01.private   
02.  FId: Integer;   
03.  FName: AnsiString;   
04.  FPassword: AnsiString;   
05.  FBirthday: TDateTime;   
06.published   
07.  property ID: Integer read FId write FId;   
08.  property Name: AnsiString read FName write FName;   
09.  property Password: AnsiString read FPassword write FPassword;   
10.  property Birthday: TDateTime read FBirthday write FBirthday; 
11.end; 
这个类中,ID,Name,Password 和 Birthday 这四个属性在传输时就会自动被序列化了,如果某个属性你不希望它被序列化(比如它是一个只读属性),使用 stored 指令将其设置为 False 就可以了。注意,要序列化的属性必须是 published 的,而不能是 public 的。

另外,TPHPObject 有一个 Properties 属性(它不是 published 的,所以无需担心它在传输时会被一起序列化),通过该属性,你可以通过属性名的字符串表示来访问属性值,返回值都是 Variant 类型的。

是否这样定义之后就可以了在 PHPRPC 中传输它了呢?不,还需要做一步小工作,那就是注册它。TPHPObject 提供了一个类方法 RegisterClass 用于注册类自身。例如:

Delphi/Pascal代码

00.TUser.RegisterClass('User'); 
就将 TUser 类注册成了别名为 User 的类。

那这个别名是干啥的呢?因为在其它语言中定义类的命名规则可能跟 Delphi 中不同,例如 PHP 或者 .NET 中,是没有在类名前加 T 这个约定的,但是在 Delphi 的类基本上都是以 T 开头的,为了能够跟其它语言互通,所以就提供了这样一个别名机制,注册之后就可以跟其它语言中的具有同样属性(或字段)的 User 类进行交互了。但是在 Delphi 中,你仍然使用的是 TUser 这个类。

另外,向 .NET 有名空间的概念,Java 也有包的概念,比如你定义的 TUser 如果要跟 .NET 或 Java 中的 myNameSpace.mySubNameSpace.User 那么在注册时,注册为:

Delphi/Pascal代码

00.TUser.RegisterClass('myNameSpace_mySubNameSpace_User'); 

就可以了,注意这里把 . 变成了 _。

如果类名与其它语言相同是否可以省略这个注册过程呢?不可以的,因为在反序列化时只有注册过的类才能被查找到,否则就不能够被反序列化了(简单的说就是没有经过注册的类可以作为参数传出,但不能作为结果返回)。不过这种同名的注册可以简化一下,直接执行不带参数的 RegisterClass 方法就可以了。1

如何创建 TUser 的对象呢?当然你可以使用 Create 来创建了,但是这样创建的对象是一个普通的对象,而不是一个 Variant 对象。那么怎样才能将它变成一个 Variant 对象呢?TPHPObject 提供了两种方式。一种是使用 New 方法(注意:这不是一个运算符),另一种是使用 ToVariant 方法。实际上 New 方法所做的就是在调用了 Create 方法之后又调用了 ToVariant 方法。因此如果你定义了带参的 Create 构造方法,可以自己同时定义一个带参的 New 方法,这样创建可传递的 Variant 对象就方便多了。

不过有一点大家要注意,Delphi 是没有自动垃圾回收机制的,因此当你创建了对象之后,在你不再需要它时,记得调用 Free 方法将它释放。TPHPObject 的 Variant 类型,在赋值时或传参时是引用传递的,而不是克隆对象,因此在你 Free 之后,它的所有副本也都不可以再用。

那如何将一个 Variant 表示的 TPHPObject 子类对象变成一个普通对象呢?也很简单,TPHPObject 提供了一个 FromVariant 类方法,可以用它来完成这个工作,例如:

Delphi/Pascal代码

00.var  
01.  user: TUser;  
02.  vuser: Variant;  
03.begin  
04.  vuser := TUser.New;  
05.  user := TUser(TUser.FromVariant(vuser));  
06.  user.Free;  
07.end; 

上面这个例子中,你发现我是在 user 上调用的 Free 方法,实际上在 vuser 上调用 Free 方法也可以得到同样的效果。

那是否可以给这种自定义的类型添加我们自己的方法呢?当然可以。但是你可能会发现,通过 Create 创建出来的对象,可以调用你加的这些方法,而通过 New 创建出来的 Variant 对象却不能像上面的 Free 方法那样调用。如何才能使我们自己定义的方法也可以在 Variant 对象上直接被调用呢?也很简单,TPHPObject 有两个保护方法:DoFunction 和 DoProcedure,只要覆写(override)它们就可以了。下面是 TStringBuffer 中对这两个方法的覆写(override):

Delphi/Pascal代码

00.function TStringBuffer.DoFunction(var Dest: TVarData; const Name: string; 
01.  const Arguments: TVarDataArray): Boolean; 
02.var 
03.  Ident: string; 
04.begin 
05.  Ident := LowerCase(Name); 
06.  Result := True; 
07.  if Ident = 'readstring' then 
08.    Variant(Dest) := ReadString(Variant(Arguments[0])) 
09.  else if Ident = 'seek' then 
10.    Variant(Dest) := Seek(Variant(Arguments[0]), Variant(Arguments[1])) 
11.  else 
12.    Result := inherited DoFunction(Dest, Name, Arguments); 
13.end; 
14. 
15.function TStringBuffer.DoProcedure(const Name: string; 
16.  const Arguments: TVarDataArray): Boolean; 
17.var 
18.  Ident: string; 
19.begin 
20.  Ident := LowerCase(Name); 
21.  Result := True; 
22.  if Ident = 'insertstring' then 
23.    InsertString(RawByteString(Variant(Arguments[0]))) 
24.  else if Ident = 'writestring' then 
25.    WriteString(RawByteString(Variant(Arguments[0]))) 
26.  else 
27.    Result := inherited DoProcedure(Name, Arguments); 
28.end; 
大家可以以这个为模板在在自己的类中覆写(override)它们,就可以直接以 Variant 对象的形式来直接调用自定义的方法了。

如果有特殊需求的话,你可能会覆写的方法大约有这几个:__sleep,__wakeup,ToBoolean,ToDate,ToDouble},ToInt64,ToInteger 和 ToString。其中,__sleep 和 __wakeup 与序列化和反序列化有关。ToXXX 的方法是用来进行类型转换的。一般情况下,你自定义的类是不会转换为这些类型的(ToString 也许是个例外)。

__sleep 方法的返回值是一个 TStringDynArray 类型,它用来做一些序列化之前的工作。如果它的返回值为空(nil),则按照默认的方式(就是前面介绍的方式)序列化对象,如果它的返回值不为空,则只序列化那些与返回值中包含的名称相同的属性(当然这些属性必须要出现在 published 声明部分,否则就会出错了)。

__wakeup 方法是在反序列化之后被调用,它没有参数也没有返回值,它一般用于反序列化之后的扫尾工作。

ToBoolean,ToDate,ToDouble,ToInt64 默认是抛出异常,当然它们都是 protected 的方法,如果你不覆写它们,你也不会调用到它们。

ToInteger 也是 protected 方法,它返回的是对象的指针地址的整数表示,不要改写它,它是有特殊用途的。

ToString 默认是返回该对象序列化后的 AnsiString 表示,你当然可以把它覆写成你需要的形式,覆写它不会影响序列化的正常工作。

另外,还有一些虽然也是 virtual 的方法,但是你一定不要去试图覆写它们,除非你知道你在做什么。这些方法包括 DoSerialize,DoUnSerialize,GetProperty,SetProperty,Equal,HashCode 和 ToVariant。

最后,再补充说明一下 ISerializable 接口。如果你的自定义类型继承自 TPHPObject,同时实现了 ISerializable 接口的话,那么这个类型就可以使用你自定义的序列化方式传输了。关于这种自定义序列化方式,可以参见 PHP 手册中关于 Serializable 接口的说明,它们的意义和实现的功能是完全相同的,这里就不再详细叙述了,因为你可能一辈子都不会用到它。
分享到:
评论

相关推荐

    phprpc_3.0_delphi.zip_Free!_PHPRPC delp_PHPRPC for delp_delphi

    PHPRPC for Delphi 对自定义类型的支持 另外,PHPRPC 还提供了 Lazarus(Free Pascal)版本,Lazarus 与 Delphi 很像,你可以认为它是一个开源版本的 Delphi。只不过现在的 Lazarus 还没有 Delphi 那样稳定。PHPRPC...

    phprpc_3.0.1_delphi

    在这个特定的版本中,它针对Delphi开发环境进行了优化,使得Delphi开发者也能轻松地利用RPC技术。 在RPC框架中,服务提供者(Server)会暴露一组可调用的函数或方法,而服务消费者(Client)则可以通过网络调用这些...

    phprpc_3.0.2_delphi_Edit_XE2

    在这个特定的版本"phprpc_3.0.2_delphi_Edit_XE2"中,重点是针对Embarcadero Delphi开发环境的一个定制编辑版,特别适配于XE2版本。这表明该版本是为使用Delphi XE2的开发者设计的,旨在方便他们在PHP和Delphi之间...

    Windows编程-Windows RPC 传递自定义数据类型、自定义数据类型数组、指针数组 示例demo

    https://blog.csdn.net/qq_29542611/article/details/88880215 Windows编程-Windows RPC 传递自定义数据类型、自定义数据类型数组、指针数组 示例demon

    phprpc for .net

    phprpc for .net 源代码都有,可修改

    自定义rpc思路设计

    自定义rpc框架 ,用到了nettty zookeeper等等,模仿dubbo

    phprpc for java 服务器实例

    同时,它的扩展性允许开发者添加自定义的序列化器、网络传输层等,以适应不同的项目需求。 通过以上步骤,你已经了解了如何在Java中使用Phprpc创建服务器实例,并从PHP客户端进行调用。在实际开发中,可以结合具体...

    phprpc中文文档

    Phprpc是一个轻量级的PHP远程过程调用(RPC)框架,它允许开发者在不同的PHP进程中进行通信,实现服务间的高效数据交换。这个中文文档是对于Phprpc框架的详细解释,对于初学者和进阶用户都非常有用。文档包含了框架...

    PHPRPC3.0分享

    在IT行业中,RPC技术是分布式系统中的关键组件,它允许一个程序调用另一个运行在不同地址空间(可能在另一台机器上)的程序,就像是在本地调用一样。PHPRPC3.0特别强调了性能和安全性,使得开发者可以轻松地构建跨...

    RemObjects Samples for Delphi

    通过研究 "RemObjects Samples for Delphi" 中的代码,Delphi 开发者可以深入理解如何在实际项目中运用 RemObjects 技术,提升他们的分布式应用开发能力。这些示例不仅是学习工具,也是解决特定问题的起点,可以帮助...

    基于IOCP的远程函数调用(RPC)

    **基于IOCP的远程函数调用(RPC)详解** 远程过程调用(RPC)是一种分布式计算技术,允许程序在不同的地址空间(如不同计算机或网络中的不同进程)间调用功能,就像是调用本地函数一样方便。IOCP(I/O完成端口)是...

    自定义RPC的Java实现

    ### 自定义RPC的Java实现详解 #### 一、引言 在现代软件开发中,分布式系统变得越来越普遍,其中远程过程调用(RPC)技术因其简单高效的特点而在跨网络节点间进行服务通信方面发挥着重要作用。本文将详细介绍如何...

    netty自定义rpc实现

    在IT行业中,RPC(Remote Procedure Call)是一种分布式计算技术,允许程序在不同的网络节点上像调用本地函数一样调用远程计算机上的函数或方法。本主题聚焦于如何使用Netty框架自定义RPC实现,结合了Zookeeper作为...

    PHPRPC .Net服务端+客户端

    标签中的"PHPRPC For ."可能表示这个版本是专门为.NET环境设计的,这意味着它可以与其他.NET语言(如C#、VB.NET等)无缝集成。 在实际应用中,PHPRPC可以用于以下场景: 1. **混合架构**:在PHP和.NET项目并存的...

    解决Delphi XML-RPC 中文乱码、结构/数组等没有解析I4项BUG

    最近要用XML-RPC机制实现delphi程序与Qt程序之间的通信,从...下载Delphi XML-RPC 后发现中文字符串会出现乱码,跟踪代码后发现XML-RPC默认的 字符编码是UTF-8,而且QT也都是用UTF-8编程,故把传输字符串改为UTF-8就行了。

    修改phprpc源码以支持集合类的string类型的转换

    在这个主题中,“修改phprpc源码以支持集合类的string类型的转换”涉及到对Phprpc框架的源代码进行定制化改造,以适应处理集合类中的字符串类型转换需求。在IT行业中,这样的改造通常是出于特定业务场景的需求,比如...

    自定义rpc框架

    2. **序列化与反序列化**:数据在网络中传输前需要序列化,到达目的地后再反序列化。选择合适的序列化框架,如protobuf、Hessian或JSON,对性能有直接影响。 3. **服务注册与发现**:服务提供者在启动时向Zookeeper...

    php rpc远程过程调用

    PHP RPC是PHP实现的RPC框架,它简化了在分布式系统中进行跨网络通信的过程。 在Web服务中,通常使用SOAP或REST等标准协议来实现跨语言和平台的通信。然而,这些方法虽然功能强大,但它们的复杂性可能导致开发和维护...

    PHPRPC asp.net的结合!

    PHPRPC基于HTTP协议,使用自定义的数据编码格式,通过HTTP请求进行数据传输。在ASP.NET应用中,可以通过HTTP客户端发送请求到PHP服务器,PHP服务器处理请求后返回结果,再由ASP.NET应用解析并处理。这种模式使得...

    ENVI自定义RPC文件图像正射校正及数据

    在ENVI中,用户可以自定义RPC文件,以适应特定传感器的几何特性。 执行ENVI自定义RPC文件图像正射校正的步骤如下: 1. **导入数据**:打开ENVI软件,加载待校正的遥感图像,确保图像文件与对应的RPC文件在同一目录...

Global site tag (gtag.js) - Google Analytics