Remoting: Passing Types and Objects Across Boundaries

The final topic in this chapter is remoting . Remoting is the technique of passing types or objects across process or machine boundaries using a process called marshalling (which is the general name for the technique of moving types or objects across programming boundaries).

In this topic, we'll create a server and client application to pass types and objects between processes on the same machine, but you can as easily pass them between processes on different machines.

In order to move types and objects across process boundaries, we're going to use two FCL classes MarshalByRefObject , which marshals types and objects, and HttpChannel , which allows us to communicate between processes using port numbers . ( HttpChannel handles firewalls well, but if firewalls are not an issue, you can also use TcpChannel , which is often faster.) You can see the significant public methods of MarshalByRefObject in Table 15.3, the significant public properties of the HttpChannel class in Table 15.4, and the significant public methods of the HttpChannel class in Table 15.5.

Table 15.3. Significant Public Methods of the MarshalByRefObject Class

METHOD

PURPOSE

CreateObjRef

Creates an object that can generate a proxy used to communicate with a remote object.

Table 15.4. Significant Public Properties of the HttpChannel Class

METHOD

PURPOSE

ChannelData

Returns channel-specific data.

ChannelName

Returns the name of the current channel.

ChannelPriority

Returns the priority of the current channel.

IsFixedSize

Returns true if the number of properties in the current channel object is fixed.

IsReadOnly

Returns true if the collection of properties in the current channel object is read-only.

IsSynchronized

Returns true if the current dictionary of channel object properties is synchronized.

Item

Returns or sets a channel property associated with the specified key. This property is the indexer for the HttpChannel class.

Keys

Returns an ICollection of keys with which the channel properties are associated.

Properties

Returns an IDictionary of the channel properties associated with the current channel.

Values

Returns an ICollection of the values of the properties associated with the current channel object.

Table 15.5. Significant Public Methods of the HttpChannel Class

METHOD

PURPOSE

StartListening

Makes the current channel start listening for requests .

StopListening

Makes the current channel stop listening for requests.

COMMUNICATING BETWEEN PROCESSES IN C++ AND C#

In languages like C++, you can also communicate between processes using memory mapping , which allows you to share memory between two processes. Memory mapping, with its heavy emphasis on pointers, is not available in C#.


In the first example, the server will provide a type to the client, which will create objects of that type. The type we'll remote will have only one method ToCaps , which takes a string argument, capitalizes that string, and returns it. Although the object is used locally in the client, its code executes in the server.

In this example, we'll use this object to send a text string from the client to the server across process boundaries, and code in the server will then send the capitalized text back to the client. It's useful to create an interface in this case to tell the client how the object that it's remoting works, so in this example, we'll implement the ICaps interface that you see in Listing 15.6, ch15_06.cs.

Listing 15.6 A Remoting Interface (ch15_06.cs)
 public interface ICaps {   string ToCaps(string text); } 

To compile ch15_06.cs into ch15_06.dll, use this command (or, of course, create a DLL project in the IDE):

 
 C:\>csc /t:library ch15_06.cs 

Now we're ready to create the server that will remote the ICaps type to the client.

Creating the Server

There are two types of servers you can use for remoting in .NET well-known and client-activated servers. The difference is that connections to client-activated servers are permanent as long as a session lasts, and connections to well-known servers are created each time the client sends a message to the server. This example uses a well-known server object.

In turn , there are two types of well-known servers singleton and single-call . Singleton servers handle connections to clients with a single object, whereas single-call servers handle requests from the client with a new object for each request. Single-call servers are useful when you have a lot of clients that need to be handled, but in this case, we have only one client so we'll use a singleton server.

To create a type to be marshaled, we'll construct a class, Capitalizer , which inherits MarshalByRefObject and implements the ICaps interface:

 
 public class Capitalizer : MarshalByRefObject, ICaps {     .     .     . } 

This class simply implements the ToCaps method, which accepts a string, capitalizes it, and returns it:

 
 public class Capitalizer : MarshalByRefObject, ICaps {  public string ToCaps(string inText)   {   System.Console.WriteLine("Got the text: \"{0}\"", inText);   string outText = inText.ToUpper();   System.Console.WriteLine("Sending back the text: \"{0}\"", outText);   return outText;   }  } 

This is the type we'll remote to the client.

Actually setting up the server is easy; all we have to do is register the Capitalizer type (so that you can create objects of that type in the client) and let .NET know that we'll be providing services on a particular port. In this case, we'll use port 65432 in the current machine for our server:

 
 public static void Main() {  HttpChannel channel = new HttpChannel(65432);   ChannelServices.RegisterChannel(channel);  .     .     . } 

To register the Capitalizer type, indicating that it's available from this server, you can use the RemotingConfiguration class's RegisterWellKnownServiceType method. Here's how we do that, indicating that we are creating a singleton well-known server, and establishing an endpoint (named EndPoint1 here), which is the name the remoting service associates with the type we'll provide:

 
 public static void Main() {   HttpChannel channel = new HttpChannel(65432);   ChannelServices.RegisterChannel(channel);  Type capitalizerType = Type.GetType("Capitalizer");   RemotingConfiguration.RegisterWellKnownServiceType   (capitalizerType, "EndPoint1", WellKnownObjectMode.Singleton);  .     .     . } 

And that's all it takesnow the server runs by itself, until the application is terminated . To let it run for a while, we'll display a message, "Server is running, press Enter to quit." , and end the application when the user presses Enter, as you see in Listing 15.7, ch15_07.cs.

Listing 15.7 A Remoting Server (ch15_07.cs)
 using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; public class Capitalizer : MarshalByRefObject, ICaps {   public string ToCaps(string inText)   {     System.Console.WriteLine("Got the text: \"{0}\"", inText);     string outText = inText.ToUpper();     System.Console.WriteLine("Sending back the text: \"{0}\"", outText);     return outText;   } } public class ch15_07 {   public static void Main()   {     HttpChannel channel = new HttpChannel(65432);     ChannelServices.RegisterChannel(channel);     Type capitalizerType = Type.GetType("Capitalizer");     RemotingConfiguration.RegisterWellKnownServiceType       (capitalizerType, "EndPoint1", WellKnownObjectMode.Singleton);  System.Console.WriteLine("Server is running, press Enter to quit.");   System.Console.ReadLine();  } } 

To build the server, you have to reference ch15_06.dll, where the ICaps interface is defined. Create the server, ch15_07.exe, like this (or in the IDE by adding a reference to ch15_06.dll in a console project):

 
 C:\>csc /t:exe /r:ch15_06.dll ch15_07.cs 

Next we'll create the client application that will communicate with the server.

Creating the Client

The client is going to connect to the server on the port we've designated, 65432 , and so gain access to our Capitalizer class, which implements the ICaps interface. We start creating the client by creating a new HttpChannel object and registering it as we did in the server:

 
 public static void Main() {   HttpChannel channel = new HttpChannel();   ChannelServices.RegisterChannel(channel);     .     .     . 

This time, however, we indicate which port to use when we connect to our server, referring to the server with the URL http://localhost:65432/EndPoint1 . Here's how we connect to the server and create a marshaled object from the type our server provides:

 
 public static void Main() {   HttpChannel channel = new HttpChannel();   ChannelServices.RegisterChannel(channel);  MarshalByRefObject marshalledObject =   (MarshalByRefObject) RemotingServices.Connect   (typeof(ICaps), "http://localhost:65432/EndPoint1");  .     .     . 

We're almost home. We've created a marshaled object, but it's still anonymous. To let the C# compiler know what members are available in this object, we'll cast it into a new ICaps variable, capper :

 
 try {   ICaps capper = (ICaps) marshalledObject;     .     .     . 

Now you can use this new object, capper , in the client's code. In this case, we'll call the ToCaps method of this object to capitalize the text "No worries." , as you see in the client's code, ch15_08.cs, Listing 15.8.

Listing 15.8 A Remoting Client (ch15_08.cs)
 using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; public class ch15_08 {   public static void Main()   {     HttpChannel channel = new HttpChannel(0);     ChannelServices.RegisterChannel(channel);     MarshalByRefObject marshalledObject =       (MarshalByRefObject) RemotingServices.Connect       (typeof(ICaps), "http://localhost:65432/EndPoint1");     try     {       ICaps capper = (ICaps) marshalledObject;  string outText = "No worries.";   System.Console.WriteLine("Sending this text: \"{0}\"", outText);   string inText = capper.ToCaps(outText);   System.Console.WriteLine("Got this text back: \"{0}\"", inText);  }     catch(System.Exception e)     {       System.Console.WriteLine(e.Message);     }   } } 

That completes the client. Compile it like this (or by adding a reference to ch15_06.dll to a console project in the IDE):

 
 C:\>csc /t:exe /r:ch15_06.dll ch15_08.cs 

Through marshalling, you can use the capper object in the client, but its code will run on the server. The server and client are two different processes, so they'll run in different DOS sessions. Start the server, ch15_07.exe, in one DOS session, and the client, ch15_08.exe, in another. When the server starts, you'll see this in its DOS window:

 
 C:\>ch15_07 Server is running, press Enter to quit. 

When the client starts, you'll see this in its DOS window:

 
 C:\>ch15_08 Sending this text: "No worries." 

By calling the ToCaps method, the client sends the text "No worries." to the server, which capitalizes that text and sends it back, as it indicates in its DOS window:

 
 C:\>ch15_07 Server is running, press Enter to quit.  Got the text: "No worries."   Sending back the text: "NO WORRIES."  

The client gets back its text capitalized, and displays the results this way:

 
 C:\>ch15_08 Sending this text: "No worries."  Got this text back: "NO WORRIES."  

Congratulations; you've just handled remoting in a client/server relationship.

What if you wanted to create a new server object each time a client sent a request? You can use a single-call server instead; just change this line in the server:

 
 RemotingConfiguration.RegisterWellKnownServiceType   (capitalizerType, "EndPoint1", WellKnownObjectMode.Singleton); 

to this:

 
 RemotingConfiguration.RegisterWellKnownServiceType   (capitalizerType, "EndPoint1", WellKnownObjectMode.SingleCall); 

Sending an Object to the Client

In our server example, we exported an entire type using RegisterWellKnownServiceType . But what if you just wanted to export an object, not a type? You can do that with RemotingServices class's Marshal method. You can see a new version of our server in Listing 15.9, ch15_09.cs, where we create a new object of the Capitalizer class and send the actual object. The code that's different from our previous version of the server is highlighted.

Listing 15.9 Sending an Object (ch15_09.cs)
 using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; public class Capitalizer : MarshalByRefObject, ICaps {   public string ToCaps(string inText)   {     System.Console.WriteLine("Got the text: \"{0}\"", inText);     string outText = inText.ToUpper();     System.Console.WriteLine("Sending back the text: \"{0}\"", outText);     return outText;   } } public class ch15_09 {   public static void Main()   {     HttpChannel channel = new HttpChannel(65432);     ChannelServices.RegisterChannel(channel);  Capitalizer capitalizer = new Capitalizer();   RemotingServices.Marshal(capitalizer, "EndPoint1");  System.Console.WriteLine("Server is running, press Enter to quit.");     System.Console.ReadLine();   } } 

There's another way to communicate as well. You can use the Simple Object Access Protocol, SOAP.



Microsoft Visual C#. NET 2003 Kick Start
Microsoft Visual C#.NET 2003 Kick Start
ISBN: 0672325470
EAN: 2147483647
Year: 2002
Pages: 181

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net