Project 2 - Simple RPC

Client-Server Interfaces, Spring 2003


Table of Contents

Due Date

This assignment is due on Tuesday, 8 April, no later than 2:00 p.m.

See the assignment turn-in page (last modified on 25 February 2003) for instructions on turning in your assignment.

Background

Simple RPC (sRPC) is a combination of Sun's ONR RPC (in terms of software structure) and XML-RPC (in terms of everything else). However sRPC uses neither XDR nor XML, but rather a simple, text-based description of calls and data.

A single sRPC server implements a single service, which consists of one or more calls. Clients of a particular sRPC server can make any of the calls supported by the server. For example, an sRPC server may act as an Accordion server, supporting the calls play(), deal(), tableau(), and quit().

An sRPC service call is similar to a normal subroutine or method call, except that an sRPC service call is more limited in the types of parameters it can use. An sRPC service-call parameter can be an integer, a string, or an array. An integer is a signed, 32-bit value. An sRPC string is a sequence of zero or more seven-bit ASCII characters; an sRPC string is not zero terminated. An sRPC array is a sequence of zero or more values of the array's base type. sRPC arrays are dynamic; they have no fixed maximum size. sRPC Arrays are also recursive; an array's base type may also be an array.

System Structure

The overall structure of a client-server system in sRPC can be shown as

In left to right order, the components of the system are:

Although the diagram shows a single client, an sRPC server can handle multiple clients.

sRPC Components

sRPC consists of two main components, the sRPC client and server runtimes and the srpc-gen interface-code generator.

The sRPC runtime system is responsible for implementing all sRPC functions, including data marshaling, data transmission, and sRPC system-call semantics.

The sRPC runtime system is generic with respect to the services it supports; the same runtime system works with all possible services defined in sRPC. The srpc-gen interface-code generator is responsible for creating the code that bridges the gap between the specific requirements of a particular service and the generic facilities of the sRPC runtime system.

Service-Description Files

A service-description file describes the service calls available from a particular sRPC server. The srpc-gen interface generator accepts as input a service-description file and produces as output several interface files that let implementors access and provide services via sRPC.

A service-description file is a text file with a simple format: a service name spec followed by one or more service-call specs. A service-name spec is a single line starting with the text service followed by an identifier giving the name of the service:

service ident

For example, the service-description file for the Accordion service might start with

service Accordion

Following the service-name spec is one or more service-call specs. A service-call spec consists of a call-name spec followed by zero or more call-parameter specs. A call-name spec is a single line starting with the text call followed by an identifier giving the name of the call:

call ident

Each call identifier must be unique among all call identifiers in the same specification file.

Each call-parameter spec is a single line of text consisting of a direction, a type spec, and an identifier:

dir type-spec ident

The direction dir is either in or out and indicates if the parameter is an input (in) or output (out) parameter. type-spec indicates the parameter's type; type-spec can be one of int for an integer, string for a string, or array of type-spec for an array containing values of the given type. ident is the parameter's name; the identifier must be unique among all parameter names in the same service-call spec.

For example, this service-call spec

call tableau
in  array of array of int piles
in  string                state
out int                   difference
out string                error

describes the Accordion service's tableau call.

Generic Service Calls

Every sRPC service includes a set of generic sRPC service calls; a generic sRPC service call is an sRPC service call that is always included in any service. The generic sRPC service calls provide management and information facilities to the clients and servers using sRPC. Such facilities include setting up and tearing down connections between clients and servers and receiving information about the service calls offered by a particular service. On the client side, there are generic service calls that allow the client to

  1. setup a local endpoint for an sRCp service and establish a connection to a remote sRPC server for the service.

    call setup
    in string  hostname
    in int     port_number
    out string emsg
    

    The sRCP server is located at hostname:port_number. This must be the first service call made by a client. If emsg is emtpy after a call to establish(), no error occurred during the call; otherwise, emsg contains an error description.

  2. determine the service calls offered by the sRPC server.

    call query
    out string spec
    out string emsg
    

    After the call to query(), spec contains the server's service-description file and emsg contains an error indicator as described above.

  3. shutdown the connection to the remote sRPC server and reclaim the resources allocated to the local endpoint.

    call shutdown
    out string emsg
    

    emsg contains an error indicator as described above. Once shutdown() is called for a particular endpoint, any other service calls to that endpoint are invalid.

    A call to shutdown() effects only the associated endpoint; any other endpoints to the same sRPC server, and any other endpoints to other sRPC servers, remain unchanged.

    The server-side generic service calls allow a server to

    1. initialize the server at the start of service.

      call initialize
      out string emsg
      

      emsg contains an error indicator as described above. initialize() is called once by the sRPC runtime code at the start of execution to initialize the implementor-supplied server code.

    2. shut-down the server code in preparation for stopping the server.

      call shutdown
      out string emsg
      

      emsg contains an error indicator as described above. shutdown() is called once by the sRPC runtime coded at the end of server execution.

    Notice that the server interface does not include the query() generic sRPC service call.

Language Bindings

sRPC is language independent; before sRPC can be used, the features and details described above have to be represented in some programming language. The programming language in which the sRPC features and details are represented is known as the host language, and the process of representing sRPC features and details in the host language is known as binding sRPC to the host language.

The specifics of binding sRPC depend on the host language. This section describes an sRPC binding to C++ as the host language; the bindings for other host languages will be similar.

The sRPC types are bound to the analogous types in C++:

sRPC typeBound C++ type
intint
stringstd::string
array of Tstd::vector<T>

Each service call is bound to a prototype of the form

void call-name(input-parameters,output-parameters)

The left-to-right order of the input parameters matches the top-to-bottom order of the in-parameter specs in the service-spec; similarly for the output parameters. An input-parameter spec

in type-spec ident

is bound to the formal parameter

const bound-type-spec & ident

or to

bound-type-spec ident

the choice is up to the sRPC implementor. An output-parameter spec

out type-spec ident

is bound to the formal parameter

bound-type-spec & ident

As an example, the tableau service-call spec

call tableau
in  array of array of int piles
in  string                state
out int                   difference
out string                error

is bound to the prototype

void tableau(
  const std::vector<std::vector<int> > & piles,
  const std::string & state,
  int & difference,
  std::string & error
  )

The client-side generic sRPC service calls have the bindings

service-name(
  const std::string & hostname,
  short port_number,
  std::string & emsg
  )

void query(
  std::string & spec,
  std::string & emsg
  )

void shutdown(
  std::string & emsg
  )

The setup generic sRPC service call becomes the class's constructor, but the shutdown generic sRPC service call does not become the class's destructor (although the destructor may call shutdown() if it needs to).

On the server side, the service-call specs and server-specific generic sRPC service calls are bound to procedures in a namespace with the same name as the service.

srpc-gen creates four C++ source files from a service-spec file:

c++ files generated by srpc-gen

The client.h file contains the declarations of the client-side interface routines defined in the client.cc file. Any implementer-supplied client-side code that make service calls needs to include the clients.h file.

The client.cc file contains the definitions of the client-side interface routines for the service. The implementer compiles and links this file with the client-side code.

The server.h file contains the declarations of the server-side interface routines called in the server.cc file. The server-side code implementing service calls needs to include the server.h file.

The server.cc file lets the sRPC server-side runtime system call-out to the implementor-supplied code for the service. The implementer compiles and links this file with the server-side code.

The Project

This project consists of two parts:

  1. Write srpc-gen.

  2. Write the sRPC runtime system, including the sRPC protocol.

Example

This example demonstrates how sRPC might be used to implement the Accordion server specified in the first project. The implementor would first first create a service-description file for the accordion server:

service Accordion

Then would follow a sequence of service calls describing the interface to the accordion server:

call play
out string emsg
out string state

The client calls play() to request a game from the accordion server. play() accepts no input parameters and returns two strings. The first string is an error indication; the second string represents the state of the game.

call deal
in  string state
out string emsg
out int    card
out string state

The client calls deal() to receive another card from the hand. deal() accepts a single parameter, which is the most recently returned state string; the return parameters are an error message, the card dealt (assuming no error) and the updated state.

call tableau
in  array of array of int piles
in  string                state
out string                emsg
out int                   difference

The client calls tableau() at the end of the game to compare it's game against the server's game. The server returns an error indication and the difference between the number of piles in the two games.

call quit
in  string state
out string emsg

The client calls quit() to end the game prematurely.

Assuming this service-description file is named accordion.srpc and that srpc-gen produces C++ code, running accordion.srpc through srpc-gen produces the four files accordion-client.h, accordion-client.cc, accordion-server.h, and accordion-server.cc.

Suggestions for Proceeding

  1. Choose a host language. This is the language in which users of your sRPC implementation will write client and server code, and the language used in the files generated by your implementation of srpc-gen. However, your implementation of srpc-gen need not be written in the host langauge. For example, you may chose C++ as the host language, but write srpc-gen in perl.

    Your implementation of the sRPC runtime system will most likely be written in the host langauge, but that isn't an absolute requirement, although it's the easiest approach to take. For example, If you choose Java as your host language, you can write the sRPC runtime system in either Java or C++; if you use C++ you'd hook up to it through the Java Native Interface.


This page last modified on 29 March 2003.