A distributed application based on the client/server model consists of two parts: the client side of the application, which runs on one machine and makes a request for service on behalf of a user, and the server side of the application, which runs on another machine on the network and fulfills the service request. The two pieces of code on two different machines need to be able to communicate across the network. One model for implementing communications between the client and server of an application is the RPC facility.
RPC gives programmers the ability to express an interaction between the client and server of a distributed application as if it were a procedure call; the programmer defines a calling interface and a procedure that implements it, makes a call to the procedure along with any arguments, and receives a return value through the argument list or as the procedure result.
In RPC, as in a traditional local procedure call, control is passed from one code segment to another, and then returns to the original segment. However, in a local procedure call, the code segments are in the same address space on the same machine; whereas, in a remote procedure call, the called procedure runs in a different address space, usually on a different machine than the calling procedure. As a result, arguments and results are passed differently for local and remote procedure calls. In local procedure calls, arguments and return values can be passed on the process's stack. In remote procedure calls, arguments and return values must be packed up into messages and sent to the peer machine over the network. The underlying RPC mechanism makes this look like a procedure call to the programmer.
An RPC facility shields the application programmer from the details of network communications between client and server nodes, such as the following:
Programmers using RPC do not need to rewrite applications in order to port them to different architectures, operating systems, communications protocols, or languages. RPC provides a high-level programming model to the distributed application programmer, hiding communications details, and removing nonportable system and hardware dependencies.
The following subsections give an overview of the DCE RPC technology component. They describe the components that constitute the technology, and how DCE RPC is seen from the end user's, programmer's, and administrator's perspectives, focusing primarily on programming with RPC since the application programmer is the main consumer of this technology. The subsections also describe the steps involved in the execution of a remote procedure call. They describe the ways in which DCE RPC frees the programmer from system software and hardware dependencies, and then list additional sources of information on DCE RPC.
DCE RPC is a facility for calling a procedure on a remote machine as if it were a local procedure call. To the application programmer, a remote call looks (almost) like a local call, but there are several RPC components that work together to implement this facility, including the Interface Definition Language (IDL) and its compiler, a Universal Unique Identifier (UUID) generator, and the RPC runtime, which supports two RPC protocol implementations. One RPC protocol operates over connection-oriented transports such as the Transmission Control Protocol/Internet Protocol (TCP/IP), and the other RPC protocol operates over connectionless transports such as the User Datagram Protocol/Internet Protocol (UDP/IP).
An end user does not see RPC at all, and the minimal amount of administration involved in RPC can usually be handled by the server-side application code, such as advertising an application server in the DCE Directory Service. It is the application programmer who most comes into contact with the RPC component. Since many of the DCE components are themselves client/server applications, they use RPC as their basis for distributed communications.
The components that constitute the DCE RPC are as follows:
An RPC interface is described in DCE IDL. The IDL file is compiled into object code via the IDL compiler. The object code is in two main parts: one for the client side of the application, and one for the server side.
This library consists of a set of routines, linked with both the client and server sides of an application, which implement the communications between them. This involves the client finding the server in the distributed system, getting messages back and forth, managing any state that exists between requests, and processing any errors that occur.
DCE RPC is integrated with the DCE Security Service component to provide secure communications. Levels of security can be controlled by the RPC application programmer through the authenticated RPC API. (See "Programming with DCE Security" for more information on authenticated RPC.)
DCE RPC is integrated with the DCE Directory Service component to facilitate the location of RPC-based servers by their clients. The NSI routines allow a programmer to control the association, or binding, of a client to a server during RPC.
The dced program runs on every DCE machine. It includes (among other things) an RPC-specific name server called the endpoint mapper service, which manages a database that maps RPC servers to the transport endpoints (in IP, the ports) that the server is listening for requests on.
dcecp is a tool for administering DCE.
These are ancillary commands and routines for generating UUIDs, which uniquely identify an RPC interface or any other resource. The uuidgen program can optionally generate an IDL template for a service interface, along with a unique identifier for the interface.
The end user does not come in direct contact with DCE RPC, but does see the end result in the form of
An end user who is browsing through the namespace may also notice the names of RPC-based servers, since these servers advertise themselves to their clients through the DCE Directory Service.
This section provides a brief overview of the process a programmer goes through in using DCE RPC to write an application. For an example of how this process applies to a simple application, see "Two DCE Application Examples" of this manual. For a more detailed description of the DCE RPC programming process, see the IBM DCE for AIX, Version 2.2: Application Development Guide.
Figure 20 shows an overview of the DCE RPC application development process. The dashed boxes indicate application code written by the DCE programmer. The other boxes indicate compiled code or code generated automatically for the programmer by DCE RPC.
Figure 20. DCE RPC Programming Process
First, the application programmer defines the RPC interface, and associated data types, using IDL. An interface is a group of operations that a server can perform. This grouping is similar to a module or library in a conventional programming language; that is, a group of operations defined in a single file or unit. For example, a Bank service might perform operations to debit, credit, or read the balance of an account. Each of those operations and their parameters must be defined in the IDL file. The collection of Bank service operations--debit, credit, read balance--together form the Bank service interface.
The process of defining RPC operations is similar to defining the input and output of a local procedure call, except that in IDL only the calling interface is defined, not the implementation of the procedure. (An IDL interface definition is similar to an ANSI C prototype definition.)
Next, the programmer compiles the IDL file with the IDL compiler. The compiler produces output in a conventional programming language, which is the C language in the DCE offering and then calls the appropriate compiler to produce object code. The output of the compilation consists of a client stub, a server stub, and a header file. The client and server stubs are routines that make the remoteness of the operation transparent to the caller or callee of the operation.
For the client side of the application, the programmer writes application code that makes calls to the operations in the IDL file. The client stub code is linked with this application code and (along with the RPC runtime code) performs the tasks that turn what looks like a procedure call into network communications with the server side of the application. Usually the client side of the application contains a relatively small amount of RPC code.
For the server side, the programmer writes application routines that implement the operations defined in the IDL file. For example, in the banking application, a database lookup might implement the operation to read an account balance. The server stub, generated by the IDL compiler, is linked with the server application code. The server stub unpacks the arguments and makes the call to the application routine as if the client program had called it directly. The server side of the application contains the bulk of the RPC code that needs to be written by the distributed application programmer.
In order for the client to send an RPC to the server, it must be able to find the server. This process is called binding. A client may have some direct way of knowing what server it needs to communicate with; for example, it may get this information from a file, a value hardcoded into its program, an environment variable, or a user. A more flexible way for a client to find a server is to take advantage of DCE RPC's use of the DCE Directory Service.
A client can find a server by asking the directory service for the location of a server that handles the interface that the client is interested in (in our example, a Bank server). In order for the directory service to be able to give the client this information, a server must first advertise itself in the directory service. The server adds itself to the namespace, along with information about what interfaces it implements, what protocols it uses to communicate with, and where it is located. This way, a server can move, or there can be multiple servers implementing a given interface, without affecting the client. The client can still go to the directory service, a well-known central source of information, and find out where the server is located.
The DCE programmer does not make calls directly to CDS; binding is supported by the NSI API, an RPC-specific name service layer. Calls to this library are made by the client side of an application in order to look up binding information for a server based on various criteria, such as the type of service, the objects it manages, and the interfaces it supports. The server side of an application calls this library to advertise information about itself to the namespace where clients can find it.
There are two parts to a server's location: the network address of the machine it resides on and the transport-specific address of the process--the network endpoint (for example, a UNIX port). The machine location is fairly stable, so its address can reasonably be entered into CDS. The network endpoint, however, can change each time the server process is started. Instead of making frequent changes to CDS to update a server's endpoint address, DCE RPC uses a specialized type of directory service, the endpoint mapper service, a service of dced. When a server starts, it registers its endpoint(s) with dced. Most servers will register an endpoint for each transport protocol supported on the host (for example, TCP and UDP).
Every machine that runs an RPC server also runs dced. The dced process always uses the same network endpoint, so its process address is well known. Therefore, once a client knows what machine a server is running on, it can find the endpoint mapper running on that same machine. It can then ask the endpoint mapper for the network endpoint of the server process. This process is shown in Figure 21 (read the messages, shown in quotes, in clockwise order).
Figure 21. Client Finds Server via CDS and dced
A few administrative tasks must be performed when running a distributed application using RPC. The application server executes most of these tasks when it first starts. As described in the previous section, the server registers its (dynamically assigned) listening endpoint with dced. The server also advertises information about itself and the interfaces it supports in the DCE Directory Service.
Nonautomated RPC administration is minimal. The administrator must ensure that each DCE machine has a DCE host daemon running on it. An administrator may be involved in registering servers in the namespace, but this can also be done from server code upon initialization as just described. Usually, an administrator will be needed to change the ACL on the directory where the server will register so that the server has write permission. The DCE control program, dcecp, allows an administrator to (among many things) control the dced and administer RPC information in the namespace.
An administrator may be involved in installing a new RPC-based application. In particular, the server side of the application must be started before it can begin receiving and servicing requests. The administrator may arrange for the server process to be started each time the machine is booted, for example.
A short ``walk-through'' of what happens during an RPC call may help clarify the RPC concept and how it works. This section describes the RPC call shown in Figure 22. (This description is somewhat simplified. The use of dced is not shown.)
Figure 22. RPC Runtime Process
On the server side, the Bank server process is started up. Before it begins its continuous cycle of receiving and servicing requests, the server process advertises its location in CDS (see Step 1 in Figure 22). In this way, when a client queries the directory service for a bank server, it will be able to find it. After initialization, the server listens for a request to come in from a client over the network. This call to wait for client requests is a call to the RPC runtime, since the runtime handles network communications.
Eventually, a user on the Bank client machine invokes the bank application. The Bank client initialization code calls the RPC runtime to find a server offering the Bank service (see Point 2). The Bank client application code makes a call to a remote procedure; for example, a call to a procedure that credits a bank account (3). This results in a call to the client stub code. The stub transforms the arguments of the call into a network message (4). It then calls the client's RPC runtime library, which sends the message to the server (5).
Back on the server side, the RPC request is received by the RPC runtime, which has been waiting for a client request (6). The runtime passes control, and the received packet, to the server stub. The stub unpacks the arguments sent by the client (7) and passes them to the appropriate operation by making a procedure call to it. At this point, the server application code that implements the requested operation is called. The operation is performed; that is, the account is credited (8).
The RPC reply (not shown in the figure) returns in the reverse direction. The Bank server application procedure returns the results of the credit operation to the stub. The stub packs up the return parameters and passes the resulting message to the RPC runtime to send off to the client over the network. The server then waits for the next client request to come in.
The client's runtime receives the server's reply. The client stub then unpacks the received network message, arranging returned parameters such that, when the client application call to RPC returns, it looks like it has just performed a local procedure call.
The mechanisms in both the client and server stubs and the runtime library are transparent to the application programmer. The programmer writes the application calls to the RPC operations on the client side, and provides implementations for those operations on the server side, but the network communications code is generated automatically.
There are several ways in which using DCE RPC frees a programmer from dependence on other parts of a system. It provides portability across programming languages, data transfer syntax mechanisms, transport and network protocols, and operating system and architecture platforms.
DCE RPC is language independent in the sense that the stubs generated by the IDL compiler can be called by programs written in any traditional programming language, provided that the language follows the calling conventions that the stub expects. The DCE IDL compiler generates stubs that use the C language calling conventions. A client written in FORTRAN, for example, can call an IDL stub in the same way that it calls any local C procedure. It can then make a remote call to a server (possibly written in another language) that contains the server stub generated from the same IDL file as the client stub was generated from.
The default data representation format is the DCE Transfer Syntax, which is currently the Network Data Representation (NDR). It allows various representations for different types of data, including multiple encodings for characters, integers, and floating-point numbers. It is multicanonical; that is, there are several canonical formats that can be used. The sender chooses one of these formats (in most cases, it will be the sender's native data representation), with information about what representation it has chosen. The receiver transforms data into its own format, if it is different from the format the data was sent in. This model optimizes for the case when both sender and receiver use the same data representation, a frequent occurrence. (Note that this data transfer is handled by the RPC software and is not visible to the application programmer.)
The DCE RPC architecture allows the use of transfer syntaxes other than DCE Transfer Syntax (although the only transfer syntax currently provided in the OSF implementation is DCE Transfer Syntax). For example, data could be formatted according to the ISO ASN.1/BER specification and sent over the wire in that way.
Independence of RPC, transport, and network protocols is achieved as follows. The DCE RPC offering includes two different RPC protocols. The first runs over connection-oriented transport protocols; the second runs over connectionless (datagram) transport protocols. The programmer can specify the underlying RPC protocol, but the semantics of RPC calls are the same whether the RPC is running over a connectionless or connection-oriented transport. Another RPC protocol could be used in place of these two DCE RPC protocols; for example, when ISO defines an RPC standard, it could be used transparently as a third RPC protocol under the DCE RPC interfaces.
Servers identify themselves to the directory service according to the interface they support and the protocols they use. In this way, a client can look up a server that uses network protocols that are compatible with those that the client supports.
Because DCE RPC uses the DCE transfer syntax, distributed applications are machine independent. DCE transfer syntax allows machines to transfer data even when their native data representations are not the same.
Finally, DCE RPC offers independence from the local operating system. The application programmer is not directly using the networking system calls provided by the local operating system. By being one level of abstraction up from this layer, the programmer is insulated from networking system calls that are operating system specific.
For additional information on DCE RPC, see the following: