Chapter 6 -- Example Server and Client

Section 6.1: Introduction

This chapter provides a sample program showing the use of Rx. Specifically, the rxdemo application, with all its support files, is documented and examined. The goal is to provide the reader with a fully-developed and operational program illustrating the use of both regular Rx remote procedure calls and streamed RPCs. The full text of the rxdemo application is reproduced in the sections below, along with additional commentary.
Readers wishing to directly experiment with this example Rx application are encouraged to examine the on-line version of rxdemo. Since it is a program of general interest, it has been installed in the usr/contrib tree in the grand.central.org cell. This area contains user-contributed software for the entire AFS community. At the top of this tree is the /afs/grand.central.org/darpa/usr/contrib directory. Both the server-side and client-side rxdemo binaries (rxdemo server and rxdemo client, respectively) may be found in the bin subdirectory. The actual sources reside in the .site/grand.central.org/rxdemo/src subdirectory.
The rxdemo code is composed of two classes of files, namely those written by a human programmer and those generated from the human-written code by the Rxgen tool. Included in the first group of files are:
  • rxdemo.xg This is the RPC interface definition file, providing high-level definitions of the supported calls.
  • rxdemo client.c: This is the rxdemo client program, calling upon the associated server to perform operations defined by rxdemo.xg.
  • rxdemo server.c: This is the rxdemo server program, implementing the operations promised in rxdemo.xg.
  • Makefile: This is the file that directs the compilation and installation of the rxdemo code.
The class of automatically-generated files includes the following items:
  • rxdemo.h: This header file contains the set of constant definitions present in rxdemo.xg, along with information on the RPC opcodes defined for this Rx service.
  • rxdemo.cs.c: This client-side stub file performs all the marshalling and unmarshalling of the arguments for the RPC routines defined in rxdemo.xg.
  • rxdemo.ss.c: This stub file similarly defines all the marshalling and unmarshalling of arguments for the server side of the RPCs, invokes the routines defined within rxdemo server.c to implement the calls, and also provides the dispatcher function.
  • rxdemo.xdr.c: This module defines the routines required to convert complex user-defined data structures appearing as arguments to the Rx RPC calls exported by rxdemo.xg into network byte order, so that correct communication is guaranteed between clients and server with different memory organizations.
The chapter concludes with a section containing sample output from running the rxdemo server and client programs.

Section 6.2: Human-Generated files

The rxdemo application is based on the four human-authored files described in this section. They provide the basis for the construction of the full set of modules needed to implement the specified Rx service.

Section 6.2.1: Interface file: rxdemo.xg

This file serves as the RPC interface definition file for this application. It defines various constants, including the Rx service port to use and the index of the null security object (no encryption is used by rxdemo). It defines the RXDEMO MAX and RXDEMO MIN constants, which will be used by the server as the upper and lower bounds on the number of Rx listener threads to run. It also defines the set of error codes exported by this facility. finally, it provides the RPC function declarations, namely Add() and Getfile(). Note that when building the actual function definitions, Rxgen will prepend the value of the package line in this file, namely "RXDEMO ", to the function declarations. Thus, the generated functions become RXDEMO Add() and RXDEMO Getfile(), respectively. Note the use of the split keyword in the RXDEMO Getfile() declaration, which specifies that this is a streamed call, and actually generates two client-side stub routines (see Section 6.3.1).
 /*======================================================================= 
 * Interface for an example Rx server/client application, using both * * 
 standard and streamed calls.  * ** * Edward R. Zayas * * Transarc 
 Corporation * ** ** * The United States Government has rights in this 
 work pursuant * * to contract no. MDA972-90-C-0036 between the United 
 States Defense * * Advanced Research Projects Agency and Transarc 
 Corporation.  * ** * (C) Copyright 1991 Transarc Corporation * ** * 
 Redistribution and use in source and binary forms are permitted * 
 provided that: (1) source distributions retain this entire copy- * * 
 right notice and comment, and (2) distributions including binaries * * 
 display the following acknowledgement: * ** * ''This product includes 
 software developed by Transarc * * Corporation and its contributors'' * 
 ** * in the documentation or other materials mentioning features or * * 
 use of this software. Neither the name of Transarc nor the names * * of 
 its contributors may be used to endorse or promote products * * derived 
 from this software without specific prior written * * permission.  * ** 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED * 
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
 =======================================================================*/
 
 package RXDEMO_ 
 %#include <rx/rx.h> 
 %#include <rx/rx_null.h> 
 %#define RXDEMO_SERVER_PORT 8000 /* Service port to advertise */
 %#define RXDEMO_SERVICE_PORT 0 /* User server's port */
 %#define RXDEMO_SERVICE_ID 4 /* Service ID */
 %#define RXDEMO_NULL_SECOBJ_IDX 0 /* Index of null security object */
 
 /* Maximum number of requests that will be handled by this service 
  * simultaneously. This number will be guaranteed to execute in 
  * parallel if other service's results are being processed. */
 
 %#define RXDEMO_MAX 3 
 
 /* Minimum number of requests that are guaranteed to be 
  * handled simultaneously. */
 
 %#define RXDEMO_MIN 2 
 
 /* Index of the "null" security class in the sample service. */
 
 %#define RXDEMO_NULL 0 
 
 /* Maximum number of characters in a file name (for demo purposes). */
 
 %#define RXDEMO_NAME_MAX_CHARS 64 
 
 /* Define the max number of bytes to transfer at one shot. */
 
 %#define RXDEMO_BUFF_BYTES 512 
 
 /* Values returned by the RXDEMO_Getfile() call. 
  * RXDEMO_CODE_SUCCESS : Everything went fine. 
  * RXDEMO_CODE_CANT_OPEN : Can't open named file. 
  * RXDEMO_CODE_CANT_STAT : Can't stat open file. 
  * RXDEMO_CODE_CANT_READ : Error reading the open file. 
  * RXDEMO_CODE_WRITE_ERROR : Error writing the open file. */
 
 /* ------------Interface calls defined for this service ----------- */
 %#define RXDEMO_CODE_SUCCESS 0 
 %#define RXDEMO_CODE_CANT_OPEN 1 
 %#define RXDEMO_CODE_CANT_STAT 2 
 %#define RXDEMO_CODE_CANT_READ 3 
 %#define RXDEMO_CODE_WRITE_ERROR 4 
 /* -------------------------------------------------------------------
 * RXDEMO_Add * 
 *      
 * Summary: 
 *      Add the two numbers provided and return the result. * 
 * Parameters: 
 *      int a_first : first operand. 
 *      int a_second : Second operand. 
 *      int *a_result : Sum of the above. * 
 *      Side effects: None.  
 *-------------------------------------------------------------------- */
 
 Add(IN int a, int b, OUT int *result) = 1; 
 /*-------------------------------------------------------------------
 * RXDEMO_Getfile * 
 * Summary: 
 *      Return the contents of the named file in the server's environment. 
 * Parameters: 
 *      STRING a_nameToRead : Name of the file whose contents are to be 
 *      fetched. 
 *      int *a_result : Set to the result of opening and reading the file 
 *      on the server side. * 
 *      Side effects: None. 
 *-------------------------------------------------------------------- */
 
 Getfile(IN string a_nameToRead<RXDEMO_NAME_MAX_CHARS>, OUT int *a_result) 
        split = 2; 

Section 6.2.2: Client Program: rxdemo client.c

The rxdemo client program, rxdemo client, calls upon the associated server to perform operations defined by rxdemo.xg. After its header, it defines a private GetIPAddress() utility routine, which given a character string host name will return its IP address.
 /*======================================================================= 
 % Client side of an example Rx application, using both standard and % % 
 streamed calls.  % %% % Edward R. Zayas % % Transarc Corporation % %% 
 %% % The United States Government has rights in this work pursuant % % 
 to contract no. MDA972-90-C-0036 between the United States Defense % % 
 Advanced Research Projects Agency and Transarc Corporation.  % %% % (C) 
 Copyright 1991 Transarc Corporation % %% % Redistribution and use in source 
 and binary forms are permitted % % provided that: (1) source distributions 
 retain this entire copy- % % right notice and comment, and (2) distributions 
 including binaries % % display the following acknowledgement: % %% % 
 ''This product includes software developed by Transarc % % Corporation and 
 its contributors'' % %% % in the documentation or other materials mentioning 
 features or % % use of this software. Neither the name of Transarc nor the 
 names % % of its contributors may be used to endorse or promote products % % 
 derived from this software without specific prior written % % permission. 
 % %% % THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED 
 % % WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF % % 
 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
 % %=======================================================================
 */
 
 #include <sys/types.h> 
 #include <netdb.h> 
 #include <stdio.h> 
 #include "rxdemo.h" 
 static char pn[] = "rxdemo"; /* Program name */
 static u_long GetIpAddress(a_hostName) char *a_hostName; 
 { /* GetIPAddress */
        static char rn[] = "GetIPAddress"; /* Routine name */
        struct hostent *hostEntP; /* Ptr to host descriptor */
        u_long hostIPAddr; /* Host IP address */
        hostEntP = gethostbyname(a_hostName); 
        if (hostEntP == (struct hostent *)0) { 
                printf("[%s:%s] Host '%s' not found\n", 
                pn, rn, a_hostName); 
                exit(1); 
        } 
        if (hostEntP->h_length != sizeof(u_long)) { 
                printf("[%s:%s] Wrong host address length (%d bytes instead of
                %d)", 
                pn, rn, hostEntP->h_length, sizeof(u_long)); 
                exit(1); 
        } 
        bcopy(hostEntP->h_addr, (char *)&hostIPAddr, sizeof(hostIPAddr)); 
        return(hostIPAddr); 
 } /* GetIpAddress */
The main program section of the client code, after handling its command line arguments, starts off by initializing the Rx facility.
 main(argc, argv) 
 int argc; 
 char **argv; 
 { /* Main */
        struct rx_connection *rxConnP; /* Ptr to server connection */
        struct rx_call *rxCallP; /* Ptr to Rx call descriptor */
        u_long hostIPAddr; /* IP address of chosen host */
        int demoUDPPort; /* UDP port of Rx service */
        struct rx_securityClass *nullSecObjP; /* Ptr to null security object */
        int operand1, operand2; /* Numbers to add int sum; Their sum */
        int code; /* Return code */
        char fileName[64]; /* Buffer for desired file's name */
        long fileDataBytes; /* Num bytes in file to get */
        char buff[RXDEMO_BUFF_BYTES+1]; /* Read buffer */
        int currBytesToRead; /* Num bytes to read in one iteration */
        int maxBytesToRead; /* Max bytes to read in one iteration */
        int bytesReallyRead; /* Num bytes read off Rx stream */
        int getResults; /* Results of the file fetch */
        printf("\n%s: Example Rx client process\n\n", pn); 
        if ((argc < 2) || (argc > 3)) { 
                printf("Usage: rxdemo <HostName> [PortToUse]"); 
                exit(1); 
        } 
        hostIPAddr = GetIpAddress(argv[1]); 
        if (argc > 2) 
                demoUDPPort = atoi(argv[2]); 
        else 
                demoUDPPort = RXDEMO_SERVER_PORT; 
        /* Initialize the Rx facility. */
        code = rx_Init(htons(demoUDPPort)); 
        if (code) { 
                printf("**      Error calling rx_Init(); code is %d\n", code); 
                exit(1); 
        } 
        /* Create a client-side null security object. */
        nullSecObjP = rxnull_NewClientSecurityObject(); 
        if (nullSecObjP == (struct rx_securityClass *)0) { 
                printf("%s: Can't create a null client-side security
                object!\n", pn); 
                exit(1); 
        } 
        /* Set up a connection to the desired Rx service, telling it to use
        * the null security object we just created.  */
        printf("Connecting to Rx server on '%s', IP address 0x%x, UDP port
        %d\n", argv[1], hostIPAddr, demoUDPPort); 
        rxConnP = rx_NewConnection(hostIPAddr, RXDEMO_SERVER_PORT,
        RXDEMO_SERVICE_ID, nullSecObjP, RXDEMO_NULL_SECOBJ_IDX); 
        if (rxConnP == (struct rx_connection *)0) { 
                printf("rxdemo: Can't create connection to server!\n"); 
                exit(1); 
        } else 
                printf(" ---> Connected.\n"); 
The rx Init() invocation initializes the Rx library and defines the desired service UDP port (in network byte order). The rxnull NewClientSecurityObject() call creates a client-side Rx security object that does not perform any authentication on Rx calls. Once a client authentication object is in hand, the program calls rx NewConnection(), specifying the host, UDP port, Rx service ID, and security information needed to establish contact with the rxdemo server entity that will be providing the service.
With the Rx connection in place, the program may perform RPCs. The first one to be invoked is RXDEMO Add():
 /* Perform our first, simple remote procedure call. */
 operand1 = 1; 
 operand2 = 2; 
 printf("Asking server to add %d and %d: ", operand1, operand2); 
 code = RXDEMO_Add(rxConnP, operand1, operand2, &sum); 
 if (code) { 
        printf("  // ** Error in the RXDEMO_Add RPC: code is %d\n", code); 
        exit(1); 
 } 
 printf("Reported sum is %d\n", sum); 
The first argument to RXDEMO Add() is a pointer to the Rx connection established above. The client-side body of the RXDEMO Add() function was generated from the rxdemo.xg interface file, and resides in the rxdemo.cs.c file (see Section 6.3.1). It gives the appearance of being a normal C procedure call.
The second RPC invocation involves the more complex, streamed RXDEMO Getfile() function. More of the internal Rx workings are exposed in this type of call. The first additional detail to consider is that we must manually create a new Rx call on the connection.
 /* Set up for our second, streamed procedure call. */
 printf("Name of file to read from server: "); 
 scanf("%s", fileName); 
 maxBytesToRead = RXDEMO_BUFF_BYTES; 
 printf("Setting up an Rx call for RXDEMO_Getfile..."); 
 rxCallP = rx_NewCall(rxConnP); 
 if (rxCallP == (struct rx_call *)0) { 
        printf("** Can't create call\n"); 
        exit(1); 
 } 
 printf("done\n"); 
Once the Rx call structure has been created, we may begin executing the call itself. Having been declared to be split in the interface file, Rxgen creates two function bodies for rxdemo Getfile() and places them in rxdemo.cs.c. The first, StartRXDEMO Getfile(), is responsible for marshalling the outgoing arguments and issuing the RPC. The second, EndRXDEMO Getfile(), takes care of unmarshalling the non-streamed OUT function parameters. The following code fragment illustrates how the RPC is started, using the StartRXDEMO Getfile() routine to pass the call parameters to the server.
 /* Sending IN parameters for the streamed call. */
 code = StartRXDEMO_Getfile(rxCallP, fileName); 
 if (code) { 
        printf("**      Error calling StartRXDEMO_Getfile(); code is %d\n",
        code); 
        exit(1); 
 } 
Once the call parameters have been shipped, the server will commence delivering the "stream" data bytes back to the client on the given Rx call structure. The first longword to come back on the stream specifies the number of bytes to follow.
Begin reading the data being shipped from the server in response to * our setup call. The first longword coming back on the Rx call is the number of bytes to follow. It appears in network byte order, so we have to fix it up before referring to it.
 bytesReallyRead = rx_Read(rxCallP, &fileDataBytes, sizeof(long)); 
 if (bytesReallyRead != sizeof(long)) { 
        printf("** Only %d bytes read for file length; should have been %d\n",
        bytesReallyRead, sizeof(long)); 
        exit(1); 
 } 
 fileDataBytes = ntohl(fileDataBytes); 
Once the client knows how many bytes will be sent, it runs a loop in which it reads a buffer at a time from the Rx call stream, using rx Read() to accomplish this. In this application, all that is done with each newly-acquired buffer of information is printing it out.
 /* Read the file bytes via the Rx call, a buffer at a time. */
 printf("[file contents (%d bytes) fetched over the Rx call appear
 below]\n\n", fileDataBytes); 
 while (fileDataBytes > 0) 
 { 
        currBytesToRead = (fileDataBytes > maxBytesToRead ?  maxBytesToRead :
        fileDataBytes); 
        bytesReallyRead = rx_Read(rxCallP, buff, currBytesToRead); 
        if (bytesReallyRead != currBytesToRead)
        { 
                printf("\nExpecting %d bytes on this read, got %d instead\n",
                currBytesToRead, bytesReallyRead); 
                exit(1); 
        }  
        /* Null-terminate the chunk before printing it. */
        buff[currBytesToRead] = 0; 
        printf("%s", buff); 
        /* Adjust the number of bytes left to read. */
        fileDataBytes -= currBytesToRead; 
 } /* Read one bufferful of the file */
After this loop terminates, the Rx stream has been drained of all data. The Rx call is concluded by invoking the second of the two automatically-generated functions, EndRXDEMO Getfile(), which retrieves the call's OUT parameter from the server.
 /* finish off the Rx call, getting the OUT parameters. */
 printf("\n\n[End of file data]\n"); 
 code = EndRXDEMO_Getfile(rxCallP, &getResults); 
 if (code) 
 { 
        printf("**      Error getting file transfer results; code is %d\n",
        code); 
        exit(1); 
 } 
With both normal and streamed Rx calls accomplished, the client demo code concludes by terminating the Rx call it set up earlier. With that done, the client exits.
 /* finish off the Rx call. */
 code = rx_EndCall(rxCallP, code); 
 if (code) 
        printf("Error   in calling rx_EndCall(); code is %d\n", code); 
 
 printf("\n\nrxdemo complete.\n"); 

Server Program: rxdemo server.c

The rxdemo server program, rxdemo server, implements the operations promised in the rxdemo.xg interface file.
After the initial header, the external function RXDEMO ExecuteRequest() is declared. The RXDEMO ExecuteRequest() function is generated automatically by rxgen from the interface file and deposited in rxdemo.ss.c. The main program listed below will associate this RXDEMO ExecuteRequest() routine with the Rx service to be instantiated.
 /*======================================================================
 % % Advanced Research Projects Agency and Transarc Corporation.  % %% % 
 (C) Copyright 1991 Transarc Corporation % %% % Redistribution and use in 
 source and binary forms are permitted % % provided that: (1) source 
 distributions retain this entire copy- % % right notice and comment, and 
 (2) distributions including binaries % % display the following 
 acknowledgement: % %% % ''This product includes software developed by 
 Transarc % % Corporation and its contributors'' % %% % in the documentation 
 or other materials mentioning features or % % use of this software. Neither 
 the name of Transarc nor the names % % of its contributors may be used to 
 endorse or promote products % % derived from this software without specific 
 prior written % % permission.  % %% % THIS SOFTWARE IS PROVIDED "AS IS" AND 
 WITHOUT ANY EXPRESS OR IMPLIED % % WARRANTIES, INCLUDING, WITHOUT
 LIMITATION, 
 THE IMPLIED WARRANTIES OF % % MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 PURPOSE.  % %
 ====================================================================== */
 
 /* Server portion of the example RXDEMO application, using both % 
 standard and streamed calls. % % Edward R. Zayas % Transarc Corporation % 
 % % The United States Government has rights in this work pursuant % 
 to contract no. MDA972-90-C-0036 between the United States Defense % */
 
 #include <sys/types.h> 
 #include <sys/stat.h> 
 #include <sys/file.h> 
 #include <netdb.h> 
 #include <stdio.h> 
 #include "rxdemo.h" 
 #define N_SECURITY_OBJECTS 1 
 extern RXDEMO_ExecuteRequest(); 
After choosing either the default or user-specified UDP port on which the Rx service will be established, rx Init() is called to set up the library.
 main(argc, argv) 
        int argc; 
        char **argv; 
 { /* Main */
        static char pn[] = "rxdemo_server"; /* Program name */
        struct rx_securityClass 
        (securityObjects[1]); /* Security objs */
        struct rx_service *rxServiceP; /* Ptr to Rx service descriptor */
        struct rx_call *rxCallP; /* Ptr to Rx call descriptor */
        int demoUDPPort; /* UDP port of Rx service */
        int fd; /* file descriptor */
        int code; /* Return code */
        printf("\n%s: Example Rx server process\n\n", pn); 
        if (argc >2) { 
                printf("Usage: rxdemo [PortToUse]"); 
                exit(1); 
        } 
        if (argc > 1) 
                demoUDPPort = atoi(argv[1]); 
        else 
                demoUDPPort = RXDEMO_SERVER_PORT; 
 
        /* Initialize the Rx facility, telling it the UDP port number this 
        * server will use for its single service.  */
 
        printf("Listening on UDP port %d\n", demoUDPPort); 
        code = rx_Init(demoUDPPort); 
        if (code) { 
                printf("**      Error calling rx_Init(); code is %d\n", code); 
                exit(1); 
        } 
A security object specific to the server side of an Rx conversation is created in the next code fragment. As with the client side of the code, a "null" server security object, namely one that does not perform any authentication at all, is constructed with the rxnull NewServerSecurityObject() function.
        /* Create a single server-side security object. In this case, the 
        * null security object (for unauthenticated connections) will be used 
        * to control security on connections made to this server. */
 
        securityObjects[RXDEMO_NULL_SECOBJ_IDX] =
        rxnull_NewServerSecurityObject(); 
        if (securityObjects[RXDEMO_NULL_SECOBJ_IDX] == (struct rx_securityClass
        *) 0) { 
                printf("** Can't create server-side security object\n"); 
                exit(1); 
        } 
The rxdemo server program is now in a position to create the desired Rx service, primed to recognize exactly those interface calls defined in rxdemo.xg. This is accomplished by calling the rx NewService() library routine, passing it the security object created above and the generated Rx dispatcher routine.
 /* Instantiate a single sample service. The rxgen-generated procedure 
 * called to dispatch requests is passed in (RXDEMO_ExecuteRequest).  */
 
        rxServiceP = rx_NewService(     0, 
                                        RXDEMO_SERVICE_ID, 
                                        "rxdemo", 
                                        securityObjects, 
                                        1, 
                                        RXDEMO_ExecuteRequest
                                ); 
        if (rxServiceP == (struct rx_service *) 0) { 
                printf("** Can't create Rx service\n"); 
                exit(1); 
        } 
The final step in this main routine is to activate servicing of calls to the exported Rx interface. Specifically, the proper number of threads are created to handle incoming interface calls. Since we are passing a non-zero argument to the rx StartServer() call, the main program will itself begin executing the server thread loop, never returning from the rx StartServer() call. The print statement afterwards should never be executed, and its presence represents some level of paranoia, useful for debugging malfunctioning thread packages.
        /* Start up Rx services, donating this thread to the server pool. */
        rx_StartServer(1); 
        /* We should never return from the previous call. */
        printf("** rx_StartServer() returned!!\n"); exit(1); 
 } /* Main */
Following the main procedure are the functions called by the automatically-generated routines in the rxdemo.ss.c module to implement the specific routines defined in the Rx interface.
The first to be defined is the RXDEMO Add() function. The arguments for this routine are exactly as they appear in the interface definition, with the exception of the very first. The a rxCallP parameter is a pointer to the Rx structure describing the call on which this function was activated. All user-supplied routines implementing an interface function are required to have a pointer to this structure as their first parameter. Other than printing out the fact that it has been called and which operands it received, all that RXDEMO Add() does is compute the sum and place it in the output parameter.
Since RXDEMO Add() is a non-streamed function, with all data travelling through the set of parameters, this is all that needs to be done. To mark a successful completion, RXDEMO Add() returns zero, which is passed all the way through to the RPC's client.
 int RXDEMO_Add(a_rxCallP, a_operand1, a_operand2, a_resultP) 
        struct rx_call *a_rxCallP; 
 int a_operand1, a_operand2; 
 int *a_resultP; 
 { /* RXDEMO_Add */
        printf("\t[Handling call to RXDEMO_Add(%d, %d)]\n", 
                a_operand1, a_operand2); 
        *a_resultP = a_operand1 + a_operand2; 
        return(0); 
 } /* RXDEMO_Add */
The next and final interface routine defined in this file is RXDEMO Getfile(). Declared as a split function in the interface file, RXDEMO Getfile() is an example of a streamed Rx call. As with RXDEMO Add(), the initial parameter is required to be a pointer to the Rx call structure with which this routine is associated, Similarly, the other parameters appear exactly as in the interface definition, and are handled identically.
The difference between RXDEMO Add() and RXDEMO Getfile() is in the use of the rx Write() library routine by RXDEMO Getfile() to feed the desired file's data directly into the Rx call stream. This is an example of the use of the a rxCallP argument, providing all the information necessary to support the rx Write() activity.
The RXDEMO Getfile() function begins by printing out the fact that it's been called and the name of the requested file. It will then attempt to open the requested file and stat it to determine its size.
 int RXDEMO_Getfile(a_rxCallP, a_nameToRead, a_resultP) 
        struct rx_call *a_rxCallP; 
 char *a_nameToRead; 
 int *a_resultP; 
 { /* RXDEMO_Getfile */
        struct stat fileStat; /* Stat structure for file */
        long fileBytes; /* Size of file in bytes */
        long nbofileBytes; /* file bytes in network byte order */
        int code; /* Return code */
        int bytesReallyWritten; /* Bytes written on Rx channel */
        int bytesToSend; /* Num bytes to read & send this time */
        int maxBytesToSend; /* Max num bytes to read & send ever */
        int bytesRead; /* Num bytes read from file */
        char buff[RXDEMO_BUFF_BYTES+1]; /* Read buffer */
        int fd; /* file descriptor */
        maxBytesToSend = RXDEMO_BUFF_BYTES; 
        printf("\t[Handling call to RXDEMO_Getfile(%s)]\n", a_nameToRead); 
        fd = open(a_nameToRead, O_RDONLY, 0444); 
        if (fd <0) { 
                printf("\t\t[**Can't open file '%s']\n", a_nameToRead); 
                *a_resultP = RXDEMO_CODE_CANT_OPEN; 
                return(1); 
        } else 
                printf("\t\t[file opened]\n"); 
        /* Stat the file to find out how big it is. */
        code = fstat(fd, &fileStat); 
        if (code) { 
                a_resultP = RXDEMO_CODE_CANT_STAT; 
                printf("\t\t[file closed]\n"); 
                close(fd); 
                return(1); 
        } 
        fileBytes = fileStat.st_size; 
        printf("\t\t[file has %d bytes]\n", fileBytes); 
Only standard unix operations have been used so far. Now that the file is open, we must first feed the size of the file, in bytes, to the Rx call stream. With this information, the client code can then determine how many bytes will follow on the stream. As with all data that flows through an Rx stream, the longword containing the file size, in bytes, must be converted to network byte order before being sent. This insures that the recipient may properly interpret the streamed information, regardless of its memory architecture.
        nbofileBytes = htonl(fileBytes); 
        /* Write out the size of the file to the Rx call. */
        bytesReallyWritten = rx_Write(a_rxCallP, &nbofileBytes, sizeof(long)); 
        if (bytesReallyWritten != sizeof(long)) { 
                printf("** %d bytes written instead of %d for file length\n", 
                bytesReallyWritten, sizeof(long)); 
                *a_resultP = RXDEMO_CODE_WRITE_ERROR; 
                printf("\t\t[file closed]\n"); 
                close(fd); 
                return(1); 
        } 
Once the number of file bytes has been placed in the stream, the RXDEMO Getfile() routine runs a loop, reading a buffer's worth of the file and then inserting that buffer of file data into the Rx stream at each iteration. This loop executes until all of the file's bytes have been shipped. Notice there is no special end-of-file character or marker inserted into the stream.
The body of the loop checks for both unix read() and rx Write errors. If there is a problem reading from the unix file into the transfer buffer, it is reflected back to the client by setting the error return parameter appropriately. Specifically, an individual unix read() operation could fail to return the desired number of bytes. Problems with rx Write() are handled similarly. All errors discovered in the loop result in the file being closed, and RXDEMO Getfile() exiting with a non-zero return value.
        /* Write out the contents of the file, one buffer at a time.  */
        while (fileBytes > 0) {  
                /* figure out the number of bytes to 
                * read (and send) this time.  */
                bytesToSend = (fileBytes > maxBytesToSend ? 
                                maxBytesToSend : fileBytes); 
                bytesRead = read(fd, buff, bytesToSend); 
                if (bytesRead != bytesToSend) { 
                        printf("Read %d instead of %d bytes from the file\n", 
                                bytesRead, bytesToSend); 
                        *a_resultP = RXDEMO_CODE_WRITE_ERROR; 
                        printf("\t\t[file closed]\n"); 
                        close(fd); 
                        return(1); 
                } 
                /* Go ahead and send them. */
                bytesReallyWritten = rx_Write(a_rxCallP, buff, bytesToSend); 
                if (bytesReallyWritten != bytesToSend) { 
                        printf("%d file bytes written instead of %d\n", 
                                bytesReallyWritten, bytesToSend); 
                        *a_resultP = RXDEMO_CODE_WRITE_ERROR; 
                        printf("\t\t[file closed]\n"); 
                        close(fd); 
                        return(1); 
                } 
                /* Update the number of bytes left to go. */
                fileBytes -= bytesToSend; 
        } /* Write out the file to our caller */
Once all of the file's bytes have been shipped to the remote client, all that remains to be done is to close the file and return successfully.
        /* Close the file, then return happily. */
        *a_resultP = RXDEMO_CODE_SUCCESS; 
        printf("\t\t[file closed]\n"); 
        close(fd); 
        return(0); 
 } /* RXDEMO_Getfile */

Section 6.2.4: Makefile

This file directs the compilation and installation of the rxdemo code. It specifies the locations of libraries, include files, sources, and such tools as Rxgen and install, which strips symbol tables from executables and places them in their target directories. This Makefile demostrates cross-cell software development, with the rxdemo sources residing in the grand.central.org cell and the AFS include files and libraries accessed from their locations in the transarc.com cell.
In order to produce and install the rxdemo server and rxdemo client binaries, the system target should be specified on the command line when invoking make:
                make system 
A note of caution is in order concerning generation of the rxdemo binaries. While tools exist that deposit the results of all compilations to other (architecture-specific) directories, and thus facilitate multiple simultaneous builds across a variety of machine architectures (e.g., Transarc's washtool), the assumption is made here that compilations will take place directly in the directory containing all the rxdemo sources. Thus, a user will have to execute a make clean command to remove all machine-specific object, library, and executable files before compiling for a different architecture. Note, though, that the binaries are installed into a directory specifically reserved for the current machine type. Specifically, the final pathname component of the ${PROJ DIR}bin installation target is really a symbolic link to ${PROJ DIR}.bin/.
Two libraries are needed to support the rxdemo code. The first is obvious, namely the Rx librx.a library. The second is the lightweight thread package library, liblwp.a, which implements all the threading operations that must be performed. The include files are taken from the unix /usr/include directory, along with various AFS-specific directories. Note that for portability reasons, this Makefile only contains fully-qualified AFS pathnames and "standard" unix pathnames (such as /usr/include).
 /*#=======================================================================# 
 # The United States Government has rights in this work pursuant # # to 
 contract no. MDA972-90-C-0036 between the United States Defense # # Advanced 
 Research Projects Agency and Transarc Corporation. # # # # (C) Copyright
 1991 
 Transarc Corporation # # # # Redistribution and use in source and binary
 forms 
 are permitted # # provided that: (1) source distributions retain this entire 
 copy-# # right notice and comment, and (2) distributions including binaries
 # 
 # display the following acknowledgement: # # # # ''This product includes 
 software developed by Transarc # # Corporation and its contributors'' # # #
 # 
 in the documentation or other materials mentioning features or # # use of
 this 
 software. Neither the name of Transarc nor the names # # of its contributors 
 may be used to endorse or promote products # # derived from this software 
 without specific prior written # # permission. # # # # THIS SOFTWARE IS 
 PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED # # WARRANTIES,
 INCLUDING, 
 WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF # # MERCHANTABILITY AND
 FITNESS 
 FOR A PARTICULAR PURPOSE. # 
 #=======================================================================# */
 
 SHELL = /bin/sh 
 TOOL_CELL = grand.central.org 
 AFS_INCLIB_CELL = transarc.com 
 USR_CONTRIB = /afs/${TOOL_CELL}/darpa/usr/contrib/ 
 PROJ_DIR = ${USR_CONTRIB}.site/grand.central.org/rxdemo/ 
 AFS_INCLIB_DIR = /afs/${AFS_INCLIB_CELL}/afs/dest/ 
 RXGEN = ${AFS_INCLIB_DIR}bin/rxgen 
 INSTALL = ${AFS_INCLIB_DIR}bin/install 
 LIBS =         ${AFS_INCLIB_DIR}lib/librx.a \ ${AFS_INCLIB_DIR}lib/liblwp.a 
 CFLAGS = -g \ 
        -I. \ 
        -I${AFS_INCLIB_DIR}include \ 
        -I${AFS_INCLIB_DIR}include/afs \ 
        -I${AFS_INCLIB_DIR} \ 
        -I/usr/include 
 
 system: install 
 
 install: all 
        ${INSTALL} rxdemo_client 
        ${PROJ_DIR}bin 
        ${INSTALL} rxdemo_server 
        ${PROJ_DIR}bin 
 
 all: rxdemo_client rxdemo_server 
 
 rxdemo_client: rxdemo_client.o ${LIBS} rxdemo.cs.o ${CC} ${CFLAGS} 
                -o rxdemo_client rxdemo_client.o rxdemo.cs.o ${LIBS} 
 
 rxdemo_server: rxdemo_server.o rxdemo.ss.o ${LIBS} ${CC} ${CFLAGS} 
                -o rxdemo_server rxdemo_server.o rxdemo.ss.o ${LIBS} 
 
 rxdemo_client.o: rxdemo.h 
 
 rxdemo_server.o: rxdemo.h 
 
 rxdemo.cs.c rxdemo.ss.c rxdemo.er.c rxdemo.h: rxdemo.xg rxgen rxdemo.xg 
 
 clean: rm -f *.o rxdemo.cs.c rxdemo.ss.c rxdemo.xdr.c rxdemo.h \ 
                rxdemo_client rxdemo_server core 

Section 6.3: Computer-Generated files

The four human-generated files described above provide all the information necessary to construct the full set of modules to support the rxdemo example application. This section describes those routines that are generated from the base set by Rxgen, filling out the code required to implement an Rx service.

Client-Side Routines: rxdemo.cs.c

The rxdemo client.c program, described in Section 6.2.2, calls the client-side stub routines contained in this module in order to make rxdemo RPCs. Basically, these client-side stubs are responsible for creating new Rx calls on the given connection parameter and then marshalling and unmarshalling the rest of the interface call parameters. The IN and INOUT arguments, namely those that are to be delivered to the server-side code implementing the call, must be packaged in network byte order and shipped along the given Rx call. The return parameters, namely those objects declared as INOUT and OUT, must be fetched from the server side of the associated Rx call, put back in host byte order, and inserted into the appropriate parameter variables.
The first part of rxdemo.cs.c echoes the definitions appearing in the rxdemo.xg interface file, and also #includes another Rxgen-generated file, rxdemo.h.
 /*======================================================================% 
 * % THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED % 
 * % WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF % 
 * % MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. % 
 * %====================================================================== */
 /* Machine generated file --Do NOT edit */
 
 #include "rxdemo.h" 
 #define RXDEMO_CODE_WRITE_ERROR 4 
 
 #include <rx/rx.h>
 #include <rx/rx_null.h>
 #define RXDEMO_SERVER_PORT 8000 /* Service port to advertise */
 #define RXDEMO_SERVICE_PORT 0 /* User server's port */
 #define RXDEMO_SERVICE_ID 4 /* Service ID */
 #define RXDEMO_NULL_SECOBJ_IDX 0 /* Index of null security object */
 #define RXDEMO_MAX 3
 #define RXDEMO_MIN 2
 #define RXDEMO_NULL 0 
 #define RXDEMO_NAME_MAX_CHARS 64
 #define RXDEMO_BUFF_BYTES 512
 #define RXDEMO_CODE_SUCCESS 0
 #define RXDEMO_CODE_CANT_OPEN 1
 #define RXDEMO_CODE_CANT_STAT 2
 #define RXDEMO_CODE_CANT_READ 3
 #define RXDEMO_CODE_WRITE_ERROR 4
The next code fragment defines the client-side stub for the RXDEMO Add() routine, called by the rxdemo client program to execute the associated RPC.
 int RXDEMO_Add(z_conn, a, b, result) register struct rx_connection *z_conn; 
 int a, b; 
 int * result; 
 { 
        struct rx_call *z_call = rx_NewCall(z_conn); 
        static int z_op = 1; 
        int z_result; 
        XDR z_xdrs; 
        xdrrx_create(&z_xdrs, z_call, XDR_ENCODE); 
        /* Marshal the arguments */
        if ((!xdr_int(&z_xdrs, &z_op)) 
                        || (!xdr_int(&z_xdrs, &a)) 
                        || (!xdr_int(&z_xdrs, &b))) { 
                z_result = RXGEN_CC_MARSHAL; 
                goto fail; 
        } 
        /* Un-marshal the reply arguments */
        z_xdrs.x_op = XDR_DECODE; 
        if ((!xdr_int(&z_xdrs, result))) { 
                z_result = RXGEN_CC_UNMARSHAL; 
                goto fail; 
        } 
        z_result = RXGEN_SUCCESS; 
        fail: return rx_EndCall(z_call, z_result); 
 } 
The very first operation performed by RXDEMO Add() occurs in the local variable declarations, where z call is set to point to the structure describing a newly-created Rx call on the given connection. An XDR structure, z xdrs, is then created for the given Rx call with xdrrx create(). This XDR object is used to deliver the proper arguments, in network byte order, to the matching server stub code. Three calls to xdr int() follow, which insert the appropriate Rx opcode and the two operands into the Rx call. With the IN arguments thus transmitted, RXDEMO Add() prepares to pull the value of the single OUT parameter. The z xdrs XDR structure, originally set to XDR ENCODE objects, is now reset to XDR DECODE to convert further items received into host byte order. Once the return parameter promised by the function is retrieved, RXDEMO Add() returns successfully.
Should any failure occur in passing the parameters to and from the server side of the call, the branch to fail will invoke Rx EndCall(), which advises the server that the call has come to a premature end (see Section 5.6.6 for full details on rx EndCall() and the meaning of its return value).
The next client-side stub appearing in this generated file handles the delivery of the IN parameters for StartRXDEMO Getfile(). It operates identically as the RXDEMO Add() stub routine in this respect, except that it does not attempt to retrieve the OUT parameter. Since this is a streamed call, the number of bytes that will be placed on the Rx stream cannot be determined at compile time, and must be handled explicitly by rxdemo client.c.
 int StartRXDEMO_Getfile(z_call, a_nameToRead) 
        register struct rx_call *z_call; 
 char * a_nameToRead; 
 { 
        static int z_op = 2; 
        int z_result; 
        XDR z_xdrs; 
        xdrrx_create(&z_xdrs, z_call, XDR_ENCODE); 
        /* Marshal the arguments */
        if ((!xdr_int(&z_xdrs, &z_op)) || (!xdr_string(&z_xdrs, &a_nameToRead,
        RXDEMO_NAME_MAX_CHARS))) { 
                z_result = RXGEN_CC_MARSHAL; 
                goto fail; 
        } 
        z_result = RXGEN_SUCCESS; 
        fail: return z_result; 
 } 
The final stub routine appearing in this generated file, EndRXDEMO Getfile(), handles the case where rxdemo client.c has already successfully recovered the unbounded streamed data appearing on the call, and then simply has to fetch the OUT parameter. This routine behaves identially to the latter portion of RXDEMO Getfile().
 int EndRXDEMO_Getfile(z_call, a_result) 
        register struct rx_call *z_call; 
 int * a_result; 
 { 
        int z_result; 
        XDR z_xdrs; 
        /* Un-marshal the reply arguments */
        xdrrx_create(&z_xdrs, z_call, XDR_DECODE); 
        if ((!xdr_int(&z_xdrs, a_result))) { 
                z_result = RXGEN_CC_UNMARSHAL; 
                goto fail; 
        } 
        z_result = RXGEN_SUCCESS; fail: 
        return z_result; 
 } 

Server-Side Routines: rxdemo.ss.c

This generated file provides the core components required to implement the server side of the rxdemo RPC service. Included in this file is the generated dispatcher routine, RXDEMO ExecuteRequest(), which the rx NewService() invocation in rxdemo server.c uses to construct the body of each listener thread's loop. Also included are the server-side stubs to handle marshalling and unmarshalling of parameters for each defined RPC call (i.e., RXDEMO Add() and RXDEMO Getfile()). These stubs are called by RXDEMO ExecuteRequest(). The routine to be called by RXDEMO ExecuteRequest() depends on the opcode received, which appears as the very first longword in the call data.
As usual, the first fragment is copyright information followed by the body of the definitions from the interface file.
 /*======================================================================% 
 % Edward R. Zayas % % Transarc Corporation % % % % % % The United States 
 Government has rights in this work pursuant % % to contract no. 
 MDA972-90-C-0036 between the United States Defense % % Advanced Research 
 Projects Agency and Transarc Corporation. % % % % (C) Copyright 1991 
 Transarc Corporation % % % % Redistribution and use in source and binary 
 forms are permitted % % provided that: (1) source distributions retain 
 this entire copy¬% % right notice and comment, and (2) distributions 
 including binaries % 
 %====================================================================== */
 /* Machine generated file --Do NOT edit */
 
 #include "rxdemo.h" 
 #include <rx/rx.h> 
 #include <rx/rx_null.h> 
 #define RXDEMO_SERVER_PORT 8000 /* Service port to advertise */
 #define RXDEMO_SERVICE_PORT 0 /* User server's port */
 #define RXDEMO_SERVICE_ID 4 /* Service ID */
 #define RXDEMO_NULL_SECOBJ_IDX 0 /* Index of null security object */
 #define RXDEMO_MAX 3 
 #define RXDEMO_MIN 2 
 #define RXDEMO_NULL 0 
 #define RXDEMO_NAME_MAX_CHARS 64 
 #define RXDEMO_BUFF_BYTES 512 
 #define RXDEMO_CODE_SUCCESS 0 
 #define RXDEMO_CODE_CANT_OPEN 1 
 #define RXDEMO_CODE_CANT_STAT 2 
 #define RXDEMO_CODE_CANT_READ 3 
 #define RXDEMO_CODE_WRITE_ERROR 4 
After this preamble, the first server-side stub appears. This RXDEMO Add() routine is basically the inverse of the RXDEMO Add() client-side stub defined in rxdemo.cs.c. Its job is to unmarshall the IN parameters for the call, invoke the "true" server-side RXDEMO Add() routine (defined in rxdemo server.c), and then package and ship the OUT parameter. Being so similar to the client-side RXDEMO Add(), no further discussion is offered here.
 long _RXDEMO_Add(z_call, z_xdrs) 
        struct rx_call *z_call; 
 XDR *z_xdrs; 
 { 
        long z_result; 
        int a, b; 
        int result; 
        if ((!xdr_int(z_xdrs, &a)) || (!xdr_int(z_xdrs, &b))) 
        { 
                z_result = RXGEN_SS_UNMARSHAL; 
                goto fail; 
        } 
        z_result = RXDEMO_Add(z_call, a, b, &result); 
        z_xdrs->x_op = XDR_ENCODE; 
        if ((!xdr_int(z_xdrs, &result))) 
                z_result = RXGEN_SS_MARSHAL; 
        fail: return z_result; 
 } 
The second server-side stub, RXDEMO Getfile(), appears next. It operates identically to RXDEMO Add(), first unmarshalling the IN arguments, then invoking the routine that actually performs the server-side work for the call, then finishing up by returning the OUT parameters.
 long _RXDEMO_Getfile(z_call, z_xdrs) 
        struct rx_call *z_call; 
 XDR *z_xdrs; 
 { 
        long z_result; 
        char * a_nameToRead=(char *)0; 
        int a_result; 
        if ((!xdr_string(z_xdrs, &a_nameToRead, RXDEMO_NAME_MAX_CHARS))) { 
                z_result = RXGEN_SS_UNMARSHAL; 
                goto fail; 
        } 
        z_result = RXDEMO_Getfile(z_call, a_nameToRead, &a_result); 
        z_xdrs->x_op = XDR_ENCODE; 
        if ((!xdr_int(z_xdrs, &a_result))) 
                z_result = RXGEN_SS_MARSHAL; 
        fail: z_xdrs->x_op = XDR_FREE; 
        if (!xdr_string(z_xdrs, &a_nameToRead, RXDEMO_NAME_MAX_CHARS)) 
                goto fail1; 
        return z_result; 
        fail1: return RXGEN_SS_XDRFREE; 
 } 
The next portion of the automatically generated server-side module sets up the dispatcher routine for incoming Rx calls. The above stub routines are placed into an array in opcode order.
 long _RXDEMO_Add(); 
 long _RXDEMO_Getfile(); 
 static long (*StubProcsArray0[])() = {_RXDEMO_Add, _RXDEMO_Getfile}; 
The dispatcher routine itself, RXDEMO ExecuteRequest, appears next. This is the function provided to the rx NewService() call in rxdemo server.c, and it is used as the body of each listener thread's service loop. When activated, it decodes the first longword in the given Rx call, which contains the opcode. It then dispatches the call based on this opcode, invoking the appropriate server-side stub as organized in the StubProcsArray.
 RXDEMO_ExecuteRequest(z_call) 
        register struct rx_call *z_call; 
 { 
        int op; 
        XDR z_xdrs; 
        long z_result; 
        xdrrx_create(&z_xdrs, z_call, XDR_DECODE); 
        if (!xdr_int(&z_xdrs, &op)) 
                z_result = RXGEN_DECODE; 
        else if (op < RXDEMO_LOWEST_OPCODE || op > RXDEMO_HIGHEST_OPCODE) 
                z_result = RXGEN_OPCODE; 
        else 
                z_result = (*StubProcsArray0[op -RXDEMO_LOWEST_OPCODE])(z_call,
                &z_xdrs); 
        return z_result; 
 } 

External Data Rep file: rxdemo.xdr.c

This file is created to provide the special routines needed to map any user-defined structures appearing as Rx arguments into and out of network byte order. Again, all on-thewire data appears in network byte order, insuring proper communication between servers and clients with different memory organizations.
Since the rxdemo example application does not define any special structures to pass as arguments in its calls, this generated file contains only the set of definitions appearing in the interface file. In general, though, should the user define a struct xyz and use it as a parameter to an RPC function, this file would contain a routine named xdr xyz(), which converted the structure field-by-field to and from network byte order.
 /*======================================================================% 
 %% % in the documentation or other materials mentioning features or % % 
 use of this software. Neither the name of Transarc nor the names % % of 
 its contributors may be used to endorse or promote products % % derived 
 from this software without specific prior written % % permission. % % % 
 % THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED % 
 % WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF % 
 % MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. % 
 % Edward R. Zayas % Transarc Corporation % % % The United States 
 Government has rights in this work pursuant to contract no. 
 MDA972-90-C-0036 between the United States Defense % Advanced Research 
 Projects Agency and Transarc Corporation. % % (C) Copyright 1991 Transarc 
 Corporation % % Redistribution and use in source and binary forms are 
 permitted % % provided that: (1) source distributions retain this entire 
 copy¬ % right notice and comment, and (2) distributions including binaries 
 % % display the following acknowledgement: % % % % ``This product includes 
 software developed by Transarc % % Corporation and its contributors'' % 
 %====================================================================== */
 /* Machine generated file --Do NOT edit */
 
 #include "rxdemo.h" 
 #include <rx/rx.h> 
 #include <rx/rx_null.h> 
 #define RXDEMO_SERVER_PORT 8000 /* Service port to advertise */
 #define RXDEMO_SERVICE_PORT 0 /* User server's port */
 #define RXDEMO_SERVICE_ID 4 /* Service ID */
 #define RXDEMO_NULL_SECOBJ_IDX 0 /* Index of null security object */
 #define RXDEMO_MAX 3 
 #define RXDEMO_MIN 2 
 #define RXDEMO_NULL 0 
 #define RXDEMO_NAME_MAX_CHARS 64 
 #define RXDEMO_BUFF_BYTES 512 
 #define RXDEMO_CODE_SUCCESS 0 
 #define RXDEMO_CODE_CANT_OPEN 1 
 #define RXDEMO_CODE_CANT_STAT 2 
 #define RXDEMO_CODE_CANT_READ 3 
 #define RXDEMO_CODE_WRITE_ERROR 4 

Section 6.4: Sample Output

This section contains the output generated by running the example rxdemo server and rxdemo client programs described above. The server end was run on a machine named Apollo, and the client program was run on a machine named Bigtime.
The server program on Apollo was started as follows:
  • apollo: rxdemo_server
  • rxdemo_server: Example Rx server process
  • Listening on UDP port 8000
At this point, rxdemo server has initialized its Rx module and started up its listener LWPs, which are sleeping on the arrival of an RPC from any rxdemo client.
The client portion was then started on Bigtime:
bigtime: rxdemo_client apollo
rxdemo: Example Rx client process
Connecting to Rx server on 'apollo', IP address 0x1acf37c0, UDP port 8000
---> Connected. Asking server to add 1 and 2: Reported sum is 3
The command line instructs rxdemo client to connect to the rxdemo server on host apollo and to use the standard port defined for this service. It reports on the successful Rx connection establishment, and immediately executes an rxdemo Add(1, 2) RPC. It reports that the sum was successfully received. When the RPC request arrived at the server and was dispatched by the rxdemo server code, it printed out the following line:
[Handling call to RXDEMO_Add(1, 2)]
Next, rxdemo client prompts for the name of the file to read from the rxdemo server. It is told to fetch the Makefile for the Rx demo directory. The server is executing in the same directory in which it was compiled, so an absolute name for the Makefile is not required. The client echoes the following:
Name of file to read from server: Makefile Setting up an Rx call for RXDEMO_Getfile...done
As with the rxdemo Add() call, rxdemo server receives this RPC, and prints out the following information:
  • [Handling call to RXDEMO_Getfile(Makefile)]
  • [file opened]
  • [file has 2450 bytes]
  • [file closed]
It successfully opens the named file, and reports on its size in bytes. The rxdemo server program then executes the streamed portion of the rxdemo Getfile call, and when complete, indicates that the file has been closed. Meanwhile, rxdemo client prints out the reported size of the file, follows it with the file's contents, then advises that the test run has completed:
 [file contents (2450 bytes) fetched over the Rx call appear below] 
 
 /*#=======================================================================# 
 # The United States Government has rights in this work pursuant # # to 
 contract no. MDA972-90-C-0036 between the United States Defense # # Advanced 
 Research Projects Agency and Transarc Corporation. # # # # (C) Copyright 
 1991 Transarc Corporation # # # # Redistribution and use in source and 
 binary forms are permitted # # provided that: (1) source distributions 
 retain this entire copy-# # right notice and comment, and (2) distributions 
 including binaries # # display the following acknowledgement: # # # # ''This 
 product includes software developed by Transarc # # Corporation and its 
 contributors'' # # # # in the documentation or other materials mentioning 
 features or # # use of this software. Neither the name of Transarc nor the 
 names # # of its contributors may be used to endorse or promote products # 
 # derived from this software without specific prior written # # permission. 
 # # # # THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED 
 # # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF # # 
 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. # 
 #=======================================================================# */
 
 SHELL = /bin/sh 
 TOOL_CELL = grand.central.org 
 AFS_INCLIB_CELL = transarc.com 
 USR_CONTRIB = /afs/${TOOL_CELL}/darpa/usr/contrib/ 
 PROJ_DIR = ${USR_CONTRIB}.site/grand.central.org/rxdemo/ 
 AFS_INCLIB_DIR = /afs/${AFS_INCLIB_CELL}/afs/dest/ 
 RXGEN = ${AFS_INCLIB_DIR}bin/rxgen 
 INSTALL = ${AFS_INCLIB_DIR}bin/install 
 LIBS =         ${AFS_INCLIB_DIR}lib/librx.a \ ${AFS_INCLIB_DIR}lib/liblwp.a 
        CFLAGS = -g \ 
        -I. \ 
        -I${AFS_INCLIB_DIR}include \ 
        -I${AFS_INCLIB_DIR}include/afs \ 
        -I${AFS_INCLIB_DIR} \ 
        -I/usr/include 
 
 system: install 
 
 install: all 
        ${INSTALL} rxdemo_client ${PROJ_DIR}bin 
        ${INSTALL} rxdemo_server ${PROJ_DIR}bin 
 
 all: rxdemo_client rxdemo_server 
 
 rxdemo_client: rxdemo_client.o ${LIBS} rxdemo.cs.o ${CC} ${CFLAGS} 
        -o rxdemo_client rxdemo_client.o rxdemo.cs.o ${LIBS} 
 
 rxdemo_server: rxdemo_server.o rxdemo.ss.o ${LIBS} ${CC} ${CFLAGS} 
        -o rxdemo_server rxdemo_server.o rxdemo.ss.o ${LIBS} 
 
 rxdemo_client.o: rxdemo.h 
 
 rxdemo_server.o: rxdemo.h 
 
 rxdemo.cs.c rxdemo.ss.c rxdemo.er.c rxdemo.h: rxdemo.xg rxgen rxdemo.xg 
 
 clean: rm -f *.o rxdemo.cs.c rxdemo.ss.c rxdemo.xdr.c rxdemo.h \ 
        rxdemo_client rxdemo_server core 
 
 [End of file data] 
 rxdemo complete. 
The rxdemo server program continues to run after handling these calls, offering its services to any other callers. It can be killed by sending it an interrupt signal using Control-C (or whatever mapping has been set up for the shell's interrupt character).

Bibliography