由浅入深,举例讲解RPC(一)

时间:2022-10-12 07:12:56


关于RPC的文章很多,但是系统讲解的很少。下面我将写一个系列报道。用代码和论述来把rpc来讲讲清楚。

这篇就是开始第一篇了。

 

由于工作比较忙。我们抽出一个星期的时间,有时间会写一点。把这个系列写完。所以,有可能每个系列都比较短些。

从最基本的讲起,让大家彻底明白RPC.

 

好了废话不多说了。正是开始。

 

 

首先,你要用RPC,必须先搞清楚什么是IDL.

 

Rpc是什么?

​http://www.cppblog.com/alantop/archive/2007/07/09/27717.html​

IDL是什么?

​http://www.cppblog.com/alantop/archive/2007/07/09/27725.html​

 

下来,举个例子。怎么样把一个标准程序改成用IDL语言写的程序。

 

这是一个标准程序。

// File Standalone.cpp
#include <iostream>

// Future server function.
void Output(const char* szOutput)
{
std::cout << szOutput << std::endl;
}

int main()
{
// Future client call.
Output("Hello Lonely World!");
}

 

下来看我们怎么把它改为一个标准IDL语言的程序

用IDL语言定义接口:

// File Example1.idl
[
// A unique identifier that distinguishes this
// interface from other interfaces.
uuid(00000001-EAF3-4A7A-A0F2-BCE4C30DA77E),

// This is version 1.0 of this interface.
version(1.0),

// This interface will use an implicit binding
// handle named hExample1Binding.
implicit_handle(handle_t hExample1Binding)
]
interface Example1 // The interface is named Example1
{
// A function that takes a zero-terminated string.
void Output(
[in, string] const char* szOutput);
}

上面这个文件是我们用idl语言定义的,我们定义了一个接口Example1, 它带有uuid和version. 这个接口里定义了一个函数Output.

 

UUID是什么?

​http://www.cppblog.com/alantop/archive/2007/07/09/27726.html​

 

 

接口的implicit_handle属性,我们后面再讨论。

 

接下来干什么呢?

我们为了在程序中使用idl,必须通过通过编译器(midl.exe)把它翻译成客户代理和服务器存根, 代理和存根将在后面被我们的编译器(windows平台下的cl.exe)所使用。

 

由浅入深,举例讲解RPC(一)

 

改好的服务器端程序:

// File Example1Server.cpp
#include <iostream>
#include "Example1.h"

// Server function.
void Output(const char* szOutput)
{
std::cout << szOutput << std::endl;
}

int main()
{
RPC_STATUS status;

// Uses the protocol combined with the endpoint for receiving
// remote procedure calls.
status = RpcServerUseProtseqEp(
reinterpret_cast<unsigned char*>("ncacn_ip_tcp"), // Use TCP/IP
// protocol.
RPC_C_PROTSEQ_MAX_REQS_DEFAULT, // Backlog queue length for TCP/IP.
reinterpret_cast<unsigned char*>("4747"), // TCP/IP port to use.
NULL); // No security.

if (status)
exit(status);

// Registers the Example1 interface.
status = RpcServerRegisterIf(
Example1_v1_0_s_ifspec, // Interface to register.
NULL, // Use the MIDL generated entry-point vector.
NULL); // Use the MIDL generated entry-point vector.

if (status)
exit(status);

// Start to listen for remote procedure
// calls for all registered interfaces.
// This call will not return until
// RpcMgmtStopServerListening is called.
status = RpcServerListen(
1, // Recommended minimum number of threads.
RPC_C_LISTEN_MAX_CALLS_DEFAULT, // Recommended
//maximum number of threads.
FALSE); // Start listening now.

if (status)
exit(status);
}

// Memory allocation function for RPC.
// The runtime uses these two functions for allocating/deallocating
// enough memory to pass the string to the server.
void* __RPC_USER midl_user_allocate(size_t size)
{
return malloc(size);
}

// Memory deallocation function for RPC.
void __RPC_USER midl_user_free(void* p)
{
free(p);
}

 

这是初始化,和注册接口的代码。

 

现在看看怎么写客户端

// File Example1Client.cpp
#include <iostream>
#include "Example1.h"

int main()
{
RPC_STATUS status;
unsigned char* szStringBinding = NULL;

// Creates a string binding handle.
// This function is nothing more than a printf.
// Connection is not done here.
status = RpcStringBindingCompose(
NULL, // UUID to bind to.
reinterpret_cast<unsigned char*>("ncacn_ip_tcp"), // Use TCP/IP
// protocol.
reinterpret_cast<unsigned char*>("localhost"), // TCP/IP network
// address to use.
reinterpret_cast<unsigned char*>("4747"), // TCP/IP port to use.
NULL, // Protocol dependent network options to use.
&szStringBinding); // String binding output.

if (status)
exit(status);

// Validates the format of the string binding handle and converts
// it to a binding handle.
// Connection is not done here either.
status = RpcBindingFromStringBinding(
szStringBinding, // The string binding to validate.
&hExample1Binding); // Put the result in the implicit binding
// handle defined in the IDL file.

if (status)
exit(status);

RpcTryExcept
{
// Calls the RPC function. The hExample1Binding binding handle
// is used implicitly.
// Connection is done here.
Output("Hello RPC World!");
}
RpcExcept(1)
{
std::cerr << "Runtime reported exception " << RpcExceptionCode()
<< std::endl;
}
RpcEndExcept

// Free the memory allocated by a string.
status = RpcStringFree(
&szStringBinding); // String to be freed.

if (status)
exit(status);

// Releases binding handle resources and disconnects from the server.
status = RpcBindingFree(
&hExample1Binding); // Frees the implicit binding handle defined in
// the IDL file.

if (status)
exit(status);
}

// Memory allocation function for RPC.
// The runtime uses these two functions for allocating/deallocating
// enough memory to pass the string to the server.
void* __RPC_USER midl_user_allocate(size_t size)
{
return malloc(size);
}

// Memory deallocation function for RPC.
void __RPC_USER midl_user_free(void* p)
{
free(p);
}