Home / Tufts / Projects / RPC

RPC

This was one of the projects I worked on for my Distributed Systems course. The goal was to create a program that generates client and server code to perform remote procedure calls (RPC), without programmer even needing to know that a remote call is happening. It does this by parsing a file of function headers then generating proxy functions (that the client will call) and stubs (that the server will call) which, together, can perform the functions remotely. The proxy functions have the exact same signatures as the original functions but compute their result by sending its function arguments over the wire to the server, then waiting for the server to send the result back. On the server side, it waits for a client to initiate a request, then calls the relevant stub to run the function, and sends back the result.

Here is a simple example. Suppose you have a function called add which adds two integers and returns the result:

int add(int x, int y) {
    return x+y;
}

To allow a programmer to call this function normally but have the result computed on the server, the RPC program will first generate a proxy function. This function has the same signature as the original function, but will send its arguments to the server then wait for the result to be sent back, instead of computing it on-device.

// The proxy for the `add` function!
int add(int x, int y)
{
    // Indicate method to call
    sendString("add");

    // Send arguments over the network
    sendInt(x);
    sendVoid();

    sendInt(y);
    sendVoid();

    // Receive the result
    int _result;
    _result = readInt();
    readVoid();
    return _result;
}

This code was generated entirely by the RPC generation program! How cool! It relies on a number of helper functions, like sendInt and readInt, that encapsulate networking details, make code generation easier, and reduce code duplication. I've purposefully omitted their implementations to maintain some semblance of academic integrity 😉.

Then, a stub is generated for the server. The stub works as the other half of the remoting process by receiving arguments, computing a result, then sending the result back to the client. Here's the generated stub for the add function:

void _add()
{
    // Receive arguments over the network
    int x;
    x = readInt();
    readVoid();

    int y;
    y = readInt();
    readVoid();

    // Call the method locally and send the result
    int _result = add(x, y);
    sendInt(_result);
    sendVoid();
}

Like the proxy, the generated stub relies on a number of helper functions that hide networking details. Isn't reusable code just the best! The RPC generator also generates the dispatchFunction function, which calls a relevant stub depending on which function name the client sent over.

void dispatchFunction()
{
    // The first value sent is the function name
    string _func_name = readString();

    if (!RPCSOCKET->eof())
    {
        if (_func_name == "add")
        {
            _add();
        }
        else if (_func_name == "divide")
        {
            _divide();
        }
        else if (_func_name == "multiply")
        {
            _multiply();
        }
        else if (_func_name == "subtract")
        {
            _subtract();
        }
    }
}

And that's RPC! But getting this far was only half of the battle. The project spec also required that the generator supports statically-sized arrays and structs, which complicates code generation considerably. The most challenging bit was supporting structs nested in other structs and generating loops to make the generated code more elegant. In the end, I was quite pleased with our solution! It handles all of the toughest test cases and generates code that is well-formatted with comments and indentation, and reduced code duplication by using helper functions and loops when appropriate.

I'm choosing to omit some of the more complex code that our project can generate as to not give too much of our solution away, but for the interested reader, here are some of the more complex headers that are supported.

int arraysOfArrays(int x[24], int y[24][15], int z[24][15]);

struct Person { string firstname; string lastname; int age; };

struct ThreePeople {
    Person p1;
    Person p2;
    Person p3;
};

Person findPerson(ThreePeople tp);

struct StructWithArrays {
    int aNumber;
    Person people[10];
};

struct rectangle { int x; int y; };

void searchRectangles(rectangle rects[200]);

signpost care to venture further?

Projects

CanJam - a multithreaded peer-to-peer sound canvas

CyberGrape - a tactile spatial audio system

RPC - remote procedure calls in C

filecopy - copy files over a highly unreliable network

arrow_upward Back to top