| #### Steps in Developing a DCE RPC Application This section provides a step-by-step description of the development of the greet application. 1. Generate an IDL template. The first step is to run the uuidgen program, which creates a Unique Universal Identifier for uniquely labeling the application's interface. It also creates a template for an IDL file. The following command: uuidgen -i > greet.idl creates the file greet.idl. It contains the following: [ uuid(3d6ead56-06e3-11ca-8dd1-826901beabcd), version(1.0) ] interface INTERFACENAME { } 2. Name the interface. Replace the string INTERFACENAME in the IDL file with the name of the application interface, in this case, greetif. [ uuid(3d6ead56-06e3-11ca-8dd1-826901beabcd), version(1.0) ] interface greetif { } 3. Define the interface operations. Within the braces, write definitions of the operations comprising the interface. In this example, there is only one operation, called greet. /* * greet.idl * * The "greet" interface. */ [uuid(3d6ead56-06e3-11ca-8dd1-826901beabcd), version(1.0)] interface greetif { const long int REPLY_SIZE = 100; void greet( [in] handle_t h, [in, string] char client_greeting[], [out, string] char server_reply[REPLY_SIZE] ); } The first line of the operation definition gives the name of the operation, greet, and indicates by the void declaration that it has no meaningful return value. The next three lines specify the arguments to the operation, namely h, client_greeting, and server_reply. The first argument is a handle containing binding information for the server. The second is a string that is passed from the client to the server (the client's greeting). The third argument is a string returned from the server back to the client (the server's reply). 4. Run the IDL compiler. The following command runs the IDL compiler: idl greet.idl (Some of the commands in this section are somewhat simplified. See the Makefile in Makefile for the greet Application for the complete command.) Three new files are created automatically as a result of this command: · greet.h · greet_cstub.o · greet_sstub.o 5. Write the client application code greet_client.c. In general, the DCE RPC application programmer writes three application code files: · The client code · The server initialization code · The server operation code The following is the client code for the greet application, a file called greet_client.c. /* * greet_client.c * * Client of "greet" interface. */ #include <stdio.h> #include <dce/nbase.h> #include <dce/rpc.h> #include "greet.h" #include "util.h" int main( int argc, char *argv[] ) { rpc_ns_handle_t import_context; handle_t binding_h; error_status_t status; idl_char reply[REPLY_SIZE]; if (argc < 2) { fprintf(stderr, "usage: greet_client <CDS pathname>\en"); exit(1); } /* * Start importing servers using the name specified * on the command line. */ rpc_ns_binding_import_begin( rpc_c_ns_syntax_default, (unsigned_char_p_t) argv[1], greetif_v1_0_c_ifspec, NULL, &import_context, &status); ERROR_CHECK(status, "Can't begin import"); /* * Import the first server (we could interate here, * but we'll just take the first one). */ rpc_ns_binding_import_next(import_context, &binding_h, &status); ERROR_CHECK(status, "Can't import"); /* * Make the remote call. */ greet(binding_h, (idl_char *) "hello, server", reply); printf("The Greet Server said: %s\en", reply); } In this routine, the client makes two calls to the RPC runtime to acquire binding information needed to communicate with the server. The client then calls the greet remote procedure, supplying a greeting to be sent to the server. The client prints the reply received by the server. 6. Write the server initialization code greet_server.c The second file that the DCE RPC application programmer must write is the server initialization code. This is boilerplate code; that is, it is largely the same for any RPC application. The greet_server.c file contains the server initialization code for the greet application. /* * greet_server.c * * Main program (initialization) for "greet" server. */ #include <stdio.h> #include <dce/dce_error.h> #include <dce/rpc.h> #include "greet.h" #include "util.h" int main( int argc, char *argv[] ) { unsigned32 status; rpc_binding_vector_t *binding_vector; if (argc < 2) { fprintf(stderr, "usage: greet_server <CDS pathname>\en"); exit(1); } /* * Register interface with RPC runtime. */ rpc_server_register_if(greetif_v1_0_s_ifspec, NULL, NULL, &status); ERROR_CHECK(status, "Can't register interface"); /* * Use all protocol sequences that are available. */ rpc_server_use_all_protseqs(rpc_c_protseq_max_reqs_default, &status); ERROR_CHECK(status, "Can't use protocol sequences"); /* * Get the binding handles generated by the runtime. */ rpc_server_inq_bindings(&binding_vector, &status); ERROR_CHECK(status, "Can't get bindings for server"); /* * Register assigned endpoints with endpoint mapper. */ rpc_ep_register( greetif_v1_0_s_ifspec, binding_vector, NULL, (unsigned_char_p_t) "greet server version 1.0", &status); ERROR_CHECK(status, "Can't register with endpoint map"); /* * Export ourselves into the CDS namespace. */ rpc_ns_binding_export( rpc_c_ns_syntax_default, (unsigned_char_p_t) argv[1], greetif_v1_0_s_ifspec, binding_vector, NULL, &status); ERROR_CHECK(status, "Can't export into CDS namespace"); /* * Start listening for calls. */ printf("Listening...\en"); rpc_server_listen(rpc_c_listen_max_calls_default, &status); ERROR_CHECK(status, "Can't start listening for calls"); /* * Unregister from endpoint mapper. */ rpc_ep_unregister( greetif_v1_0_s_ifspec, binding_vector, NULL, &status); ERROR_CHECK(status, "Can't unregister from endpoint map"); } In this file, the server registers its interface with the RPC runtime. It then retrieves the binding information assigned to it by the runtime. It registers its binding information with the RPC endpoint mapper, and then with the Cell Directory Service. It then is ready to service requests. Before exiting, the server unregisters its information in the endpoint map. 7. Write the server operation code greet_manager.c. The third file that an RPC programmer writes is the code that implements the operations defined in the IDL file. In this case, there is only one operation, greet. The greet_manager.c file implements this operation. /* * greet_manager.c * * Implementation of "greet" interface. */ #include <stdio.h> #include "greet.h" void greet( handle_t h, idl_char *client_greeting, idl_char *server_reply ) { printf("The client says: %s\en", client_greeting); strcpy(server_reply, "Hi, client!"); } The server prints the message it received from the client, then puts its own message in the reply parameter to be sent back to the client. 8. Write any utility code. In addition to the three standard RPC application code files, greet_client.c, greet_server.c, and greet_manager.c, the greet application contains a utility file for handling errors. This file is called util.c. /* * util.c * * Utility routine(s) shared by "greet" client and server programs. */ #include <stdio.h> #include <dce/nbase.h> #include <dce/dce_error.h> void error_exit( error_status_t status, char *text ) { unsigned char error_text[100]; int dummy; dce_error_inq_text(status, error_text, &dummy); fprintf(stderr, "Error: %s - %s\en", text, error_text); exit(1); } The util.c file comes with a header file called util.h. /* * util.h * * Declarations of utility routine(s) shared by "greet" client * and server programs. */ #define ERROR_CHECK(status, text) \e if (status != error_status_ok) error_exit(status, text) void error_exit( error_status_t status, char *text ); 9. Compile the client and server programs. The greet_client and greet_server programs can now be compiled. The client side of the application is compiled using the following command (again, somewhat simplified): cc -o greet_client greet_client.c greet_cstub.o util.o -ldce The server side of the application is compiled as follows: cc -o greet_server greet_server.c greet_manager.c greet_sstub.o util.o -ldce ####