Sun Remote Procedure Call Mechanism

Originally developed by Sun, but now widely available on other platforms (including Digital Unix). Also known as Open Network Computing (ONC).

Sun RPC package has an RPC compiler (rpcgen) that automatically generates the client and server stubs.

RPC package uses XDR (eXternal Data Representation) to represent data sent between client and server stubs.

Has built-in representation for basic types (int, float, char).

Also provides a declarative language for specifying complex data types.

Remote Date Example

From Stevens Unix Networking book.

Running rpcgen date.x generates date.h, date_clnt.c and date_svc.c. The header file is included with both client and server. The respective C source files are linked with client and server code.

/*
 * date.x  Specification of the remote date and time server
 */
 
/*
 * Define two procedures
 *      bin_date_1() returns the binary date and time (no arguments)
 *      str_date_1() takes a binary time and returns a string
 *
 */
 
program DATE_PROG {
    version DATE_VERS {
        long BIN_DATE(void) = 1;    /* procedure number = 1 */
        string STR_DATE(long) = 2;  /* procedure number = 2 */
    } = 1;                          /* version number = 1 */
} = 0x31234567;                     /* program number = 0x31234567 */

Notes:

/*
 * rdate.c  client program for remote date program
 */
 
#include <stdio.h>

#include <rpc/rpc.h>    /* standard RPC include file */
#include "date.h"       /* this file is generated by rpcgen */

main(int argc, char *argv[])
{
    CLIENT *cl;         /* RPC handle */
    char *server;
    long *lresult;      /* return value from bin_date_1() */
    char **sresult;     /* return value from str_date_1() */
    
    if (argc != 2) {
        fprintf(stderr, "usage: %s hostname\n", argv[0]);
        exit(1);
    }
    
    server = argv[1];
    
    /*
     * Create client handle
     */
    
    if ((cl = clnt_create(server, DATE_PROG, DATE_VERS, "udp")) == NULL) {
        /*
         * can't establish connection with server
         */
         clnt_pcreateerror(server);
         exit(2);
    }
    
    /*
     * First call the remote procedure "bin_date".
     */
     
    if ( (lresult = bin_date_1(NULL, cl)) == NULL) {
        clnt_perror(cl, server);
        exit(3);
    }
    printf("time on host %s = %ld\n",server, *lresult);
    
    /*
     * Now call the remote procedure str_date
     */
     
    if ( (sresult = str_date_1(lresult, cl)) == NULL) {
        clnt_perror(cl, server);
        exit(4);
    }
    printf("time on host %s = %s", server, *sresult);
    
    clnt_destroy(cl);         /* done with the handle */
    exit(0);
}

/*
 * dateproc.c   remote procedures; called by server stub
 */
 
#include <rpc/rpc.h>        /* standard RPC include file */
#include "date.h"           /* this file is generated by rpcgen */

/*
 * Return the binary date and time
 */
 
 long *bin_date_1()
 {
    static long timeval;    /* must be static */
 
    timeval = time((long *) 0);
    return(&timeval);
 }
 
/*
 * Convert a binary time and return a human readable string
 */
  
char **str_date_1(long *bintime)
{
    static char *ptr;       /* must be static */
    ptr = ctime(bintime);   /* convert to local time */
    return(&ptr);
}

Notes:

< wpi /cs/cs4513/public/example/Date 1 >ls

date.x       dateproc.c   rdate.c
< wpi /cs/cs4513/public/example/Date 2 >rpcgen date.x


< wpi /cs/cs4513/public/example/Date 3 >ls

date.h        date_clnt.c   dateproc.c
date.x        date_svc.c    rdate.c
< wpi /cs/cs4513/public/example/Date 4 >cc -o rdate rdate.c date_clnt.c

rdate.c:
date_clnt.c:
< wpi /cs/cs4513/public/example/Date 5 >cc -o dateproc dateproc.c date_svc.c

dateproc.c:
date_svc.c:
< wpi /cs/cs4513/public/example/Date 6 >dateproc&

[1] 17940
< wpi /cs/cs4513/public/example/Date 7 >rdate wpi

time on host wpi = 954778980
time on host wpi = Mon Apr  3 12:23:00 2000

< wpi /cs/cs4513/public/example/Date 8 >exit

exit

What happens with these processes:

  1. Server process creates a UDP socket and binds to a local port. It calls svc_register() routine to register its program number and version with the local port mapper process. This process should have been started as a daemon at system boot time. Server then waits for requests.

  2. Client program contacts the port mapper on the designated machine using UDP. Gets port number for the server process.

  3. Client program calls the client stub for the remote procedure (first bin_date_1() and then str_date_1. Defaults for how long to wait, how many times to retry, etc.

Remote Message Printing Example

Adaptation of the sample program in the rpcgen Programming Guide. Changes:

/*
 * msg.x Remote message printing protocol
 */

program MESSAGEPROG {
    version MESSAGEVERS {
        int PRINTMESSAGE(string) = 1;
    } = 1;
} = 0x20000099;

/*
 * rprintmsg.c   remote printing version of "printmsg.c"
 */
 
#include <stdio.h>
#include <rpc/rpc.h>        /* always needed */
#include "msg.h"            /* msg.h wil be generated by rpcgen */

main(int argc, char *argv[])
{
    CLIENT *cl;
    int *result;
    char *server;
    char *message;
    
    if (argc != 3) {
        fprintf(stderr,
        "usage %s host message\n",argv[0]);
    }    
    server = argv[1];
    message = argv[2];
    
    /* Creare client handle for calling MESSAGEPROG on the server
     * designated on the command line.  We tell the RPC package
     * to use the tcp protocol when contacting the server.
     */    
    cl = clnt_create(server, MESSAGEPROG, MESSAGEVERS, "tcp");
    if (cl == NULL) {
        /*
         * Couldn't establish connection with server.
         * Print error message and die.
         */
        clnt_pcreateerror(server);
        exit(1);
    }
    
    /* 
     * Call the remote procedure "printmessage" on the server.
     */     
    result = printmessage_1(&message, cl);
    if (result == NULL) {
        /*
         * An error occurred while calling the server.
         * Print error message and die
         */
        clnt_perror(cl, server);
        exit(1);
    }
    /*
     * Okay, we successfully called the remote procedure
     */
    if (*result == 0) {
        /*
         * Server was unable to print our message.
         * Print error message and die.
         */
        fprintf(stderr, "%s: %s couldn't print your message\n",
        argv[0], server);
        exit(1);
    }
    /*
     * Message got printed at server
     */
     printf("Message delivered to %s!\n", server);
     exit(0);
}


/*
 * msg_proc.c  implementation of the remote procedure call "printmessage"
 */
 
#include <stdio.h>
#include <rpc/rpc.h>        /* always needed */
#include "msg.h"            /* msg.h will be generated by rpcgen */

/*
 * Remote version of "printmessage"
 */

int * printmessage_1(char **msg)
{
    static int result;       /* must be static! */

    printf("%s\n", *msg);
    result = 1;
    return(&result);
}

< wpi /cs/cs4513/public/example/Message 1 >ls

msg.x         msg_proc.c    rprintmsg.c
< wpi /cs/cs4513/public/example/Message 2 >rpcgen msg.x


< wpi /cs/cs4513/public/example/Message 3 >ls

msg.h         msg_clnt.c    msg_svc.c
msg.x         msg_proc.c    rprintmsg.c
< wpi /cs/cs4513/public/example/Message 4 >cc -o msg_server msg_proc.c msg_svc.c 

msg_proc.c:
msg_svc.c:
< wpi /cs/cs4513/public/example/Message 5 >cc -o rprintmsg rprintmsg.c msg_clnt. c

rprintmsg.c:
msg_clnt.c:
< wpi /cs/cs4513/public/example/Message 6 >./msg_server&

[1] 23501
< wpi /cs/cs4513/public/example/Message 7 >./rprintmsg wpi "Hello wpi!"

Hello wpi!
Message delivered to wpi!

< wpi /cs/cs4513/public/example/Message 8 >Hello from sequoia
Hello from crane, a Sun
Hello from emu, a Linux machine
exit

exit

Note that the client was run on a different types of machines all sending a message to server running on wpi machine.

XDR/C++ Example


/*
 * testxdr.x  
 */
 
/*
 * Define a procedure
 *      str_test_1() takes a structure parameter and returns a string
 *
 */
 
struct testxdr{
    long long_arg;
    string string_arg < 128 >;
};

program TEST_PROG {
    version TEST_VERS {
    string STR_TEST(testxdr) = 1;  /* procedure number = 1 */
    } = 1;                          /* version number = 1 */
} = 0x31234567;                     /* program number = 0x31234567 */

So here is an example of how to pass multiple arguments. A long and a string in a single argument. Note that rpcgen will create another file testxdr_xdr.c that must be compiled and linked.


/* client.C  */

#include <iostream.h>
#include <string.h>

#include <rpc/rpc.h>

extern "C" {
#include "testxdr.h"
}

main(int argc, char *argv[])

{
  CLIENT *c1;
  char *server;

  char **sresult;


  if (argc !=2){
    cerr <<"usage:" << argv[0] <<" hostname\n";
    exit(1);
  }

  server = argv[1];

  if ((c1 = clnt_create(server, TEST_PROG, TEST_VERS, "udp")) == NULL){
       clnt_pcreateerror(server);
    exit(2);
  }

  testxdr xdrmessage; //structure testxdr defined in testxdr.x

  long temp_long = 1;
  char *temp_str = "Client is testing";
  
  //initialize xdrmessage
  xdrmessage.long_arg = temp_long;
  xdrmessage.string_arg = temp_str;
  
  if ((sresult = str_test_1(&xdrmessage, c1)) == NULL){
       clnt_perror(c1, server);
       exit(4);
     }

  cout << "Client call server successfully\n ";
  cout << "Server send message back:\n " << server << " = " <<*sresult<<"\n";
    
  clnt_destroy(c1);
  exit(0);
 }

//server.C   remote procedures; called by server stub

#include <iostream.h>
#include <string.h>
#include <rpc/rpc.h>        //* standard RPC include file */

extern "C"{
#include "testxdr.h"
}                        //* this file is generated by rpcgen */


/*
 * Accept and print out client message and return a server string
 */
  
char **str_test_1(testxdr *xdrm)
{
    static char *ptr = "Server say Hi to client!"; /* must be static */

    cout << "Message from client: "<< xdrm->string_arg << "\n";
     
    return(&ptr);
}

%rpcgen testxdr.x
%cc -c *.c
testxdr_clnt.c:
testxdr_svc.c:
testxdr_xdr.c:
%g++ -o server server.C testxdr_xdr.o testxdr_svc.o
%g++ -o client client.C testxdr_xdr.o testxdr_clnt.o
%exit
exit

Sun RPC Summary

More information in the way of Internet drafts:

Look at www.ietf.org.