我是一名有用的助手,可以为您翻译文本。
我有一个用于Win32的Delphi双层应用程序,其中实现了许多业务逻辑,这些逻辑都在一个“上帝对象”中。我想将其外包到一个单独的服务中。该服务应该通过TCP/IP Telnet样式协议由多个客户端访问。
我该如何使过渡变得简单?
具体来说,我想保持这种简单性:我希望只需定义每个函数一次。例如,如果我想要将PIN码登录功能添加到我的应用程序中,我只需要定义即可。
第三步,当客户端调用marshaller时,它会打包参数并将其发送到服务器:
此外,这种方法会破坏类型安全性,因为每个参数都作为变量传递。如果可能的话,我想保持编译时的类型安全。
也许可以自动生成客户端代码?我可以在服务器端编写“GetFunctionList()”和“GetFunctionPrototype(Name: string)”:
因此,每次需要更新客户端时,我只需从服务器重新查询所有函数原型,并自动生成marshaller代码。但这会混合编译和执行:我必须先编译服务器,然后启动它并查询其函数,然后构建客户端marshaller并在此之后仅重新编译客户端。太复杂了!
另一个选择是编写通用的marshaller函数,然后从所有客户端原型函数中调用它:
这让我省去了每次手动编写包装代码的麻烦(减少错误的机会),但仍需要我手动将服务器函数原型复制到客户端对象中。此外,编写这种通用的编组程序可能会是一场噩梦。
还有一种选择是使用RPC,但我不喜欢它,因为我需要重新定义IDL中的所有函数。而且Delphi的IDL编辑器很糟糕。对于OLE接口,Delphi强制生成“safecall”函数,这也很糟糕。除了检查每个RPC类的函数调用之外,没有办法实现自动检测断开连接和自动恢复连接功能:
我们回到了几个函数而不是一个。
那么,你们有什么建议?我错过了哪些其他选择?在Delphi中进行远程控制是否有常见的模式或现成的解决方案?
我有一个用于Win32的Delphi双层应用程序,其中实现了许多业务逻辑,这些逻辑都在一个“上帝对象”中。我想将其外包到一个单独的服务中。该服务应该通过TCP/IP Telnet样式协议由多个客户端访问。
我该如何使过渡变得简单?
具体来说,我想保持这种简单性:我希望只需定义每个函数一次。例如,如果我想要将PIN码登录功能添加到我的应用程序中,我只需要定义即可。
function Login(Username: string; PinCode: integer): boolean;
如果在服务器上有某个对象中的函数,那么我可以从客户端使用它而无需进行任何额外的工作。
最坏的情况是,我需要实现三个函数而不是一个。首先,在服务器上实现函数本身,其次,在网络上接收文本行的解码器,对其进行拆包并检查其有效性:
procedure HandleCommand(Cmd: string; Params: array of string);
begin
...
if SameText(Cmd, 'Login') then begin
CheckParamCount(Params, 2);
ServerObject.Login(
Params[0],
StrToInt(Params[1])
);
end;
end;
第三步,当客户端调用marshaller时,它会打包参数并将其发送到服务器:
function TServerConnection.Login(Username: string; PinCode: integer): boolean;
begin
Result := StrToBool(ServerCall('Login '+Escape(Username)+' '+IntToStr(PinCode)));
end;
显然,我不想要这个。
到目前为止,我已经成功摆脱了unmarshaller。通过使用Delphi RTTI,我编写了一个通用的unmarshaller,它会查找名称相同的已发布方法,检查参数并调用它。
现在,我只需要向服务器对象添加一个已发布的方法,就可以从telnet中调用它了:
function Login(Username: string; PinCode: integer): boolean;
> login john_locke
Missing parameter 2 (PinCode: integer)!
那么我该如何编写一个marshaller呢?我不能动态获取服务器函数列表并将函数添加到客户端对象中。 我可能可以保留一些动态的伪函数集合,但这样会让我的客户端调用变得难看:
ServerConnection.Call('Login', [Username, Password]);
此外,这种方法会破坏类型安全性,因为每个参数都作为变量传递。如果可能的话,我想保持编译时的类型安全。
也许可以自动生成客户端代码?我可以在服务器端编写“GetFunctionList()”和“GetFunctionPrototype(Name: string)”:
> GetFunctionList
Login
Logout
IsLoggedIn
> GetFunctionPrototype Login
function Login(Username: string; PinCode: integer): boolean;
因此,每次需要更新客户端时,我只需从服务器重新查询所有函数原型,并自动生成marshaller代码。但这会混合编译和执行:我必须先编译服务器,然后启动它并查询其函数,然后构建客户端marshaller并在此之后仅重新编译客户端。太复杂了!
另一个选择是编写通用的marshaller函数,然后从所有客户端原型函数中调用它:
procedure TServerConnection.GenericMarshaller(); assembler;
asm
//finds the RTTI for the caller function, unwinds stack, pops out caller params,
//packs them according to RTTI and sends to the server.
//receives the result, pushes it to stack according to RTTI, quits
//oh god
end;
function TServerConnection.Login(Username: string; PinCode: integer): boolean; assembler;
asm
call GenericMarshaller
end;
这让我省去了每次手动编写包装代码的麻烦(减少错误的机会),但仍需要我手动将服务器函数原型复制到客户端对象中。此外,编写这种通用的编组程序可能会是一场噩梦。
还有一种选择是使用RPC,但我不喜欢它,因为我需要重新定义IDL中的所有函数。而且Delphi的IDL编辑器很糟糕。对于OLE接口,Delphi强制生成“safecall”函数,这也很糟糕。除了检查每个RPC类的函数调用之外,没有办法实现自动检测断开连接和自动恢复连接功能:
function TServerWrapper.Login(Username: string; PinCode: integer): boolean;
begin
try
RealObject.Login(Username, Pincode);
except
on E: EOleException do
if IndicatesDisconnect(E) then
Disconnect;
Reconnect;
RealObject.Login(Username, Pincode);
end;
end;
我们回到了几个函数而不是一个。
那么,你们有什么建议?我错过了哪些其他选择?在Delphi中进行远程控制是否有常见的模式或现成的解决方案?