1. What is remote process call?
What is Remote Procedure Call (RPC )? You may be a little unfamiliar with this concept, and you may be very familiar with NFS, yes,
NFS is based on rpc. To understand the Remote Procedure Call, Let's first look at the procedure call.
The so-called process call is to pass the control from process a to process B, and return process B to process. Most systems
Both the caller and the called are in a process in the given host system. They are connected by the linker when an executable file is generated.
Is called for the local process.
Remote Procedure Call (RPC) refers to a process activated by a process on the local system on the remote system. We call this process call because it represents a programmer.
Is a regular process call. There are two processes that process remote process calls. One is a local client process and the other is a remote server process. For local processes
Process invocation represents the control of the customer process, and a message is generated by the customer process and sent to the remote server through a network system call. Network Information includes process adjustment
With the required parameters, the remote server calls the corresponding process after receiving the message, and then sends the result back to the customer process through the network. Then, the customer process returns the result to the call.
Process. Therefore, a remote system call is performed on the caller as a local process call, but the process is actually called on the remote system.
Ii. Remote process call Model
Local process invocation: a traditional program consists of one or more processes. They are usually arranged according to a call level. As shown in:
Remote process call: it uses the same abstraction as a traditional process, but it allows the boundaries of a process to span two computers. As shown in:
Iii. Comparison between remote process and local Process
First, the network latency will make the cost of a remote process much larger than that of a local process.
Second, because the call process and the call process run in the same memory space, pointers can be transferred between processes. However, the remote process cannot
The pointer is used as a parameter because the Remote Process and the caller are running in a completely different address space.
Once again, because a remote call cannot share the caller's environment, it cannot directly access the caller's I/O descriptor or operating system functions.
Iv. Versions of Remote Procedure Calls
(1) sun rpc (UDP, TCP)
(2) Xerox courier (SPP)
(3) Apollo RPC (UDP, DDS)
Sun RPC can be used for connection-oriented or non-connection-oriented protocols, Xerox courier is only used for connection-oriented protocols, and Apollo RPC is only used for non-connection protocols.
5. How to compile a Remote Procedure Call Program
To rewrite a traditional program into an RPC program, we need to add some other code in the program. This process is called the stub process. We can imagine
A traditional program, a process of which is transferred to a remote machine. At the remote process end, the stub process replaces the caller. In this way, Stub implements remote
All communications required by the call. Because stub uses the same interface as the original call, adding these stub processes does not need to change the original called
And do not need to change the original called process. As shown in:
Win32 RPC programming (1) from a simple RPC "Hello, world !" .
Reference: msdn: Win32 and COM development-> networking-> network protocols-> Remote Procedure CALS (RPC)
Step 2: Compile the IDL (Interface Description Language) File
-------------------------------------------------------------------------
IDL is a general industrial standard language, and everyone should be familiar with it, because it is also used in COM to describe interfaces.
Hello. IDL:
[
UUID ("4556509f-618a-46cf-ab3d-ed736ed66477"), // unique UUID, generated using guidgen
Version (1.0)
]
Interface helloworld
{
// Our defined method
Void Hello ([IN, string] const char * psz );
Void Shutdown (void );
}
An optional file is the application configuration file (. ACF). It is used to configure the RPC interface, for example, the following hello. ACF file:
Hello. ACF:
[
Implicit_handle (handle_t helloworld_binding)
]
Interface helloworld
{
}
Implicit_handle is defined above, so the client will bind the helloworld_binding handle. We will see it in the client code later.
Compile the IDL file:
> Midl hello. IDL
Microsoft (r) 32B/64b midl compiler version 6.000000366
Copyright (c) Microsoft Corporation 1991-2002. All rights reserved.
Processing./Hello. IDL
Hello. IDL
Processing./Hello. ACF
Hello. ACF
We can see that the hello. H, hello_s.c, and hello_c.c files are automatically generated. These are called RPC stub programs, but we can ignore this concept,
We only need to know That hello. h defines
Extern rpc_if_handle helloworld_v1_0_s_ifspec;
This rpc_if_handle will be used later.
Step 2: Write a server program
-------------------------------------------------------------------------
We have already agreed on the called interface in step 1, so now we start to implement its server. The Code is as follows:
Server. c
# Include <stdlib. h>
# Include <stdio. h>
# Include "Hello. H" // reference the header file generated by midl
/**
* This is the interface method defined in IDL.
* Note that the declaration in IDL is: void Hello ([IN, string] const char * psz );
* But it turns into const unsigned char *. Why?
* See midl command-line reference->/Char switch in msdn.
* The default compilation option is used to process char in IDL according to unsigned char.
*/
Void Hello (const unsigned char * psz)
{
Printf ("% s/n", psz );
}
/** This is also the interface method defined in IDL, which provides a mechanism to disable the server */
Void Shutdown (void)
{
// The following operations will cause rpcserverlisten () to exit.
Rpcmgmtstopserverlistening (null );
Rpcserverunregisterif (null, null, false );
}
Int main (INT argc, char * argv [])
{
// Use named pipe as the RPC channel, so that the endpoint parameter is the name of named pipe.
// According to the named pipe naming rules,/pipe/pipename, where pipename can be/
// Any other character, so a guid string is used to name it, so that it will not be repeated.
Rpcserveruseprotseqep (unsigned char *) "ncacn_np", 20, (unsigned char *) "// pipe // {8dd50205-3108-498f-96e8-dbc4ec074cf9}", null );
// Register the interface. helloworld_v1_0_s_ifspec is defined in hello. h generated by midl.
Rpcserverregisterif (helloworld_v1_0_s_ifspec, null, null );
// Start listening. This function will be blocked all the time.
Rpcserverlisten (1, 20, false );
Return 0;
}
// The following functions are written to meet the link requirements. If not, a link error occurs.
Void _ rpc_far * _ rpc_user midl_user_allocate (size_t Len)
{
Return (malloc (LEN ));
}
Void _ rpc_user midl_user_free (void _ rpc_far * PTR)
{
Free (PTR );
}
Compile:
> Cl/d_win32_winnt = 0x500 server. c hello_s.c rpcrt4.lib
Microsoft (r) 32-bit C/C ++ optimization compiler 14.00.50727.42 for 80x86
Copyright (c) Microsoft Corporation. All rights reserved.
Server. c
Hello_s.c
Generating code...
Microsoft (r) incremental linker version 8.00.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.
/Out: server.exe
Server. OBJ
Hello_s.obj
Rpcrt4.lib
Why do we need to specify _ win32_winnt = 0x500 during compilation? If not, the following error will be reported:
Hello_s.c (88): Fatal error c1189: # error: You need a Windows 2000 or later
Run this stub because it uses these features:
Step 2: Compile the client program
-------------------------------------------------------------------------
Client code:
Client. c
# Include <stdlib. h>
# Include <stdio. h>
# Include <string. h>
# Include "Hello. H" // reference the header file generated by midl
Int main (INT argc, char * argv [])
{
Unsigned char * pszstringbinding = NULL;
If (argc! = 2)
{
Printf ("Usage: % S <Hello text>/N", argv [0]);
Return 1;
}
// Use named pipe as the RPC channel. See rpcserveruseprotseqep () in server. C.
// If the 3rd parameter networkaddr is null, the local service is connected.
// Otherwise, the format like // SERVERNAME is required. For example, if your computer name is Jack
Rpcstringbindingcompose (null, (unsigned char *) "ncacn_np",/* (unsigned char *) "// SERVERNAME" */null, (unsigned char *) "// pipe // {8dd50205-3108-498f-96e8-dbc4ec074cf9}", null, & pszstringbinding );
// Bind the interface, which must be consistent with the configuration of Hello. ACF, so helloworld_binding
Rpcbindingfromstringbinding (pszstringbinding, & helloworld_binding );
// The following is the function that calls the server.
Rpctryexcept
{
If (_ stricmp (argv [1], "shutdown") = 0)
{
Shutdown ();
}
Else
{
Hello (unsigned char *) argv [1]);
}
}
Rpcexcept (1)
{
Printf ("RPC exception % d/N", rpcexceptioncode ());
}
Rpcendexcept
// Release resources
Rpcstringfree (& pszstringbinding );
Rpcbindingfree (& helloworld_binding );
Return 0;
}
// The following functions are written to meet the link requirements. If not, a link error occurs.
Void _ rpc_far * _ rpc_user midl_user_allocate (size_t Len)
{
Return (malloc (LEN ));
}
Void _ rpc_user midl_user_free (void _ rpc_far * PTR)
{
Free (PTR );
}
Compile:
> Cl/d_win32_winnt = 0x500 client. c hello_c.c rpcrt4.lib
Microsoft (r) 32-bit C/C ++ optimization compiler 14.00.50727.42 for 80x86
Copyright (c) Microsoft Corporation. All rights reserved.
Client. c
Hello_c.c
Generating code...
Microsoft (r) incremental linker version 8.00.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.
/Out: client.exe
Client. OBJ
Hello_c.obj
Rpcrt4.lib
Step 2: Test:
-------------------------------------------------------------------------
Run server.exe. A console window is displayed, waiting for the client to call.
Run client client.exe:
> Client Hello
The Hello string is displayed in the console window of server.exe.
> Client Shutdown
Log out of server.exe.
Win32 RPC programming (ii) is basically the same as in the previous section. However, RPC is called through named pipe in the previous section. Let's try the TCP method again.
Most of the Code is the same, and the IDL Interface does not need to be changed (no matter how RPC is used, the interface is irrelevant ).
The server must switch to the TCP mode:
---------------------------------
Int main (INT argc, char * argv [])
{
// Use TCP as the RPC channel. Bind port 13521.
Rpcserveruseprotseqep (
(Unsigned char *) "ncacn_ip_tcp ",
Rpc_c_protseq_max_reqs_default,
(Unsigned char *) "13521 ",
Null );
// Note: Since Windows XP SP2, security requirements are enhanced. If rpcserverregisterif () is used for registration
// Interface, when the client is called, rpcexceptioncode () = 5, that is, the error of Access denied. Therefore
// Use the rpcserverregisterifex with the rpc_if_allow_callbacks_with_no_auth flag to allow the client to directly
// Call.
// Rpcserverregisterif (helloworld_v1_0_s_ifspec, null, null );
Rpcserverregisterifex (
Helloworld_v1_0_s_ifspec, // interface to register.
Null,
Null, // use the midl generated entry-point vector.
Rpc_if_allow_callbacks_with_no_auth,
0,
Null );
// They are all the same
...
Return 0;
}
The call method of the client must also be changed:
---------------------------------
Int main (INT argc, char * argv [])
{
// The previous steps are the same
...
// Use TCP as the RPC channel. The server port is 13521. 3rd
// If networkaddr is null, the local service is connected,
// You can also obtain IP addresses, domain names, and servernames.
Rpcstringbindingcompose (
Null,
(Unsigned char *) "ncacn_ip_tcp ",
(Unsigned char *) "localhost"/* null */,
(Unsigned char *) "13521 ",
Null,
& Pszstringbinding
);
// They are all the same
...
}
Everything else is the same.
Based on the previous section, we will discuss how to implement asynchronous RPC calls. The function calls demonstrated in the first two sections are synchronized, that is, when the function Hello () is called,
The client will block until the hello () function of the server returns. If the server-side functions require time-consuming operations, such as complex computing and query,
The client can only be blocked all the time. In this case, we can use Asynchronous RPC to improve the client performance.
Asynchronous RPC is enabled through the configuration file (. ACF:
--------------------------------------------
Hello. ACF:
[
Implicit_handle (handle_t helloworld_binding)
]
Interface helloworld
{
[Async] Hello (); // added [async] to indicate that this is an asynchronous call.
}
The original interface helloworld has two methods: Hello (), shutdown (), and Shutdown (). We still make it synchronously called, so in. ACF
Parts are not listed. You do not need to modify the IDL Interface file.
The Hello () in server code server. C should be changed to the following:
------------------------------------------------------
Void Hello (prpc_async_state rpcasynchandle, const unsigned char * psz)
{
// Simulate a long operation
Printf ("sleep 5 seconds.../N ");
Sleep (5000 );
Printf ("% s/n", psz );
// Indicates that the call has been completed
Rpcasynccompletecall (rpcasynchandle, null );
}
You do not need to modify other code on the server.
The call method in client. c must also be changed:
---------------------------------
Int main (INT argc, char * argv [])
{
// The previous steps are the same
...
// The following is a function that calls the server.
Rpctryexcept
{
If (_ stricmp (argv [1], "shutdown") = 0)
{
Shutdown ();
}
Else
{
// Initialize asynchronous call
Rpc_async_state async;
Rpcasyncinitializehandle (& async, sizeof (async ));
Async. userinfo = NULL;
Async. icationicationtype = rpcnotificationtypenone;
// This function returns immediately
Hello (& async, (unsigned char *) argv [1]);
// Query the call status
While (rpcasyncgetcallstatus (& async) = rpc_s_async_call_pending)
{
Printf ("call Hello () pending, wait 1S.../N ");
Sleep (1000 );
}
// Notification call completed
Rpcasynccompletecall (& async, null );
}
}
Rpcexcept (1)
{
Printf ("RPC exception % d/N", rpcexceptioncode ());
}
Rpcendexcept
// They are all the same
...
}
In this way, the client implements asynchronous calls!
Win32 RPC programming (4) This section describes the high-performance RPC mode-LPC in Windows NT.
Many windows programming books talk about inter-process communication in windows, including wm_copydata, anonymous, named, and shared memory,
But RPC is rarely mentioned. Why? RPC is called "Remote Procedure Call", which is used for communication between distributed systems.
It can also be used for communication between local processes, but the performance is always doubtful. Therefore, the inter-process communication model designed by many people uses wm_copydata or pipelines,
Or simply share the memory, which is equivalent to creating your own wheels. Everything starts from scratch. But RPC is really easy to use. Calling is like calling a library function. All the communication details are for you.
Encapsulated. Does RPC have a better performance model? This is the LPC model to be discussed below.
LPC (Local Procedure Call) is a high-performance communication mode within Windows NT. It is implemented in the kernel and is mainly used inside the Win32 subsystem.
Communication, such as CSRSS and LSASS, are widely used in LPC. In the Code demonstrated above, we only need to change a line of code to use LPC. In fact, RPC
LPC is used internally for communication, which greatly improves the performance.
Server code:
Server. c
--------------
// Use LPC for communication
Rpcserveruseprotseqep (
(Unsigned char *) "ncalrpc ",
Rpc_c_protseq_max_reqs_default,
(Unsigned char *) "appname ",
Null );
Client code:
Client. c
--------------
// Use LPC for communication
// The 3rd parameter networkaddr can only be null.
Rpcstringbindingcompose (
Null,
(Unsigned char *) "ncalrpc ",
Null, (unsigned char *) "appname ",
Null,
& Pszstringbinding );