Distinct ONC RPC/XDR for .NET Toolkit
Programming Guide

Documentation Roadmap

The Distinct ONC RPC/XDR Documentation is comprised of a:

·         Programming Guide: the current document. It provides a description of what this toolkit offers and provides information on how the components work and who should use it.

·         Quick Start for the RPC Novice: this is intended for users who are not experienced RPC programmers and wish to get a head start in integrating RPC into their application

·         Programmer’s Reference: this provides the syntax and description of all RPC and XDR API calls for the Distinct ONC RPC/XDR libraries. Note that the syntax in this reference marked as Visual C++ is for the C++/CLR language.

1. Introduction
This introduction is addressed to programmers who wish to write their own distributed applications in .NET using the Distinct® ONC RPC/XDR Toolkit for .NET.  The following are provided: 

·         A short description of the components of the Distinct ONC RPC/XDR for .NET Toolkit.

·         Step by step instructions how to build an RPC client application in .NET. First, the XDR interface definition file and the source code of the server in C are described. Then the required steps to build a .NET client are detailed resulting in a complete client application

·         The RPCGen.NET utility which compiles XDR Interface Definition files (.x) into C# client and server stubs.

·         Step by step instructions how to build an RPC server application in .NET. The required steps to build a .NET server are described resulting in a complete server application. The use of the ONC RPCBIND is also discussed.

·         How to restrict the range of ports that a server will listen on.

·         A listing of mapping between ONC XDR data types and .NET data types.

·         A section on how to use Distinct® ONC RPC/XDR Toolkit for .NET from Visual Basic .NET

·         A section on how to use Distinct® ONC RPC/XDR Toolkit for .NET from C++/CLR.

·         Several important RPC programming concepts are discussed in the last section. These concepts include authentication, making broadcast RPCs, batching RPCs, using XDR streamstimeout handling, error handling if required, and how to establish client and server connections through a firewall explains the use of fixed ports

·         A summary of issues to be aware of when upgrading from a previous version of Distinct ONC RPC/XDR for .NET.

This document assumes that you are already familiar with .NET, the basic concepts of RPC client/server computing and ONC RPC and XDR in particular. It also assumes a familiarity with the basics of the C binding of ONC RPC. If you wish to write .NET ONC RPC applications and are not yet familiar with these concepts you should start with the Quick Start for the RPC Novice guide and do further reading before using the toolkit. 

You may need to refer to the reference part of this manual to understand parts of the examples given in this programming guide. 

This toolkit may be used to help you out in several different scenarios. Here are some: 

·         Whenever you wish to write an ONC RPC client in any .NET-language to communicate with existing ONC RPC/XDR servers.

·         Whenever you wish to preserve the existing interface of an application written using the XDR specifications.

·         Whenever you wish to write C# code that can read and write XDR streams. Other (for example C, C++ ) applications often use this platform independent encoding format for serializing data. 

·         Whenever you wish to write simple client/server applications in .NET. 

In all these scenarios Distinct ONC RPC/XDR for .NET will certainly help you to write portable, high-quality C# code in a minimum amount of time that can be used from any .NET-language. 

This guide discusses how to write client/server applications using the Distinct ONC RPC/XDR Toolkit for .NET. It first explains how to write an ONC RPC client and then illustrates how a server is built in .NET.  It then describes some more advanced features, the understanding of which is fundamental to using this toolkit. It also includes a section on how to pass RPCs through Internet or Intranet firewalls.

2. What comes with the Distinct ONC RPC/XDR for .NET Toolkit? 
The Distinct ONC RPC/XDR Toolkit for .NET is a set of tools and libraries that enables you to write pure .NET ONC RPC clients and servers.
It consists of: 

·         The Distinct ONC RPC/XDR for .NET assembly that contains the ONC RPC/XDR run time libraries that conform to RFC 1831 (RPC: Remote Procedure Call Protocol Specification Version 2) and RFC 1832 (XDR: External Data Representation Standard). The API consists of classes that allow you to write .NET clients, for standard RPC servers, which can be embedded in applets and be run by a standard Web browser. It also allows you to develop ONC RPC stand-alone servers. The package allows connections over TCP and UDP. 

·         An RPCBIND application that implements the RPCBIND protocol versions 2 (portmapper), 3 and 4. 

·         The RPCGen.NET compiler that translates standard RPC/XDR interface definition files into C# classes that implement the client and server side stubs and the XDR conversion routines for the described interface. This means RPCGen.NET implements a C# language mapping for .x IDL files.

·         The RPCInfo utility displays a list of all the services registered with the RPCBIND on a system. This utility can be used to query the RPCBIND on the local host (by specifying "localhost") or on a remote host (by specifying the name or IP address of the host). Source code for the RPCInfo utility is included for reference. 

·         A set of demo applications that consist of an XDR file which describes an interface of a very simple server, the server implementation, and demo applications that invokes the server written in both Visual Basic.NET and C#.

3. Building a Client with Distinct ONC RPC/XDR for .NET
I
n this section we will illustrate how to use this toolkit to build a .NET client in C#. For our example we will use the scenario of an application programmer who wants to write a .NET-based front end for an existing ONC RPC service. 

When writing an ONC RPC application the first thing to do is to write an XDR interface definition (IDL) file. If you need to write an RPC client for an existing server you should obtain the XDR file for that server first. The IDL file describes the data types and the signature of your interface. Distinct ONC RPC/XDR for .NET understands the XDR language as described in RFC 1832 (XDR: External Data Representation Standard) and many of the extensions that have been added by various vendors over the last decade (like multiple arguments in one procedure and "in"/"out"/"inout" parameters). By common programming convention IDL files have a .x extension and are commonly referred to as .x files. 

3.1. The XDR Interface Definition Language File
For the remainder of this guide we will work with the XDR interface definition language (IDL) file demo.x as shown below. It defines a simple service that returns a sequence of consecutive lines from a text: 

%/*****************************************
% * Distinct ONC RPC/XDR for .NET Example *
% ****************************************/
 
struct request {
        int from;
        int to;
};
 
struct result {
        int number;
        string line<>;
        struct result *next;
};
 
typedef result *res_list;
 
program DEMO_SERVER {
        version DEMO_VERSION {
               res_list get_line(request) = 1;
        } = 1;
} = 0x20000023;
 
This IDL file has 5 sections: 
1.       A comment with leading '%' characters.
2.       A struct request type declaration. 
3.       A struct result type declaration. 
4.       A typedef res_list type declaration.
5.       A program definition for the server interface DEMO_SERVER.

The program definition contains only one procedure (function get_line()). It was a limitation of the original ONC RPC that each procedure may have only one input parameter and one return value. But as these parameters may be of arbitrary complex type (e.g. a structure), the restriction is only a syntactical one (with Distinct ONC RPC/XDR for .NET this limitation is gone and you can also specify multiple arguments per procedure like you would do in C). In our example the input type of get_line() (named request) contains two integers specifying a range of lines (named from and to). The output type res_list is a pointer to a linked list of result structures. Each element of this list describes one line of the result (line number and content string). In this example it is important to define the typedef result because with RPCGen.NET a procedure can have only plain type names in its signature (e.g. result *get_line(request) is not allowed). However since you can use any level of typedefs this is not really a semantic restriction.

3.2 The Server Implementation in C 
Although the functionality of the service might be obvious at this point, we show here its implementation in C to clarify any questions you may have. In this simple version the lines of text that can be returned are hard coded and there is no error processing done (e.g. if a client requests a non-existing range of lines). 

#include <stdio.h> 
#include <rpc/rpc.h> 
#include "demo.h" 
 
char *text[] = { 
        "This is the first line of text.", 
        "This is the second line of text.", 
        "This is the third line.", 
        "This is the 4th line.", 
        "This is the 5th line.", 
        "This is the 6th line.", 
        "This is the 7th line.", 
        "This is the 8th line." 
}; 
 
res_list *get_line_1(request *req) 
        static res_list rl; 
        result *p, *q; 
        result **n; 
        int c; 
 
        /* free any dynamically allocated memory from previous requests */ 
        p = rl; 
        while (p != NULL) { 
               q = p; 
               p = p->next; 
               free(q); 
        
 
        /* the handling of the new request starts here*/ 
        rl = NULL; 
 
        /* n points to the pointer where the next list element is inserted */ 
        n = &rl; 
        for (c = req->from - 1; c < req->to; c++) { 
               /* allocate the next element of the list */ 
               *n = malloc(sizeof(result)); 
 
               /* assign the line number */ 
               (*n)->number = c + 1; 
 
               /* assign the line (string) */ 
               (*n)->line = text[c]; 
               n = &((*n)->next); 
               *n = NULL; 
        
        return &rl; 

To build the server binary with just the demo.x IDL file and the demo_server.c file above, run the standard ONC RPC rpcgen (for C) on demo.x and compile and link the generated files demo_svc.c, demo_xdr.c, demo.h, and demo_server.c into one native binary. 

Once you have built the above server and have it up and running with the ONC RPC RPCBIND, you can proceed to build a .NET stand-alone client that calls this server. 

3.3 The .NET Client 
This section will take you through the steps needed to build the .NET client. The first thing to do is to run the RPCGen.NET provided with the Distinct ONC RPC/XDR for .NET Toolkit on the IDL file. The RPCGen.NET application translates XDR files into .NET stubs. For a complete description of the RPCGen.NET command see the section on RPCGen.NET.

You run RPCGen.NET on demo.x by typing: 

>RPCGen.NET –n demo.x 
RPCGen.NET V2.0, Copyright 1997 - 2009 by Distinct Corporation
writing: request.cs
writing: result.cs
writing: res_list.cs
writing: demoClient_vers1.cs


The execution of RPCGen.NET creates four .NET classes in four files, that implement the client stub for calling the demo service described. There is one file per type definition for request.cs, result.cs, and res_list.cs, and one main stub file called demoClient_vers1.cs. 

3.3.1 The Main Stub
We will now take a look at the demoClient_vers1.cs main stub file that has been generated. 

/*****************************************

 * Distinct ONC RPC/XDR for .NET Example *

 ****************************************/

 

using com.distinct.rpc;

 

namespace demoRPC {

 

/// <summary>

/// This class was automatically generated by Rpcgen.NET from the RPC/XDR file "demo.x"  .

/// It defines the client interface to a server implementing version 1 of the "DEMO_SERVER" program.

/// </summary>

public class demoClient_vers1 : NetRPCClient {

 

/// <summary> Program ID. </summary>

public const int DEMO_SERVER = 0x20000023;

 

/// <summary>

/// Creates and connects an RPC client for a server that implements version 1 of the "DEMO_SERVER" program.

/// Calls the remote Portmapper in order to get the port of the server. </summary>

/// <param name = "host"> The host on which the server resides. </param>

/// <param name = "stream"> true for a TCP connection, false for UDP. </param>

 

public demoClient_vers1(System.Net.IPAddress host, bool stream) :

  base(host, DEMO_SERVER, DEMO_VERSION, stream) { }

 

/// <summary>

/// Creates and connects an RPC client for a server that implements version 1 of the "DEMO_SERVER" program.

/// The client will try to connect to the specified server port. (No interaction with a portmapper) </summary>

/// <param name = "host"> The host on which the server resides. </param>

/// <param name = "port"> The port on which the server listens. </param>

/// <param name = "stream"> true for a TCP connection, false for UDP. </param>

 

public demoClient_vers1(System.Net.IPAddress host, int port, bool stream) :

  base(host, DEMO_SERVER, DEMO_VERSION, port, stream) { }

 

/// <summary>

/// Creates an RPC client for a server that implements version 1 of the "DEMO_SERVER"  program.

/// The client is initialized with an externally created protocol client object. </summary>

/// <param name = "protocol"> The protocol object that implements the client connection. </param>

 

public demoClient_vers1(ClientGeneric protocol) :

  base(protocol) { }

 

/// <summary> Version ID of the program. </summary>

public const int DEMO_VERSION = 1;

 

/// <summary> Procedure number of get_line </summary>

public const int get_line = 1;

 

/// <summary>

/// Stub method that invokes the server function "get_line" (version 1). </summary>

/// <param name = "arg1"> input parameter of the RPC. </param>

/// <returns> The return value of the RPC. </returns>

 

public res_list get_line_1(request arg1) {

 

  res_list retval = new res_list();

  GetClient().Call(get_line, arg1, retval);

 

  return retval;

}

}

}

An instance of this demo stub class represents a client to the demo server. It maintains the complete necessary connection status. Without going into all details we can easily see that this class consists of a number of constants, three constructors, and a public method get_line_1() that has the same signature as get_line() in the XDR file. You can also see that the lines from the beginning of the XDR file (the three comment lines) were copied over to the .NET source generated, without the leading % characters. Also RPCGen.NET generates XML-style comments that are understood by the C#-compiler auto documentation feature (option /doc). 

Like all main client-stub classes in Distinct ONC RPC/XDR for .NET, the demo class is derived from the base class NetRPCClient. This class provides the framework for calling RPC servers using the various possible protocols. Similar to the C binding of ONC RPC, the constants are the program's number and version as well as an ordinal number for each procedure. Of the three constructors the first (public demoClient_vers1 (System.NET.IPAddress host, bool stream)) is probably the most commonly used one. We will use it in our application example. Finally, the get_line_1() method is what we have to invoke when we want to interact with the server. The "_1" extension results from the fact that this is the implementation of version 1 of this RPC program. As with the C binding the version number from the XDR file is always appended with an underscore in front of it. 

3.3.2 The Type Definition Files 
We now take a look into the result.cs file, which is one of the type definition files that were generated. 

/*****************************************

 * Distinct ONC RPC/XDR for .NET Example *

 ****************************************/

 

using System;

using com.distinct.rpc;

 

namespace demoRPC

{

 

/// <summary>

/// This class was automatically generated by Rpcgen.NET from the RPC/XDR file "demo.x".

/// result: was struct

/// </summary>

 

[Serializable()]

public class result : XDRType {

  /// <summary> public member </summary>

  public int number;

  /// <summary> public member </summary>

  public string line;

  /// <summary> public member </summary>

  public result next;

 

/// <summary>

/// Default constructor for objects of class result. </summary>

 

public result()

{}

 

/// <summary>

/// Creates an object of class result. </summary>

/// <param name = "arg_number"> The value of the number component.</param>

/// <param name = "arg_line"> The value of the line component.</param>

/// <param name = "arg_next"> The value of the next component.</param>

 

public result(int arg_number, string arg_line, result arg_next)

{

  number = arg_number;

  line = arg_line;

  next = arg_next;

}

 

/// <summary>

/// Encodes an object of class result in compliance with RFC 1832 (XDR). </summary>

/// <param name = "xdrs"> The XDR output stream. </param>

 

public void xdr_encode(XDRStream xdrs)

{

        xdrs.xdr_encode_int(number);

        xdrs.xdr_encode_string(line);

        xdrs.xdr_encode_bool(next != null);

        if (next != null)

               next.xdr_encode(xdrs);

}

 

/// <summary>

/// Decodes an object of class result in compliance with RFC 1832 (XDR). </summary>

/// <param name = "xdrs"> The XDR input stream.</param>

 

public void xdr_decode(XDRStream xdrs)

{

        number = xdrs.xdr_decode_int();

        line = xdrs.xdr_decode_string();

        next = null;

        if (xdrs.xdr_decode_bool()) {

               next = new result();

               next.xdr_decode(xdrs);

        }

}

}

}

Once again you can see that the lines from the beginning of the XDR file (the three comment lines) were copied over to the C#-file generated. This happens for all files generated. The data members of the result class mirror the C like definition of the struct result in the XDR file. The linked-list structure of the result type is mapped in an XDR-typical manner to a recursive invocation guided by a Boolean value that indicates a NULL pointer value (and thus, the end of the list). The constructor allows you to create a result-object and initialize its data members.

Like all classes that are created by RPCGen.NET from type definitions, result implements the XDRType interface. This interface defines the two methods xdr_encode() and xdr_decode() that are used by the stub implementation for marshalling and unmarshalling the parameter into and from an XDRStream object. RPCGen.NET generates the necessary code to implement these methods for the given XDR type. 

Typically, if you are using the RPC functionality of the Distinct ONC RPC/XDR for .NET Toolkit, you will not have to bother with these methods. As with the C binding, the stub implementation hides all details of their usage. You just have to construct objects of the defined input parameter class and pass them to the stub method (in our case e.g. of class request to demoClient_vers1.get_line_1 ()) and you will receive a newly created reply object from the stub (in our case of class res_list). However, if you use the Distinct ONC RPC/XDR Toolkit just for encoding/decoding XDR data (e.g. from a file) you will have to call these methods directly in conjunction with an appropriate XDRStream object. 

The other two type definition classes, request.cs and res_list.cs, are very similar and have exactly the same structure. 

3.3.3 The Client Application
Now we are ready to write our first Distinct ONC RPC/XDR for .NET application. Below we show the listing of a simple console C# application, RPCClient.cs,  that uses the generated stubs to make some invocations of the demo server: 

using System;

using System.Net;

 

namespace demoRPC

{

 

/// <summary>

/// The main program of the client sample

/// </summary>

 

public class RPCClient

{

 

static public void Main (String[] args)

{

    demoClient_vers1 client; // this is the RPC client object

    request req = new request();

 

    try {

        client = new demoClient_vers1(

            System.Net.Dns.GetHostByName(args[0]).AddressList[0], // the host

            true); // use TCP

        for (int i = 1; i < 9; i++)

            for (int j = i + 1; j < 9; j++) {

                req.from = i;

                req.to = j;

 

                System.Console.Out.WriteLine("from " + i + " to " + j);

                res_list rl = client.get_line_1(req);

                result res = rl.value;

 

                while (res != null) {

                    System.Console.Out.WriteLine(res.number+":"+res.line);

                    res = res.next;

                }

            }

        client.CloseClient();

    }

    catch (Exception e) {

        System.Console.Out.WriteLine(e);

    }

}

}

}

Here we take a step by step look at this application: 

  1. In this example we assume that the generated demo stub class is in the same namespace (in this case demoRPC). So we have no special using directive here.
  2. We then define a very simple class RPCClient with just the static Main() method. Main() interprets the first string parameter from the command line as the name of the host where the demo server is running. 
    Now we declare an object of class demoClient_vers1. This is the client object we use every time we call the demo server. 
  3. Next we open an exception context. Most of the Distinct ONC RPC/XDR for .NET methods throw either an RPCError or a System.Net.Sockets.SocketException. Please note that every time this occurs, the status of the used client object is undefined. If this happens, you should reestablish the connection with a new client object if you want to call the server again. In our sample program we just catch every exception and print the message on the screen. 
  4. Now, we establish the connection to the server by calling the simplest constructor of the demoClient_vers1 client class. Parameters are the hostname of the server (from the command line) and a boolean that indicates whether we want to set up a TCP or a UDP connection. A value of true means TCP. 
  5. If this call does not throw an exception we are connected. Depending on whether the addressed host really has an ONC RPC RPCBIND and a demo server running, it may take several seconds before the success or the failure of this call are reported. 
  6. Once we are connected we call the server several times in a loop. We initialize the input object req, invoke the server with res_list rl = client.get_line_1(req); and process the output object rl. Please note the handling of the value member of rl that represents the original type (* result) of the typedef res_list. The invocation of client.get_line_1() may again result in an asynchronous exception of type RPCError, System.Net.Sockets.SocketException, or System.IO.IOException if any problem occurs in the RPC runtime (typically, when the server becomes unreachable). 
  7. Finally, we close the connection to the server and free all resources with client.CloseClient(). 

Here we are. This is our first RPC client written in C#. Now we only have to compile the stubs and our RPCClient.cs with the C# compiler (don't forget to add a reference to the assembly RPC_NET.dll that contains the ONC RPC runtime library). 

If you have compiled the files and you run the program, the output will look like this: 

>DemoClient localhost

from 1 to 2

1:This is the first line of text.

2:This is the second line of text.

from 1 to 3

1:This is the first line of text.

2:This is the second line of text.

3:This is the third line.

from 1 to 4

1:This is the first line of text.

2:This is the second line of text.

3:This is the third line.

4:This is the 4th line.

from 1 to 5

...

If the output looks like this: 

> DemoClient localhost 
System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it at System.Net.Sockets.Socket.Connect(EndPoint remoteEP)
...


This means that our program has caught an exception because we have most probably forgotten to start the demo server (in this case on localhost, our local machine). 

4. Building a Server with Distinct ONC RPC/XDR for .NET 

4.1 The .NET Server 
This section illustrates how to build a .NET server. To build a server, again we have to run RPCGen.NET from the Distinct ONC RPC/XDR Toolkit for .NET on the XDR file. This time you run RPCGen.NET on demo.x with the -S option, telling it that it has to create the server stub as well. Thus, you type: 

>RPCGen.NET –n -S demo.x 
RPCGen.NET V2.0, Copyright 1997 - 2009 by Distinct Corporation
writing: request.cs
writing: result.cs
writing: res_list.cs
writing: demoClient_vers1.cs
writing: demoServer_vers1.cs

The execution of RPCGen.NET creates the same four C# classes as before (even if we do not need the client part demoClient_vers1.cs this time) plus the additional server stub class named demoServer_vers1.cs. 

4.2 The Main Stub
We will now take a look into the demoServer_vers1.cs main stub file that was generated. 

/*****************************************

 * Distinct ONC RPC/XDR for .NET Example *

 ****************************************/

 

using com.distinct.rpc;

using System.Net;

 

namespace demoRPC {

 

/// <summary>

/// DEMO_SERVER Server Stub

/// This class was automatically generated by Rpcgen.NET from the RPC/XDR file "demo.x".

/// It is a prototype for implementing version DEMO_VERSION of DEMO_SERVER.

/// Create a subclass of this class, overriding the abstract methods to implement the required server functionality.

/// </summary>

 

public abstract class demoServer_vers1 : NetRPCServer{

 

/// <summary> Program ID. </summary>

public const int DEMO_SERVER = 0x20000023;

 

/// <summary> Version ID of the program. </summary>

public const int DEMO_VERSION = 1;

 

/// <summary> Procedure number of get_line </summary>

public const int get_line = 1;

 

/// <summary>

/// Constructor that creates the RPC server that implements version DEMO_VERSION of the "demo" program.

/// Throws RPCError if there was a problem creating the server.

/// </summary>

public demoServer_vers1() :

        base(DEMO_SERVER, DEMO_VERSION, true) {

        /*Unregister the service.*/

        Pmap pm = new Pmap(DEMO_SERVER, DEMO_VERSION, 0, 0);

        pm.unset(IPAddress.Loopback);

 

        /*start udp service(s):

        the IPv4 service is always started and the IPv6 service is started if IPv6 is available on the system*/

        StartUDP(0);

        /*start tcp service(s):

        the IPv4 service is always started and the IPv6 service is started if IPv6 is available on the system*/

        StartTCP(0);

        /*Register all services started by StartUDP/StartTCP.*/

        RegisterServer();

}

 

/// <summary>

/// Constructor that creates the RPC server that implements version DEMO_VERSION of the "demo" program.

/// Throws RPCError if there was a problem creating the server.

/// <param name = "port"> The port on which the server listens (if 0 a random port will be chosen). </param>

/// </summary>

public demoServer_vers1(int port) :

        base(DEMO_SERVER, DEMO_VERSION, true) {

        /*Unregister the service.*/

        Pmap pm = new Pmap(DEMO_SERVER, DEMO_VERSION, 0, 0);

        pm.unset(IPAddress.Loopback);

 

        /*start udp service(s):

        the IPv4 service is always started and the IPv6 service is started if IPv6 is available on the system*/

        StartUDP(port);

        /*start tcp service(s):

        the IPv4 service is always started and the IPv6 service is started if IPv6 is available on the system*/

        StartTCP(port);

        /*Register all services started by StartUDP/StartTCP.*/

        RegisterServer();

}

 

/// <summary>

/// Constructor that creates the RPC server that implements version DEMO_VERSION of the "demo" program. </summary>

/// Throws RPCError if there was a problem creating the server.

/// <param name = "port"> The port on which the server listens (if 0 a random port will be chosen). </param>

/// <param name = "do_tcp"> True, if a TCP server should be started. </param>

/// <param name = "do_udp"> True, if a UDP server should be started. </param>

/// <param name = "do_rpcb"> True, if a RPCBind name server should be started (only if one is not already running.) </param>

public demoServer_vers1(int port, bool do_tcp, bool do_udp, bool do_rpcb) :

        base(DEMO_SERVER, DEMO_VERSION, do_rpcb) {

        /*Unregister the service.*/

        Pmap pm = new Pmap(DEMO_SERVER, DEMO_VERSION, 0, 0);

        pm.unset(IPAddress.Loopback);

 

        if (do_udp)

        {

               /*start udp service(s):

               the IPv4 service is always started and the IPv6 service is started if IPv6 is available on the system*/

               StartUDP(port);

        }

 

        if (do_tcp)

        {

               /*start tcp service(s):

               the IPv4 service is always started and the IPv6 service is started if IPv6 is available on the system*/

               StartTCP(port);

        }

 

        /*Register all services started by StartUDP/StartTCP.*/

        RegisterServer();

}

 

/// <summary>

/// Dispatcher Routine that interpretes the call requests. </summary>

/// <param name = "proc"> The index of the requested function. </param>

/// <param name = "xin"> read the input-parameter from this XDR stream. </param>

/// <param name = "xout"> write the return-parameter to this XDR stream. </param>

/// <returns> true, if the function with the index proc can be served, false otherwise. </returns>

 

override public bool DoCall(int proc, XDRStream xin, XDRStream xout) {

        try

        {

               if (getCallVersion() == DEMO_VERSION)

               switch (proc)

               {

               case get_line: {

                               request arg = new request();

                               arg.xdr_decode(xin);

                               res_list ret = get_line_1(arg);

                               ret.xdr_encode(xout);

                               return true;

                       }

 

                       default:

                               break;

               }

               return (proc==0)?true:false;

        }

        catch (System.Exception) {

               return false;

        }

}

 

// Override these abstract server methods for implementing the server's functionality.

 

public abstract res_list get_line_1(request arg1);

}

}

This stub class demoServer_vers1 represents the frame for a server that implements the DEMO_SERVER interface described in demo.x. Similar to the client stub we see, this class consists of a number of constants, two constructors, and an abstract method get_line_1() that has the same signature as get_line() in the XDR file. You can also see that the lines from the beginning of the XDR file (the three comment lines) were copied over to the generated .NET source without the leading % characters. 

Like all server stub classes in Distinct ONC RPC/XDR for .NET, the class demoServer_vers1 is derived from the base class NetRPCServer. This class provides the framework for implementing RPC servers using the various possible protocols. Again, the constants are the program's number and version and ordinal numbers for the remote procedure. The method DoCall() is the core of the server. It implements dispatching of the remotely called procedures depending on the given ordinal number and it calls marshalling and unmarshalling of the parameters. Finally, the abstract get_line_1() method is the procedure we have to implement for our demo server by overriding it in a derived class. 

4.3 Synchronization
In Distinct ONC RPC/XDR for .NET, the DoCall() method, and consequently all implemented remote procedures, can be called concurrently by different server threads. In the Distinct ONC RPC/XDR for .NET implementation of an ONC RPC server, each TCP client is served by its own thread. An additional server thread pool handles the UDP requests. 

4.4 The Server Application
Now we are ready to write our first RPC server in .NET. Below is the listing of a simple .NET application that uses the generated stub to implement the demo server in module RPCServer.cs: 

using System;

using com.distinct.rpc;

 

namespace demoRPC

{

 

    /// <summary>

    /// The main program of the server sample.

    /// </summary>

 

    public class RPCServer : demoServer_vers1

    {

        static String[] text = {

            "This is the first line of text.",

            "This is the second line of text.",

            "This is the third line.",

            "This is the 4th line.",

            "This is the 5th line.",

            "This is the 6th line.",

            "This is the 7th line.",

            "This is the 8th line."

        };

 

        static public void Main(String[] args)

        {

            try

            {

                new RPCServer();

            }

            catch (RPCError e)

            {

                System.Console.Out.WriteLine(e);

            }

        }

 

        public RPCServer()

            : base()

        { }

 

        // here we override and implement the real remote procedure

 

        public override res_list get_line_1(request arg)

        {

            res_list rl = new res_list();

            result n = new result();

 

            for (int c = arg.from - 1; c < arg.to; c++)

            {

                // allocate the next element in the list

                n.next = new result();

                n = n.next;

                if (c == arg.from - 1)

                    rl.value = n;

 

                // assign the line number

                n.number = c + 1;

                // assign the line (string)

                n.line = text[c];

            }

 

            return rl;

        }

    }

}

This C# code implements the same simple server functionality as the C version shown earlier. It defines a new class named RPCServer that is derived from the demoServer_vers1 class and that finally implements the remote procedure get_line_1() in a straightforward manner. Compared to the implementation in C there are two differences worth noting: 

  1. The Main() method is not defined by the automatically generated stub but by the implementing class. The only thing Main() does, is create an RPCServer server object and handle the possible RPCError exception. 
  2. Compared to the C version all the ugly memory management has gone from get_line_1(). 

If you now compile this program and run it as a stand-alone program together with the RPC client as shown above, you will see that our first C# ONC RPC server behaves exactly like the initial C version. 

4.5 RPCBIND
If you are not using fixed server ports (see below) you will need an instance of the RPCBIND running on each machine that hosts ONC RPC servers. It is required to bind RPC clients to servers. Basically, RPCBIND is a name server. Server ports of ONC RPC servers are chosen randomly. It is the responsibility of RPCBIND to tell the clients the port number of a server with a given program/version number. RPCBIND itself is an RPC server that listens on a well-known port (111) for these requests. Typically, RPCBIND is running as a daemon in the background and it is usually provided by the standard ONC RPC infrastructure that is part of e.g. any network connected UNIX host (e.g. NFS relies on ONC RPC). However, if you are starting your RPC server on a machine that does not provide this infrastructure by default (e.g. all flavors of MS Windows) you have to make sure that RPCBIND is present to enable clients to connect to your new server. 

The Distinct ONC RPC/XDR for .NET Toolkit provides its own implementation of the RPCBIND. This native implementation of the RPCBIND implements the RPCBIND protocol versions 3 and 4 as well as the portmapper interface version 2. As RPCBIND uses a fixed port number there can be only one RPCBIND instance per node. Therefore, any server that uses the RPCGen.NET generated stub will first check, whether the system already provides RPCBIND, and if not, it will start its own RPCBIND. Please note that all ONC RPC servers that are subsequently started on that node will now also use this instance of RPCBIND. This means if you shut down the Distinct ONC RPC/XDR for .NET server that started RPCBIND, you will also lose the ability to bind new clients to the other, still running servers, as the RPCBIND that stores the binding information is gone. Connections that were established before RPCBIND went down are not affected, as the binding is established only once during the connection setup. You can avoid this by starting the RPCBIND manually before starting the server. 

To check whether RPCBIND is running you can use the Distinct RPCInfo utility or the standard ONC RPC rpcinfo tool with the -p <hostname> option. This should enumerate the servers registered with RPCBIND of this host. If this tool reports an error or does not show the servers you were expecting, there is obviously a problem with RPCBIND. 

If you have to disable the RPCBIND autostart feature of RPCGen.NET generated server stubs for some reason, you currently have to edit the xxxServer_versx class. Simply change the third parameter of the invocation base(DEMO_SERVER, DEMO_VERSION, true); in the constructors to false and the server will not try to start RPCBIND. 

RPCBIND may also be started as a stand-alone application by selecting it from the Start menu. This is the preferred way to run it while you are still debugging your application as you will be able to close RPCBIND using its interface in the system tray as needed in the course of your debugging.

Running RPCBIND as a Service

You may also run RPCBIND as a service. To do this run:

RPCBind.NET –install

This will install RPCBIND as a service but will not start the service. You must manually start the service the first time after installing it. To uninstall the service you need to run:

RPCBind.NET –uninstall

Note that when you or your customers purchase the server run times, the Distinct Installation will ask whether to install RPCBIND as a service, so that the user does not need to run the above command directly.

5. RPC Programs in Visual Basic .NET and C++/CLR

One of the main features of .NET is the Common Language Runtime (CLR). Among various other features it allows for interoperability of code modules written in different .NET-languages. Especially it allows you to use libraries (assemblies) written in C# from Visual Basic .NET and C++/CLR and vice-versa. This means, with the Distinct ONC RPC/XDR for .NET you can create the stub code for accessing ONC RPC server in C# and use these stubs from Visual Basic .NET or C++/CLR. Of course this also applies to implementing servers in Visual Basic .NET and C++/CLR and for using the methods from the toolkit directly in your code (e.g. for XDR encoding/decoding).  

5.1 Visual Basic Sample

As an example here is the Visual Basic code of a client accessing the demo sever. This code basically does the same as the C# client above (to keep it short, it uses directly the loopback address to access a local server and it makes just one call instead of some kind of loop):

Imports demoRPC

Module DemoClient

  Sub Main()

    Dim cl As demoClient_vers1
    cl = New demoClient_vers1(System.Net.IPAddress.Loopback, True)

    Dim req As request
    Dim res As res_list

    req = New request()
    req.from = 1
    req.to = 5

    res = cl.get_line_1(req)

    Dim r As result
    r = res.value

    While (Not r Is Nothing)
      System.Console.Write(r.number)
      System.Console.WriteLine(". " + r.line)
      r = r.next
    End While

  End Sub

End Module

As C# and Visual Basic cannot be mixed directly at code level, the following project structure is required to get the program running:

image005

The C# stub code generated by RPCGen.NET is put into a class library - in this example the library named "DemoClientLib". This code is then compiled to a DLL (a .NET assembly) and used as a reference in the Visual Basic project. Both projects, the library and the Visual Basic client also need a reference to the assembly RPC_NET.dll that contains the ONC RPC runtime library.

5.2 C++/CLR Sample

As an example here is the C++/CLR code of a client accessing the demo sever. This code basically does the same as the C# above (to keep it short, it uses directly the loopback address to access a local server and it makes just one call instead of a loop):

 

#include "stdafx.h"

 

using namespace System;

using namespace demoRPC;

 

int main(array<System::String ^> ^args)

{

    demoClient_vers1 ^cl = gcnew demoClient_vers1(System::Net::IPAddress::Loopback, true);

 

    request ^req = gcnew request();

    req->from = 1;

    req->to = 5;

 

    res_list ^res = cl->get_line_1(req);

 

    result ^r = res->value;

 

    while(r != nullptr)

    {

        System::Console::Write(r->number);

        System::Console::WriteLine(". " + r->line);

        r = r->next;

    }

    return 0;

}

As C# and C++/CLR cannot be mixed directly at code level, the following project structure is required to get the program running:

image006

The C# stub code generated by RPCGen.NET is put into a class library - in this example the library named "DemoClientLib". This code is then compiled to a DLL (a .NET assembly) and used as a reference in the C++/CLR project. Both projects, the library and the C++/CLR client also need a reference to the assembly RPC_NET.dll that contains the ONC RPC runtime library.

6. XDR to .NET Type Mapping 
In most cases the mapping of XDR types to .NET types and classes is quite obvious. For an overview of the complete mapping please have a look at the following table: 

XDR Type

.NET Type

int

int

unsigned int

uint

long

int

unsigned long

uint

short

short

unsigned short

ushort

hyper

long

unsigned hyper

ulong

char

sbyte

unsigned char

byte

float

float

double

double

bool

boolean

string

String

opaque

byte array

fixed length array

.NET array

variable length array

.NET array

optional data (pointer like *x)

Reference to an object of class x. If x is a basic type a special wrapper class XDRx that implements XDRType is used instead.

enum x

Class x implements XDRType with member variable int value and one constant int per enum constant.

struct x

Class x implements XDRType with member variables for each struct member.

union x

Class x implements XDRType with member variables for each union member including discriminant. No overlaying of members is supported (neither for type conversion nor for saving space).

typedef x y

Class x implements XDRType with member variable value of the redefined type y.


The most notable difficulty in the .NET mapping is the handling of typedefs. As .NET has no notion of type name aliasing, the only viable solution that preserves the introduced names is the definition of a new class. In the .NET mapping of typedef this new class has always only one data member, called value, that is a variable of the redefined type (for an illustration please refer to the generated file res_list.cs). 

7. Advanced Topics 
This section discusses how to accomplish some more advanced tasks in the Distinct ONC RPC/XDR for .NET environment. It includes information on how to authenticate, how to broadcast RPCs, how to run RPCs in batch mode and discusses server data flow, firewalls, using XDR streams and error and timeout handling. 

7.1 Authentication
By default, Distinct ONC RPC/XDR for .NET uses NULL authentication. However, this default can be changed at the client object by using setCredential() and setVerifier(). In the other direction the returned verifier from the server can be obtained after each call with getReturnedVerifier(). Please note that Distinct ONC RPC/XDR for .NET (like the C binding) provides the data structures for transferring authentication data and the methods for setting, retrieving and checking this data. It does not provide protection against forged authentication data. Any security and access control features have to be implemented by your application. Below is a code sample that creates a Unix authentication (sometimes also called System authentication) and sets it as the new credential of this client. From now on each request will be sent with this authentication. 

using System;

using System.Net;

using com.distinct.rpc;

 

namespace demoRPC

{

    ...

 

    try {

        client = new demoClient_vers1(

            System.Net.Dns.GetHostByName(args[0]).AddressList[0], // the host

            true); // use TCP

        // a new Unix authentication is created (UID = GID = 0)
        client.GetClient().setCredential(new AuthUnix(
            (int)(System.DateTime.Now.Ticks/10000000),
            "myhostname", 0, 0, new int[0]));

        ...

 

                res_list rl = client.get_line_1(req);

                ...

 

                // here it is assumed that the server returned an authentication

                // (typically of type short) that is now used for any subsequent calls

                client.GetClient().setCredential(client.GetClient().getReturnedVerifier());

                ...

The class com.distinct.rpc.NETRPCServer provides the DoAuth() method for handling authentication at the server side. Override this method if you want your server to check authentication (the default is no check). DoAuth() receives the credential and verifier provided by the client as well as the call identifier and returns the verifier that should be sent back with the reply to the calling client (simply return null for NULL authentication). It throws a com.distinct.rpc.RPCAuthError if it wants to signal an unsuccessful authentication. The following code fragment illustrates how DoAuth() can be used in the server code. 

... 
public override Auth DoAuth(int proc, Auth cred, Auth verf)
{
  if (cred.Flavor != NetRPC.kAUTH_UNIX)
    throw new RPCAuthError(NetRPC.kAUTH_TOOWEAK);

  // convert it to the correct type
  AuthUnix unix_cred = new AuthUnix(cred);

  if (unix_cred.Uid != 0)
    throw new RPCAuthError(NetRPC.kAUTH_BADCRED);

  // uses the same authenticator as returned verifier
  // usually you would use some shortcut here
 
return unix_cred; 


... 


This example implementation of DoAuth() checks for each call to see if it has a credential of type AuthUnix and if so if the user id is 0. If either condition is not fulfilled it throws a com.distinct.rpc.RPCAuthError exception in order to terminate any further processing of this call. The remote procedure itself is not called at all in this case. 

7.2 RPC Broadcast
RPC broadcast allows your application to broadcast a message to all computers on a network. Typically you would use it if you were trying to identify which servers are available. RPC broadcast runs over UDP only and always uses NULL authentication. RPC broadcast can handle many responses from all responding servers. It filters out all unsuccessful responses, therefore, if a version mismatch exists between the broadcaster and a remote service, the user of RPC broadcast will never know this. When using RPC broadcast keep in mind that all broadcast messages are sent to RPCBIND. Thus, only services that register themselves with their RPCBIND are accessible via RPC broadcast. 

In Distinct ONC RPC/XDR for .NET broadcast calls are implemented by the static member function NetRPCClient.BroadcastCall(). Syntax and semantics are very similar to the C binding. Here is an example of how this function may be used calling the demo server we used earlier. 

using System;

using System.Net;

using com.distinct.rpc;

 

namespace demoRPC

{

    /// <summary>

    /// The main program of the client sample

    /// </summary>

 

    public class RPCClient : BroadcastHandler

    {

        // "BroadcastHandler.onReply()" is the handler called for each reply packet

        public bool onReply(XDRType retval, System.Net.IPAddress addr)

        {

            result res = ((res_list)retval).value;

            while (res != null)

            {

                System.Console.Out.WriteLine(res.number + ":" + res.line);

                res = res.next;

            }

            return true; // return after the first reply

        }

 

        static public void Main(String[] args)

        {

            request req = new request();

            res_list rl = new res_list();

            RPCClient app = new RPCClient();

 

            try

            {

                for (int i = 1; i < 9; i++)

                    for (int j = i + 1; j < 9; j++)

                    {

 

                        req.from = i;

                        req.to = j;

                        System.Console.Out.WriteLine("from " + i + " to " + j);

 

                        demoClient_vers1.BroadcastCall(demoClient_vers1.DEMO_SERVER,

                            demoClient_vers1.DEMO_VERSION,

                            demoClient_vers1.get_line, req, rl, app);

                    }

            }

            catch (Exception e)

            {

                System.Console.Out.WriteLine(e);

            }

        }

    }

}

The changes to the original client program are:

·         The com.distinct.rpc.BroadcastHandler and com.distinct.rpc.XDRType have to be imported from com.distinct.rpc.

·         One class has to implement the com.distinct.rpc.BroadcastHandler interface. This interface has only one method, onReply(), that is called by Distinct ONC RPC/XDR for .NET for each reply packet (as long as onReply() returns false). In our example onReply() just prints the reply and returns true in order to stop the handling of further reply messages.

·         There is no longer a client object. It is not required as the BroadcastCall() method is static (a class-method) and not dependent on a special connection between a client and a server.

·         The return object is created as an empty object before the call is done and it is then passed to the call as an input parameter. 
The request is made via the BroadcastCall() method. This method is not implemented in the client class but directly in com.distinct.rpc.NETRPCClient. This means the call is generic and takes the server number, the server version, and the call identifier (all taken from the .x file and defined in the client class) as well as references to the argument and the return class as parameters.

·         The last parameter is the object that implements the reply handler onReply(). If this parameter is null all possible replies are discarded. 

7.3 How to Batch RPCs
Usually RPC is synchronous, meaning that clients send a call message and wait for the server to reply by indicating that the call succeeded. But this implies that clients may be sitting idle while servers process calls. Therefore, in cases where the client does not require an acknowledgement for every message sent, RPC messages can be placed in a pipeline of calls to the desired server. This process is known as batching. When operating in batch mode, RPC is simply acting as a message passing system. It is possible to batch RPC calls when: 

·         No RPC call in the pipeline requires a response from the server, and the server does not send a response message. 

·         The pipeline of calls is transported via the reliable stream transport protocol TCP. 

Because the server does not respond to every call, the client can generate new calls in parallel with the server executing previous calls. This overlapped execution greatly decreases the total elapsed time of a series of calls. Because the batched calls may be buffered, the client must eventually do a nonbatched call to flush the pipeline. 

Distinct ONC RPC/XDR for .NET provides two additional methods for the use of batched calls, one for the client and one for the server side. A client simply has to set the timeout value of the client object to the value of -1 in order to force all RPC calls to return immediately (by throwing an RPCTimeoutError exception) after sending the request message. Use the com.distinct.rpc.setTimeout() method to modify the timeout value.

The com.distinct.rpc.NETRPCServer class provides the IsBatched() method for handling batched calls at the server. Override this method if you want your server not to send any reply to a request. IsBatched() receives the call identifier and returns a boolean value. Depending on the call identifier it decides whether this call is one that uses the batched mode and if so it simply returns true. This tells Distinct ONC RPC/XDR for .NET that there is no need to send any reply to the client. 

7.4 Control and Data Flow on the Server
As we have seen so far, there are three methods that you can override to implement an ONC RPC server's functionality: NetRPCServer.DoAuth(), NetRPCServer.DoCall(), and NetRPCServer.IsBatched(). The server calls these functions in exactly this order to serve each call, meaning first it calls DoAuth() to check authentication, then it calls DoCall() to perform the remote procedure call itself, and finally, it checks with IsBatched() whether there is a need to send back a reply message. Simple RPC servers only override DoCall() and use defaults for DoAuth() and IsBatched(). You only have to override the latter two methods if you want to change this default. 

In some cases you might want to pass data between these three server-specific methods, e.g. if you need authentication data when processing the remote procedure call itself. Distinct ONC RPC/XDR allows for this data flow by providing the NetRPCServer.setClientData() and NetRPCServer.getClientData() methods. With setClientData() you can register any .NET object and retrieve it later by calling getClientData(). The data is stored per-thread. This means one server thread (the entity that serves one client request) can store any data that has to be transferred between the three methods (NetRPCServer.DoAuth(),NetRPCServer.DoCall(),NetRPCServer.IsBatched()) here. The data will no longer be available after the call to IsBatched(). 

7.5 Using XDR Streams
XDRStream implements all the encoding/decoding methods that are required for encoding/decoding the .NET basic types according to RFC 1832 (XDR). It also provides a simple memory management for the streamed data. XDRStream implements a dynamically growing buffer of bytes (in fact a queue). The constructors allow for the creation of an empty XDRStream (with default allocation size 1024 unless overridden by the InitialBufferSize registry entry), an empty XDRStream with user defined allocation size (in which case the greater of the user specified size and the value of InitalBufferSize in the registry if present will be used), or an XDRStream initialized with the content of an existing byte array (usually the start of the decoding procedure). The put_byte(), put_bytes(), and all xdr_encode_xxx() methods add bytes to the head of the queue, while the get_byte(), get_bytes(), and all xdr_decode_xxx() methods consume bytes from the tail. With get_length() and get_data() you can request the current length and content of the queue without changing its status. The reset() method resets the XDRStream to an empty queue. The dump() method just prints the contents of the queue in hex and ASCII to System.out (typically used for debugging). 

When you want to use Distinct ONC RPC/XDR for .NET just for reading or writing XDR encoded data you might want to use an XDRStream object and just manipulate the contents of its buffer. Alternatively you can derive your own XDRStream class that handles I/O automatically. 

The buffer size default values can be set by the user through registry entries as follows:

First create the following key if it does not already exist:

HKEY_LOCAL_MACHINE\SOFTWARE\Distinct\RPC.NET

And the following entries need to be made:

GrowthRate                   DWORD                          1 - 10

InitialBufferSize             DWORD                          1024 or greater

The default values for these are 2 and 1024 respectively.

7.6 Timeout Handling
Whenever asynchronous network protocols are designed, timeouts play an important role. Since setting a timeout for a response is the only way to determine whether your server is still alive, it is crucial to choose the right timeout value. If it is too short, you might think your server has crashed when it may just be working slowly under a heavy load. If the timeout is too long, you might waste important time waiting for something that will never happen. 

RPC maintains timeout values for both types of communication protocols (UDP and TCP). Typically, it is more important to have a timeout in UDP sessions, as the protocol itself does not provide any error detection. In addition, as UDP does not guarantee message delivery at all, a retransmission feature is desirable for UDP clients. RPC implements both. TCP provides reliable streams and usually it is a good idea to rely on the TCP connection management to determine whether your server is still alive. But, as this does not protect your client from locked up (or totally overloaded) servers, RPC also has a timeout mechanism for TCP connections. 

By default, the RPC timeout is set to 25 seconds (UDP retransmission timeout is 5 seconds). When a client does not receive any byte of the reply message for more than 25 seconds it assumes the server is no longer running and throws a timeout RPCError exception. You can change this timeout value by calling NetRPCClient.SetTimeout() on your client object, specifying the new timeout value in milliseconds (a value of 0 means no timeout at all). For clients running over the UDP protocol, the retransmission time can be changed by calling NetRPCClient.SetResend.

7.7 Error Handling
Whenever the Distinct ONC RPC/XDR for .NET runtime detects a non-recoverable error in a client call it will throw an exception. Usually this will be a System.Net.Sockets.SocketException when the problem is directly related to low-level socket I/O or an RPCError when the problem has a close relation to the RPC protocol. For details about the problem please look into the message string of the particular exception. Once you have received an exception, the status of the client object is undefined. Therefore, it is important to reestablish the connection by creating a new client object before trying to call the server again. 

Exception

Reason

RPCAuthError

An authentication error occurred

RPCDecodeError

A problem such as an unexpected EOF has occurred during XDR decoding

RPCServerError

An RPC server is not available or not registered with RPCBIND

RPCTimeoutError

An RPC blocked longer than the timeout value of the client

RPCError

For all other errors

7.8 Connecting through Firewalls 
Most RPC applications are designed for Intranets that have access to the arbitrary ports allocated by the system to a server. However over the Internet and within some corporate networks connections over arbitrary ports (like those established by the RPC protocol) are often blocked by firewalls. Please ask your firewall administrator for the details of your network security.  Distinct ONC RPC/XDR for .NET offers two solutions to mitigate this issue. It offers the ability to start a server on a fixed port number as well as the opportunity for the system administrator to restrict the ports that can be selected to a fixed range.

Using a fixed Port
Distinct ONC RPC/XDR for .NET allows you to build servers and clients with well-known (fixed) server ports. Depending on the configuration of your firewalls this may be sufficient for you to access certain servers over the Internet. 

The fact that RPC server ports are not known in advance makes it difficult to configure a firewall accordingly. When this turns out to be a problem it might help to be able to configure the used ports statically. Distinct ONC RPC/XDR for .NET supports this additional feature for .NET clients and servers. If you use fixed ports you will not need to run the RPCBIND server. 

To connect a client using a fixed port you only have to make a small change to the initialization sequence. You have to use another constructor of the client class. In our example the demo class can be created using the following code: 

client = new demoClient_vers1( 
    InetAddress.getByName(args[0]), // the host 
    Integer.parseInt(args[1]), // the port 
    true); // use TCP 


Now, the second command line parameter can be used to specify the known server port of the demo server.

Restricting the Range of Port Numbers that a Server will listen on

When you are not using fixed ports for an RPC service, the system will dynamically allocate a port to the service when it is started. This may prove problematic when a service needs to be accessed across firewalls because in most cases administrators do not wish to leave all ports open and at the same time have no control over which ports may be used by the underlying system. Distinct ONC RPC/XDR for .NET includes a new feature that allows the system administrator to take control over the pool of port numbers to be used by the system. This is done by specifying the range of ports that the system may use to allocate to an RPC service that is started on that system. When selecting a range of ports the administrator should make sure that these ports are not well-known ports reserved for other services that may be started on that same system. To make use of this feature the following registry key needs to be created on the system that is to host the RPC service or services in question:

HKEY_LOCAL_MACHINE\SOFTWARE\Distinct\RPC.NET

And the following entries need to be made:

MinPort                          DWORD                          value

MaxPort                         DWORD                          value

Where value is a numeric value in the range of 0 to 65535. When allocating the range of port numbers to be used please keep in mind that ports 0 through 1023 are well known ports and ports 1024 through 49151 are registered ports and may be required by other servers on the system.

7.9 Debugging your Application

The Distinct RPC libraries for .NET come with the capability to collect debug information to help when you are having difficulties. To enable the debug capabilities you need to add the following key to the Windows registry if it is not already present:

HKEY_LOCAL_MACHINE\SOFTWARE\Distinct\RPC.NET

And the following entries need to be made:

DebugLevel                   DWORD                          0 or 2

LogFilePath                    String Value                   path where log file should be created

The default value for DebugLevel is 0 (set this to 2 to turn logging on) and that for LogFilePath is the temp folder. Note that DebugLevel should be reset to 0 when your testing is complete. The log file containing the debug information collected is called rpcnet.log and will be created in the folder pointed to by the LogFilePath entry. If the LogFilePath entry does not exist, then the log file is created in the temp folder. The exact location of the temp folder may vary depending on your system. You can type “set %temp%” in a command prompt window to find the full path of the temp folder, or type “cd %temp%” in a command prompt window to change the current directory to the temp folder. To view the contents of the temp folder, you can open a copy of Windows Explorer by executing the command “%temp%” after choosing Run from the Start menu.

7.10 Upgrading from a Previous Version of RPC.NET

The following changes have taken effect with the release of version 2 of the Distinct ONC RPC/XDR for .NET toolkit.

·         The names of the client and server classes generated by the RPCGen.NET utility have been changed. All class names are now identified as client or server classes with the version number added to the class names. For example, a “sample.x” definition file might have previously created the client class “sample” and the server class “sampleServer”. The new version of RPCGen.NET will instead create the classes “sampleClient_vers1” and “sampleServer_vers1”. The file names of the client and server classes also have been changed accordingly. This change will help to make the source code easier to read and maintain.

·         To unregister a server you should now use the new RegisterServer() method. All previous versions of the NetRPCServer.RegisterServer() method have been deprecated and will issue a compiler warning. Applications should stop using the deprecated versions of this method as soon as possible because they may no longer be supported in future versions of Distinct RPC.NET.

·         The name of the assembly (DLL) containing RPC.NET has been changed from RPC_NET to RPC_NET2. To upgrade to the current version you need to remove the reference to the RPC_NET assembly from your project and add a reference to the RPC_NET2 assembly instead.