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
streams, timeout 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
In 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:
- 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.
- 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.
- 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.
- 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.
- 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.
- 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).
- 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:
- 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.
- 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:

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:

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.
|