Introduction to RPC - Part 1
2008-12-28 18:10
441 查看
Download source files - 13.1 Kb
Download demo project - 314 Kb
IDL, RPC and you
Interface Definition Language (IDL)
Remote Procedure Call (RPC)
Are you scared yet?
The standalone application
Hello Lonely World!
The IDL file
Hello IDL World!
What's next?
How may I serve you?
Hello Server World!
Let me be your client
Hello Client World!
Putting it all together
Appendix
Debugging and RPC
Implicit and explicit handles
Application Configuration File (ACF)
Don't fiddle with the generated files
Shutting down the server
Conclusion
References
Revision history
now, all of these applications use RPC as the layer of communication
between the client and the server. I found it strange that no real
article existed on this matter here on CodeProject, so I decided to
write one of my own to spread my knowledge on this matter.
The matter is, on the other hand, a bit big so I will split it into
several articles of different levels of difficulty. This first one is
at beginner level.
defining interfaces (no kidding). Writing an IDL file is somewhat like
writing a C header file with some additional keywords and constructs.
IDL is an attributed programming language and thus it can describe
parameters, functions and interfaces in more detail than C.
creating distributed client/server programs. The RPC runtime libraries
manage most of the details relating to network protocols and
communication. This enables you to focus on the details of the
application rather than the details of the network.
With RPC a client can connect to a server running on another
platform. For example: The server could be written for Linux and the
client could be written for Win32.
HelloWorld application that we later will transform into a RPC
client/server application.
Collapse
I don't think anyone can think of anything to say about the above application. It outputs the string
Collapse
Here you see the attribute programming used by IDL. The above
example defines an interface with an universally unique identifier (
The
that will translate the IDL to a client proxy and a server stub in C.
The proxy/stub will later be compiled using your favorite compiler (cl.exe in my case).
Collapse
This is a bit different from the standalone application, but not
much. It has some initialization code for registering the interface but
the
Collapse
Other than connecting to the server and some cleanup, the actual
call is the same, except that I've changed the string to be outputted.
server stub and the common header file. The proxy and the stub is
compiled, as are the client and server implementation. We link the two
applications and run them. If everything works, we can be really glad
as we have done our first RPC client/server application.
a MIDL generated file, the real problem will most likely be in the
client or in the server. I have sometime run into problems with
pointers, but in a follow up article I will describe these things more
thoroughly.
example in this article) or explicit. I always use explicit handles as
I sometime are connected to multiple servers, and that does not work
with the implicit handle. To use explicit handles, you'll have to
change the IDL file, the server and the client:
Collapse
Collapse
Collapse
There are also something called
directly in the IDL file, but that is a Microsoft extension. One
usually need to use a separate Application Configuration File that
contain these. The sample code in the zip file does use a separate ACF
file, but I felt like, writing that in the article would only confuse
you even more.
if you feel that they are incorrect. When compiling them you may on the
other hand get a lot of warnings, but when lowering the warning level
to 2, they are silent.
somehow. That ain't the best way of doing it, another better way is to
call the
applications. If you step through and read my other (future) articles
on this matter, you'll be fully prepared for the world at hand.
This was my first ever article on CodeProject, I hope you enjoyed reading it as much as I enjoyed writing it.
MSDN - MIDL and RPC [^]
MSDN - Remote Procedure Call [^]
The OSF Distributed Computing Environment [^]
Updated demo and source.
Changed from Named Pipe to TCP/IP (suggestion by Hector Santos).
2003-08-23:
Original article.
From: http://www.codeproject.com/KB/IP/rpcintro1.aspx
Download demo project - 314 Kb
Contents
IntroductionIDL, RPC and you
Interface Definition Language (IDL)
Remote Procedure Call (RPC)
Are you scared yet?
The standalone application
Hello Lonely World!
The IDL file
Hello IDL World!
What's next?
How may I serve you?
Hello Server World!
Let me be your client
Hello Client World!
Putting it all together
Appendix
Debugging and RPC
Implicit and explicit handles
Application Configuration File (ACF)
Don't fiddle with the generated files
Shutting down the server
Conclusion
References
Revision history
Introduction
I have worked with client-server applications for a couple of yearsnow, all of these applications use RPC as the layer of communication
between the client and the server. I found it strange that no real
article existed on this matter here on CodeProject, so I decided to
write one of my own to spread my knowledge on this matter.
The matter is, on the other hand, a bit big so I will split it into
several articles of different levels of difficulty. This first one is
at beginner level.
IDL, RPC and you
In order to use RPC, you need to have some knowledge about IDL, but don't you worry, this article will help you.Interface Definition Language (IDL)
IDL stands for Interface Definition Language and it's a language fordefining interfaces (no kidding). Writing an IDL file is somewhat like
writing a C header file with some additional keywords and constructs.
IDL is an attributed programming language and thus it can describe
parameters, functions and interfaces in more detail than C.
Remote Procedure Call (RPC)
Remote Procedure Call (RPC) defines a powerful technology forcreating distributed client/server programs. The RPC runtime libraries
manage most of the details relating to network protocols and
communication. This enables you to focus on the details of the
application rather than the details of the network.
With RPC a client can connect to a server running on another
platform. For example: The server could be written for Linux and the
client could be written for Win32.
Are you scared yet?
If you're not scared, I think it's good time for an example.The standalone application
This standalone application will not use RPC and it is a simpleHelloWorld application that we later will transform into a RPC
client/server application.
Hello Lonely World!
Collapse
// 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!"); }
I don't think anyone can think of anything to say about the above application. It outputs the string
"Hello Lonely World!"to the standard output.
The IDL file
It is time to define our interface in IDL.Hello IDL World!
Collapse
// 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); }
Here you see the attribute programming used by IDL. The above
example defines an interface with an universally unique identifier (
uuid) and version (
version) that is named
Example1. The
Example1interface defines one function named
Outputthat takes a
const char*argument named
szOutputas input (
in) that is zero-terminated (
string).
The
implicit_handleattribute for the interface will be discussed later, leave it there for now.
What's next?
In order to use the IDL in our application we need to run it through a compiler (midl.exe)that will translate the IDL to a client proxy and a server stub in C.
The proxy/stub will later be compiled using your favorite compiler (cl.exe in my case).
How may I serve you?
It's time to take the generated files and put them to use in our server application.Hello Server World!
Collapse
// 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); }
This is a bit different from the standalone application, but not
much. It has some initialization code for registering the interface but
the
Outputfunction remains the same.
Let me be your client
Time has come to write our client application that will connect to the server.Hello Client World!
Collapse
// 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); }
Other than connecting to the server and some cleanup, the actual
call is the same, except that I've changed the string to be outputted.
Putting it all together
First we need to compile the IDL file to get the client proxy, theserver stub and the common header file. The proxy and the stub is
compiled, as are the client and server implementation. We link the two
applications and run them. If everything works, we can be really glad
as we have done our first RPC client/server application.
Appendix
This section describes some techniques useful when writing RPC applications.Debugging and RPC
If you get into trouble when debugging and the problem seem to be ina MIDL generated file, the real problem will most likely be in the
client or in the server. I have sometime run into problems with
pointers, but in a follow up article I will describe these things more
thoroughly.
Implicit and explicit handles
When using RPC, the binding handles can be implicit (as in theexample in this article) or explicit. I always use explicit handles as
I sometime are connected to multiple servers, and that does not work
with the implicit handle. To use explicit handles, you'll have to
change the IDL file, the server and the client:
Collapse
// File Example1Explicit.idl [ // A unique identifier that distinguishes this // interface from other interfaces. uuid(00000002-EAF3-4A7A-A0F2-BCE4C30DA77E), // This is version 1.0 of this interface. version(1.0), // This interface will use explicit binding handle. explicit_handle ] interface Example1Explicit // The interface is named Example1Explicit { // A function that takes a binding handle and a zero-terminated string. void Output( [in] handle_t hBinding, [in, string] const char* szOutput); }
Collapse
// File Example1ExplicitServer.cpp #include <iostream> #include "Example1Explicit.h" // Server function. void Output(handle_t hBinding, const char* szOutput) { std::cout << szOutput << std::endl; } // main - same as before.
Collapse
// File Example1ExplicitClient.cpp #include "Example1Explicit.h" int main() { // Call to RpcStringBindingCompose - same as before. handle_t hExample1ExplicitBinding = NULL; // 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. &hExample1ExplicitBinding); // Put the result in the explicit // binding handle. if (status) exit(status); RpcTryExcept { // Calls the RPC function. The hExample1ExplicitBinding binding handle // is used explicitly. // Connection is done here. Output(hExample1ExplicitBinding, "Hello RPC World!"); } RpcExcept(1) { std::cerr << "Runtime reported exception " << RpcExceptionCode() << std::endl; } RpcEndExcept // Call to RpcStringFree - same as before. // Releases binding handle resources and disconnects from the server. status = RpcBindingFree( &hExample1ExplicitBinding); // Frees the binding handle. if (status) exit(status); }
There are also something called
auto_handle, but I've never ever used that. It somehow takes care about connecting to the server automatically.
Application Configuration File (ACF)
In this example I usedimplicit_handleand
explicit_handle
directly in the IDL file, but that is a Microsoft extension. One
usually need to use a separate Application Configuration File that
contain these. The sample code in the zip file does use a separate ACF
file, but I felt like, writing that in the article would only confuse
you even more.
Don't fiddle with the generated files
You should not fiddle with the generated files to make them compile, they are (should be) correct. Check the switches to midl.exeif you feel that they are incorrect. When compiling them you may on the
other hand get a lot of warnings, but when lowering the warning level
to 2, they are silent.
Shutting down the server
The example server will run until it is shut down by closing itsomehow. That ain't the best way of doing it, another better way is to
call the
RpcMgmtStopServerListeningfunction. But how would you call it? You could add another function in the interface (perhaps named
Shutdown?) that will call
RpcMgmtStopServerListeningor you could create another thread in the server before calling
RpcServerListenthat will call
RpcMgmtStopServerListeningafter a minute or so. More on this in another article.
Conclusion
This is only the entry door to the world of RPC and client/serverapplications. If you step through and read my other (future) articles
on this matter, you'll be fully prepared for the world at hand.
This was my first ever article on CodeProject, I hope you enjoyed reading it as much as I enjoyed writing it.
References
MSDN - Microsoft Interface Definition Language [^]MSDN - MIDL and RPC [^]
MSDN - Remote Procedure Call [^]
The OSF Distributed Computing Environment [^]
Revision history
2003-08-25:Updated demo and source.
Changed from Named Pipe to TCP/IP (suggestion by Hector Santos).
2003-08-23:
Original article.
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)About the Author
Anders Dalvander |
|
相关文章推荐
- Introduction to RPC - Part 2
- Introduction to RPC - Part 1
- Introduction to RPC - Part 2
- Commands In The CAB (Introduction To CAB/SCSF Part 10)
- Introduction To The Smart Client Software Factory (CAB/SCSF Part 18)
- Model-View-Presenter: Variations On The Basic Pattern (Introduction To CAB/SCSF Part 24)
- Driver Development Part 3: Introduction to driver contexts
- Driver Development Part 6: Introduction to Display Drivers
- Driver Development Part 1: Introduction to Drivers
- Nmap Cheat Sheet: From Discovery to Exploits – Part 1: Introduction to Nmap
- Introduction to Nutch, Part 2: Searching
- Introduction to Neural Machine Translation - part 2
- A chatroom for all! Part 1 - Introduction to Node.js(转发)
- Introduction to Classification Evaluation Methods -- Part 1
- A Quick Introduction to Bash Programming - Part 1
- Introduction to Nutch, Part 1: Crawling
- 循环神经网络教程Recurrent Neural Networks Tutorial, Part 1 – Introduction to RNNs
- Introduction to DCOM/RPC
- Introduction To Events In The CAB (Introduction To CAB/SCSF Part 11)
- Business Modules And Interfaces In The SCSF Smart Client Solution (Introduction To CAB/SCSF Part 19)