双向 RPC






4.12/5 (17投票s)
2003年5月5日
3分钟阅读

103781

3333
如何使用 RPC 实现双向客户端/服务器过程调用。
引言
RPC 通常不直接使用。因此,当您使用 DCOM 在 www 上系统之间进行通信时,通常不必了解 RPC API。 但是 - 正如您稍后将看到的 - 有时未覆盖的 RPC 非常有用。
例如:在传统的 RPC 编程中,您有两个软件,每个软件都采用可区分的可执行形式。
app A : the client - send request to be served.
app B : the server - complies to the client request.
大多数情况下,这很好而且令人满意。 但是,为了实现这个目标 - 您不需要直接使用 RPC - 分布式 COM 已经足够了。
那么,什么时候才真正需要 RPC?
好吧,假设一个可执行文件交替扮演客户端或服务器的角色。 除此之外,假设客户端角色和服务器角色使用相同的接口和相同的功能。
困惑了吗?
不要!!!
解决方案
- 试用下载的演示项目,并仔细扫描源代码以获取更多信息...
- 在进行大试用之前,请阅读以下几行
解释
我假设您已经知道如何通过 MIDL 编译器生成 RPC 代码,因此我将仅解释需要进行的更改
- RPC 代码中的一些变量必须替换为相似的变量,这样就不会有重复的变量,因为客户端代码和服务器代码对三个变量使用相似的名称。
__MIDL_ProcFormatString replaced by __MIDL_ProcFormatString2 __MIDL_TypeFormatString replaced by __MIDL_TypeFormatString2 <interface_name>_StubDesc replaced by <interface_name>_StubDesc2
上面的更改应该只在源代码的客户端进行。
我们不能对客户端和服务器端使用这些变量的一个实例,因为在每种情况下,这些变量都有不同的值;
- 将所有需要参与 RPC 故事的文件添加到一个项目中,而不是像通常实现 RPC 那样 - 在两个不同的项目中。
- 此外,更改 RPC 故事中涉及的函数的名称
<func-name>(<parameters>) to <func-name>2(<parameters>).
这是演示项目中的一段代码。 服务器端接口实现了
GetName
函数。 因为客户端和服务器端都实现了以下功能void GetName( /* [size_is][string][out] */ unsigned char __RPC_FAR name[ ]) { .... }
...并且因为我们将客户端代码和服务器代码一起编译,所以我们必须更改服务器端中的 func-name 以避免重复,如下所示
void GetName2( /* [size_is][string][out] */ unsigned char __RPC_FAR name[ ]) { .... }
这是 MIDL 生成的函数 - 唯一的变化是内部函数
GetName
的名称(纯实现函数 - stub naked)。void __RPC_STUB details_GetName( PRPC_MESSAGE _pRpcMessage ) { MIDL_STUB_MESSAGE _StubMsg; unsigned char ( __RPC_FAR *name )[ ]; RPC_STATUS _Status; ((void)(_Status)); NdrServerInitializeNew( _pRpcMessage, &_StubMsg, &details_StubDesc); name = 0; RpcTryFinally { RpcTryExcept { if(_StubMsg.Buffer > _StubMsg.BufferEnd) { RpcRaiseException(RPC_X_BAD_STUB_DATA); } } RpcExcept( RPC_BAD_STUB_DATA_EXCEPTION_FILTER ) { RpcRaiseException(RPC_X_BAD_STUB_DATA); } RpcEndExcept if(100 * 1 < 0) { RpcRaiseException(RPC_X_INVALID_BOUND); } name = (unsigned char (*)[])NdrAllocate(&_StubMsg,100 * 1); // GetName(*name) is the original source line produced // by midl compiler. GetName2(*name); _StubMsg.BufferLength = 0U; _StubMsg.MaxCount = 100; NdrConformantStringBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, (unsigned char __RPC_FAR *)*name, (PFORMAT_STRING) &__MIDL_TypeFormatString.Format[2] ); _pRpcMessage->BufferLength = _StubMsg.BufferLength; _Status = I_RpcGetBuffer( _pRpcMessage ); if ( _Status ) RpcRaiseException( _Status ); _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; _StubMsg.MaxCount = 100; NdrConformantStringMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, (unsigned char __RPC_FAR *)*name, (PFORMAT_STRING) &__MIDL_TypeFormatString.Format[2] ); } RpcFinally { if ( name ) _StubMsg.pfnFree( name ); } RpcEndFinally _pRpcMessage->BufferLength = (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); }
练习
要测试演示项目,请启动两个 GETNAME 应用程序副本。 在任何副本中输入两个不同的端点,并针对您敌人的名字(或您的妻子 - 同上)输入您的名字。
end point of end point of your name
remote server local server
--------------- ------------- ------------
app A - 55459 55460 moshe
app B - 55460 55459 shoshana
在两个副本上单击PlayServer按钮。 正如您所看到的,每个应用程序都指向另一个应用程序,因此通信是双向的。 因此,每个应用程序都是客户端和服务器,具有相同的请求(即 - 从远程应用程序获取名称)。
此演示中的 IP 由“localhost”字符串表示,指向由“localhost”表示的本地计算机。
在应用程序的每个副本中单击“从远程服务器获取名称”按钮,您将获得对方应用程序用户的姓名。
好极了!!!
兼容性说明
该演示项目在 Windows 98 和 Windows 2000 中运行良好。
澄清
为了简化并突出本文的主题,我没有使用一些必要的代码(RPC 异常、线程状态检查活动从摇篮到伊甸园以及其他有用的状态处理)。