Building a Message Server with JRPC

This tutorial demonstrates how to build a simple message server and client using JRPC. This is a "Hello World" example - for more elaborate examples, download the SDK and explore the code under the samples/ directory.

Note: This tutorial focuses on using the JRPC tool. If you're not familiar with ONC RPC, the book "Power Programming RPC" from O'Reilly is an excellent guide.

📋 Step 1: Define the RPC Interface

Create the interface definition file msg.x:

program msgserv {
    version MSGSERV_V1 {
        string sendmsg(string) = 2;
    } = 1;
} = 1234567;

This interface defines an RPC program with a single procedure sendmsg. The client sends a string to the server, and the server returns a string back.

The C/C++ version of the Msg client/server is available from the Netbula ONC RPC For Win32 SDK.

⚙️ Step 2: Compile with jrpcgen

Run the jrpcgen compiler to generate Java classes:

jrpcgen msg.x

Generated Files:

msgserv.java

RPC program interface definition with constant definitions

msgserv_cln.java

Client stub class that implements the RPC interface

msgserv_svcb.java

Abstract RPC service class that needs implementation

Note: The demo package includes jrpcgen binaries for Win32, Solaris, and Linux, all generating identical Java code.

👨‍💻 Step 3: Code the Client Application

Create the client application ClientTest.java:

import netbula.ORPC.*;
import java.net.*;

public class ClientTest {
    static public void main(String args[]) {
        try {
            // Create client connecting to server using UDP protocol
            msgserv_cln cl = new msgserv_cln(args[0], "udp");
            
            /*
            // Optional authentication
            cl.setAuth(new AuthUnix("localhost", 501, 100, new int[2]));
            */
            
            String msg = "hello world\n";
            System.out.println("sending.. ");
            
            // Send multiple messages
            for(int i = 0; i < 5; i++) {
                String reply = cl.sendmsg(msg);
                System.out.println("got " + reply + "\n");
            }
        } catch (rpc_err e) {
            System.out.println("rpc: " + e.toString());
        }
    }
}

This client connects to the Msg server on the specified host using UDP protocol, sends a message, and prints the reply. The client sends multiple messages in a loop.

🔧 Step 4: Compile the Client

Compile the client application:

javac ClientTest.java MSGSERV_1.java

Make sure the netbula.ORPC package is in the classpath by adding the orpc.jar file to the CLASSPATH environment variable.

🚀 Step 5: Run the Client

1

Ensure the Msg server (C version or Java version) is running on localhost

2

Run the client:

java ClientTest

If the server is running, you should see the client print out replies from the server. Otherwise, it will print an RPC error: "Program not registered."

🖥️ Step 6: Code the Server

Create the server implementation msgsvc.java:

import netbula.ORPC.*;

class msgsvc extends msgserv_svcb {
    // Implement the server function - echo the message back
    String sendmsg(String msg) {
        System.out.println("got msg from client " + msg);
        return msg;
    }

    // Main function runs the server
    public static void main(String argv[]) {
        // Run the server using the run() method in Svc
        // For more flexibility, use TCPServer and UDPServer directly
        new msgsvc().run();
    }
}

The server simply echoes back the received message. The run() method handles all the server initialization and request processing.

⚡ Step 7: Compile and Run the Server

# Compile the server
javac msgsvc.java

# Run the server
java msgsvc

📁 Advanced Example: File Transfer Server/Client

Next, let's look at a more interesting example: a JRPC server/client that transfers multiple files via RPC mechanism. This example is under the samples/filexfer directory of the JRPC package.

File Transfer Interface

Interface definition file filexfer.x:

%import netbula.ORPC.*;

struct NFiles { 
    XDTFile files<>; 
};

program FileXFER {
    version v1 {
        void xferFile(NFiles) = 1;
    } = 1;
} = 12345678;

This interface defines a struct NFiles containing a variable-length array of XDTFile objects. The xferFile function can transfer any number of files in a single RPC call.

File Server Implementation

Server code FileServer.java:

import netbula.ORPC.*;
import java.io.*;

public class FileServer extends filexfer_svcb {
    public void xferfile(NFiles in_arg) {
        for(int i = 0; i < in_arg.files.length; i++) {
            System.out.println("Received file: " + 
                in_arg.files[i].receivedFilepath() + 
                " " + in_arg.files[i].byteCount() + 
                " bytes transfered");
            System.out.println("saved file: " + 
                in_arg.files[i].savedFilename());
        }
    }

    static public void main(String args[]) {
        rpc_err.debug = true;
        FileServer server = new FileServer();
        try {
            server.run();
            System.out.println("server exited");
        } catch(rpc_err e) {
            System.out.println("Fail to run server:" + e.toString());
        }
    }
}

File Client Implementation

Client code FileClient.java:

import netbula.ORPC.*;

public class FileClient {
    static public void main(String args[]) {
        try {
            if(args == null || args.length < 2) {
                System.out.println("Usage: java fileClient server_hostname file1 [file2 file3 ..]");
                System.exit(1);
            }
            
            String servhost = args[0];
            
            // Use TCP (UDP is not reliable for file transfer)
            filexfer_cln cl = new filexfer_cln(servhost, "tcp");
            System.out.println("Connected to " + servhost);
            
            // Send files listed on command line to server
            NFiles nf = new NFiles();
            nf.files = new XDTFile[args.length - 1];
            
            for(int i = 0; i < args.length - 1; i++) {
                nf.files[i] = new XDTFile(args[i + 1]);
            }
            
            cl.xferfile(nf); // Send all files over
            
            for(int i = 0; i < args.length - 1; i++) {
                System.out.println(args[i + 1] + " " + 
                    nf.files[i].byteCount() + " bytes sent");
            }
        } catch (Exception e) {
            System.out.println("rpc: " + e.toString());
            e.printStackTrace();
        }
    }
}

🎯 Summary

This file transfer example demonstrates how JRPC can handle complex data types and transfer multiple files efficiently. The XDTFile class handles all the file serialization and deserialization automatically.