这篇文章值得认真学习和动手做个小程序跑一下:
以下是根据RemObject的远程方法调用原理实现的简单远程方法调用的内容及代码:
因为在RemObject中,调用远程方法需要三个单元,即:XXX_Intf,XXX_Invk,XXX_Impl
(1)XXX_Intf主要是服务接口声明和代理类定义及实现。
(2)XXX_Invk主要是实现了具体的方法执行
(3)XXX_Impl主要是服务接口的具体实现
因为本人比较懒惰,没有像RemObject那样分的那么具体。本文主要讲述远程方法调用的简单实现,让大家可以看出调用的原理,而且实现的代码简 单,大家可以通过阅读代码就可以看出相关的原理。
以下是具体的代码:
客户端通过代理类调用
procedure TForm1.Button1Click(Sender: TObject);
var
iA, iB, iC: Integer;
obj: TMathFunc_Proxy; //代理类
begin
iA := StrToInt(Edit1.Text);
iB := StrToInt(Edit2.Text);
obj := TMathFunc_Proxy.Create;
try
iC := obj.Sum(iA, iB);
finally
FreeAndNil(obj);
end;
Edit3.Text := IntToStr(iC);
end;
代理类负责构造传递和接收处理与远程服务的交互信息:
function TMathFunc_Proxy.Sum(const A, B: Integer): Integer;
var
strModuleName,
strInterfaceName,
strMethodName: string;
DNAMessage: TDNAMessage;
begin
//创建消息交互对象
DNAMessage := TDNAMessage.Create;
try
//创建新的方法调用消息
DNAMessage.CreateMessage('Test', 'MathFunc', 'Sum');
//往消息中写入参数值
DNAMessage.Write('A', TypeInfo(Integer), A);
DNAMessage.Write('B', TypeInfo(Integer), B);
//分发传递消息
DNAMessage.Distributed(DNAMessage.MessageStream);
//从方法请求结果消息返回的消息中提取相关结果
DNAMessage.Read('', TypeInfo(string), strModuleName);
DNAMessage.Read('', TypeInfo(string), strInterfaceName);
DNAMessage.Read('', TypeInfo(string), strMethodName);
DNAMessage.Read('Result', TypeInfo(Integer), Result);
finally
//销毁方法调用消息
if Assigned(DNAMessage.MessageStream) then
DNAMessage.DestroyMessage;
//销毁消息交互对象
FreeAndNil(DNAMessage);
end;
end;
TDNAMessage类负责消息的序列化和反序列化过程,不过本文中因为没有具体的去实现TCP等协议的通信管道,所以直接在 TDNAMessage类的方法Distributed中模拟了整个调用过程中的客户端发送 -> 服务端接收、处理、返回 -> 客户端接收、处理的过程
方法原型:
procedure Distributed(aStream: TStream);
代码:
procedure TDNAMessage.Distributed(aStream: TStream);
var
strModuleName,
strInterfaceName,
strMethodName: string;
iA, iB, iC: Integer;
obj: TMathFunc;
begin
//在这里我们可以写把方法请求交互消息传递给远程服务
{ TODO -omichelsn -c : 2007-10-11 8:45:24 }
//此处代码模拟远程服务在接收到客户端发送的方法请求交互消息及相应处理过程
//-------------------------------------------------------------Begin
aStream.Position := 0;
//读取要调用的模块/接口/方法名称等信息
Read('', TypeInfo(string), strModuleName);
Read('', TypeInfo(string), strInterfaceName);
Read('', TypeInfo(string), strMethodName);
//读取要调用的方法的参数信息
Read('A', TypeInfo(Integer), iA);
Read('B', TypeInfo(Integer), iB);
//调用具体的方法来计算
obj := TMathFunc.Create;
try
iC := obj.Sum(iA, iB);
finally
FreeAndNil(obj);
end;
//创建法请求交互结果消息
CreateMessage(strModuleName, strInterfaceName, strMethodName);
//写入方法调用的结果值
Write('Result', TypeInfo(Integer), iC);
aStream.Position := 0;
//------------------------------------------------------------End
//在这里我们可以写把方法请求交互结果消息传递给客户端服务
{ TODO -omichelsn -c : 2007-10-11 8:45:16 }
//在这里我们可以写客户端接收远程服务发送的方法请求交互结果消息
{ TODO -omichelsn -c : 2007-10-11 8:46:48 }
end;
以下是全部的源代码:
Unit1单元(客户端调用单元)
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, TypInfo, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
Unit2;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
iA, iB, iC: Integer;
obj: TMathFunc_Proxy;
begin
iA := StrToInt(Edit1.Text);
iB := StrToInt(Edit2.Text);
obj := TMathFunc_Proxy.Create;
try
iC := obj.Sum(iA, iB);
finally
FreeAndNil(obj);
end;
Edit3.Text := IntToStr(iC);
end;
end.
Unit2单元(类似RemObject中的XXX_Intf,XXX_Impl)
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes;
type
//MathFunc接口
IMathFunc = interface(IUnKnown)
['{30A624D5-4DA0-43FF-B210-5D5BCAFC5593}']
function Sum(const A, B: Integer): Integer; stdcall;
end;
//MathFunc实现类
TMathFunc = class(TInterfacedObject, IMathFunc)
public
function Sum(const A, B: Integer): Integer; stdcall;
end;
//MathFunc代理类
TMathFunc_Proxy = class(TInterfacedObject, IMathFunc)
public
function Sum(const A, B: Integer): Integer; stdcall;
end;
implementation
uses
Unit3;
{ TMathFunc }
function TMathFunc.Sum(const A, B: Integer): Integer;
begin
Result := A + B;
end;
{ TMathFunc_Proxy }
function TMathFunc_Proxy.Sum(const A, B: Integer): Integer;
var
strModuleName,
strInterfaceName,
strMethodName: string;
DNAMessage: TDNAMessage;
begin
//创建消息交互对象
DNAMessage := TDNAMessage.Create;
try
//创建新的方法调用消息
DNAMessage.CreateMessage('Test', 'MathFunc', 'Sum');
//往消息中写入参数值
DNAMessage.Write('A', TypeInfo(Integer), A);
DNAMessage.Write('B', TypeInfo(Integer), B);
//分发传递消息
DNAMessage.Distributed(DNAMessage.MessageStream);
//从方法请求结果消息返回的消息中提取相关结果
DNAMessage.Read('', TypeInfo(string), strModuleName);
DNAMessage.Read('', TypeInfo(string), strInterfaceName);
DNAMessage.Read('', TypeInfo(string), strMethodName);
DNAMessage.Read('Result', TypeInfo(Integer), Result);
finally
//销毁方法调用消息
if Assigned(DNAMessage.MessageStream) then
DNAMessage.DestroyMessage;
//销毁消息交互对象
FreeAndNil(DNAMessage);
end;
end;
end.
Unti3单元(本单元实现了消息创建、序列化和发送返回的过程,类似RemObject中的TROMessage, TROStreamSerializer,和TROSerializer等类的对消息创建、序列化、发送返回的实现过程)
unit Unit3;
interface
uses
SysUtils, Classes, TypInfo;
type
IDNAMessage = interface(IUnKnown)
//读-写整型参数值
procedure ReadInteger(aName: string; anOrdType : TOrdType; var Ptr);
procedure WriteInteger(aName: string; anOrdType : TOrdType; const Ptr);
//读-写字符串型参数值
procedure ReadString(aName: string; anOrdType : TOrdType; var Ptr);
procedure WriteString(aName: string; anOrdType : TOrdType; const Ptr);
//读写参数值入口方法
procedure Write(aName: string; aType : PTypeInfo; const Ptr);
procedure Read(aName: string; aType : PTypeInfo; var Ptr);
end;
TDNAMessage = class(TInterfacedPersistent, IDNAMessage)
private
fStream: TStream;
procedure ReadInteger(aName: string; anOrdType : TOrdType; var Ptr);
procedure WriteInteger(aName: string; anOrdType : TOrdType; const Ptr);
procedure ReadString(aName: string; anOrdType : TOrdType; var Ptr);
procedure WriteString(aName: string; anOrdType : TOrdType; const Ptr);
public
//创建新的方法调用消息
procedure CreateMessage(aModuleName, aInterfaceName, aMethodName: string);
//销毁方法调用消息
procedure DestroyMessage;
procedure Write(aName: string; aType : PTypeInfo; const Ptr);
procedure Read(aName: string; aType : PTypeInfo; var Ptr);
//分发传递消息
procedure Distributed(aStream: TStream);
//具体的交互消息
property MessageStream: TStream read fStream default nil;
end;
implementation
uses Unit2;
{ TDNAMessage }
procedure TDNAMessage.CreateMessage(aModuleName, aInterfaceName,
aMethodName: string);
begin
if Assigned(fStream) then
FreeAndNil(fStream);
fStream := TMemoryStream.Create;
fStream.Position := 0;
Write('', TypeInfo(string), aModuleName);
Write('', TypeInfo(string), aInterfaceName);
Write('', TypeInfo(string), aMethodName);
end;
procedure TDNAMessage.DestroyMessage;
begin
if Assigned(fStream) then
FreeAndNil(fStream);
end;
procedure TDNAMessage.Distributed(aStream: TStream);
var
strModuleName,
strInterfaceName,
strMethodName: string;
iA, iB, iC: Integer;
obj: TMathFunc;
begin
//在这里我们可以写把方法请求交互消息传递给远程服务
{ TODO -omichelsn -c : 2007-10-11 8:45:24 }
//此处代码模拟远程服务在接收到客户端发送的方法请求交互消息及相应处理过程
//-------------------------------------------------------------Begin
aStream.Position := 0;
//读取要调用的模块/接口/方法名称等信息
Read('', TypeInfo(string), strModuleName);
Read('', TypeInfo(string), strInterfaceName);
Read('', TypeInfo(string), strMethodName);
//读取要调用的方法的参数信息
Read('A', TypeInfo(Integer), iA);
Read('B', TypeInfo(Integer), iB);
//调用具体的方法来计算
obj := TMathFunc.Create;
try
iC := obj.Sum(iA, iB);
finally
FreeAndNil(obj);
end;
//创建法请求交互结果消息
CreateMessage(strModuleName, strInterfaceName, strMethodName);
//写入方法调用的结果值
Write('Result', TypeInfo(Integer), iC);
aStream.Position := 0;
//------------------------------------------------------------End
//在这里我们可以写把方法请求交互结果消息传递给客户端服务
{ TODO -omichelsn -c : 2007-10-11 8:45:16 }
//在这里我们可以写客户端接收远程服务发送的方法请求交互结果消息
{ TODO -omichelsn -c : 2007-10-11 8:46:48 }
end;
procedure TDNAMessage.Read(aName: string; aType: PTypeInfo; var Ptr);
begin
case aType^.Kind of
tkInteger: ReadInteger(aName, GetTypeData(aType)^.OrdType, Ptr);
tkLString, tkString: ReadString(aName, GetTypeData(aType)^.OrdType, Ptr);
end;
end;
procedure TDNAMessage.ReadInteger(aName: string; anOrdType : TOrdType;
var Ptr);
var sze : byte;
src : pointer;
begin
src := @Ptr;
sze := 0;
case anOrdType of
otSByte,
otUByte : sze := SizeOf(byte);
otSWord,
otUWord : sze := SizeOf(word);
otSLong,
otULong : sze := SizeOf(integer);
end;
fStream.ReadBuffer(src^, sze);
end;
procedure TDNAMessage.ReadString(aName: string; anOrdType: TOrdType;
var Ptr);
var
sze : integer;
begin
sze := 0;
fStream.ReadBuffer(sze, SizeOf(sze));
if (sze>0) then begin
SetLength(string(Ptr), sze);
fStream.ReadBuffer(string(Ptr)[1], sze);
end
else string(Ptr) := '';
end;
procedure TDNAMessage.Write(aName: string; aType: PTypeInfo; const Ptr);
begin
case aType^.Kind of
tkInteger: WriteInteger(aName, GetTypeData(aType)^.OrdType, Ptr);
tkLString, tkString: WriteString(aName, GetTypeData(aType)^.OrdType, Ptr);
end;
end;
procedure TDNAMessage.WriteInteger(aName: string; anOrdType : TOrdType;
const Ptr);
var
sze : byte;
src : pointer;
begin
{ ToDo: make sure a Integer is always marshaled as Int32 }
src := @Ptr;
sze := 0;
case anOrdType of
otSByte,
otUByte : sze := SizeOf(byte);
otSWord,
otUWord : sze := SizeOf(word);
otSLong,
otULong : sze := SizeOf(integer);
end;
fStream.Write(src^, sze);
end;
procedure TDNAMessage.WriteString(aName: string; anOrdType: TOrdType;
const Ptr);
var
sze : integer;
begin
sze := Length(string(Ptr));
fStream.Write(sze, SizeOf(sze));
if (sze > 0) then
fStream.Write(string(Ptr)[1], sze);
end;
end.