Working with Remoting Channels


Before you can create a working remoting sample, you need to be able to manipulate the remoting configuration. A remoting host needs to inform the remoting infrastructure of what objects it is hosting and the channel and URL at which the object can be found. A remoting client needs to tell the remoting infrastructure where it can find remote objects.

A remoting host provides the remoting infrastructure with information about an object being hosted by using code similar to the following:

RemotingConfiguration.RegisterWellKnownServiceType(                 typeof(HostedObject),                 "HostedObject.rem",                 WellKnownObjectMode.Singleton); 


The RemotingConfiguration class is the main interface between your code and the remoting infrastructure. The remoting host uses the RegisterWellKnownServiceType method to indicate what type is being exposed remotely, as well as the well-known object mode and the object's URI. It is extremely common for the same application to host multiple objects. To distinguish one object from the other, a URI is assigned to each object. In the past, this URI has often been the object's type followed by .rem as a convention, but you can use any string you like. The object mode in the preceding line of code can either be single call or singleton.

Whereas the host, or server, must register a "service" type, a remoting client can either configure a client type using a configuration file, or it can just manually obtain a reference to the remote object by specifying its URI. The URI consists of the channel name, the machine address, and the object URI. For example, for an object hosted as RemoteObject.rem on the TCP channel at a machine called LabServer12 on port 2020, the URI a client would use to obtain a reference to the object would be

tcp://labserver12:2020/RemoteObject.rem 


The next two sections show you how to configure two different types of channels, IPC and TCP, as well as when each should be used. Using each of these channels, you will build and run a distributed system.

Using the IPC Channel

The Inter-Process Communication (IPC) channel is one that is used to communicate between components on the same machine. The IPC channel uses a mechanism very similar to named pipes, a means of Inter-Process Communication used before managed code. If you know that the remote objects with which your client code needs to communicate reside on the same physical machine, there is no faster and more efficient remoting channel to use than the IPC channel.

To start this sample, create a new console application called SimpleRemoting. This will also create a solution with the same name. Before doing anything to the console application, create a new class library called SharedLibrary. Inside the SharedLibrary project, add the following definition for the ISharedObject interface:

using System; using System.Collections.Generic; using System.Text; namespace SharedLibrary {     public interface ISharedObject     {         void PrintMessage(string msg);     } } 


There is a specific reason for this shared library. For a client to create an instance of a remotely hosted object, the client needs to be able to access the type information on the remote object. This means that, in most cases, the client needs a direct reference to the assembly containing the remote object. At first this doesn't seem like such a bad thing, but it creates a dependency that can cause a lot of problems in the long run. By creating an interface in an assembly that can be referenced by the client and the server, the client can work with any server that implements the interface, regardless of the concrete implementation. This also prevents the client from having to reference any of the assemblies on the remote side.

The remoting host application can then contain an implementation of the shared interface. Add a class called HostedObject to the SimpleRemoting console application project. Make sure to add a project reference from the console application project to the SharedLibrary project and a reference to the System.Runtime.Remoting assembly. The code for this class is shown in Listing 41.1.

Listing 41.1. The HostedObject Class

using System; using System.Collections.Generic; using System.Text; using SharedLibrary; namespace SimpleRemoting { public class HostedObject : MarshalByRefObject, ISharedObject { public void PrintMessage(string msg) {     Console.WriteLine("Message Printed from AppDomain:  " +         AppDomain.CurrentDomain.FriendlyName);     Console.WriteLine("Message: " + msg); } } } 

The PrintMessage method will display some text to the console output window. This text will contain the name of the application domain in which the HostObject class is running, as well as the message sent to it by the client application.

Next you need to set up the hosting environment for the HostedObject class. To do this, modify the Program.cs file of the SimpleRemoting project to look like the code shown in Listing 41.2.

Listing 41.2. Program.cs for SimpleRemoting

using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Ipc; using System.Collections.Generic; using System.Text; namespace SimpleRemoting { class Program { static void Main(string[] args) {     IpcChannel ipc = new IpcChannel("sampleapp");     ChannelServices.RegisterChannel(ipc, false);     RemotingConfiguration.RegisterWellKnownServiceType(         typeof(HostedObject),         "HostedObject.rem",         WellKnownObjectMode.Singleton);     Console.WriteLine("Remoting host activated... Press enter to stop.");     Console.ReadLine(); } } } 

Because the IPC channel is restricted to working on a single machine and it functions the same way named pipes do, you specify a string name for the port instead of a number. In all remoting applications, the channel on which an object is hosted or consumed must be registered using ChannelServices.RegisterChannel. The false in that method call turns off remoting security checks. In a production application, you would probably want channel security to be enabled.

Now you need a client application. Add a new console application to the solution called RemotingClient. Add a project reference to the SharedLibrary project and a reference to the System.Runtime.Remoting assembly. Modify the Program.cs file so that the code looks like the code in Listing 41.3.

Listing 41.3. Program.cs for RemotingClient

using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Ipc; using System.Collections.Generic; using System.Text; using SharedLibrary; namespace RemotingClient { class Program { static void Main(string[] args) {     IpcChannel ipc = new IpcChannel("sampleapp_client");     ChannelServices.RegisterChannel(ipc, false);     ISharedObject sharedObj = (ISharedObject)      Activator.GetObject( typeof(ISharedObject), "ipc://sampleapp/HostedObject.rem");     if (sharedObj == null)     {         Console.WriteLine("Failed to establish IPC link");         return;     }     sharedObj.PrintMessage("Hello from AppDomain " + AppDomain.CurrentDomain.FriendlyName);     Console.ReadLine(); } } } 

This code attempts to create an instance of the remote object. If a valid instance has been created, it calls the PrintMessage method of the remote object with a string that contains the friendly name of the client's application domain.

Build the solution and then run the SimpleRemoting.exe file. You will see the message indicating that the remote host has started. Now run the RemotingClient.exe application. In the window containing the output from SimpleRemoting.exe, you will see the following text:

Remoting host activated... Press enter to stop. Message Printed from AppDomain:  SimpleRemoting.exe Message: Hello from AppDomain RemotingClient.exe 


There are two important things to note about the way this application ran:

  • The output appeared within the SimpleRemoting.exe window.

  • The application domain of the hosted object was SimpleRemoting.exe.

This is important because the Singleton object executed within the host application domain. This is often a reason for using remote objects: so that a client can gain access to something that ordinarily only the remote object could access, such as a database or other protected resource.

Using the TCP Channel

The TCP channel varies only slightly in its use from the IPC channel. The IPC channel uses a string name for its port, whereas the TCP channel uses a standard TCP port number to identify the port on which the channel is running. The sample in this section will show you how to work with the TCP channel as well as show you how to pass nonstandard .NET types (such as classes you created) back and forth between remoting endpoints.

To start off, create a new console application called TcpRemoting. A new solution will be created with the same name. Next, create a new class library called "Shared Library." This should look fairly similar to the IPC sample so far. Add a new class to the SharedLibrary project called Customer, as shown in the following code:

using System; using System.Collections.Generic; using System.Text; namespace SharedLibrary { [Serializable()] public class Customer {     public string ID;     public string FirstName;     public string LastName; } } 


Note the presence of the SerializableAttribute class associated with the Customer class. Without this, or inheriting from MarshalByRefObject, a class instance cannot be transmitted between remoting endpoints. Now create the shared interface in a file called ISharedObject.cs:

using System; using System.Collections.Generic; using System.Text; namespace SharedLibrary {     public interface ISharedObject     {         void ProcessCustomer(Customer cust);         Customer GetCustomer(string ID);     } } 


With the shared components in place, you can modify the Program.cs of the TcpRemoting project as shown in Listing 41.4.

Listing 41.4. Program.cs for TcpRemoting

using System; using System.Collections.Generic; using System.Text; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; namespace TcpRemoting { class Program {     static void Main(string[] args)     {         TcpChannel tcp = new TcpChannel(8080);         ChannelServices.RegisterChannel(tcp, false);         RemotingConfiguration.RegisterWellKnownServiceType(             typeof(SharedObject),             "SharedObject.rem",             WellKnownObjectMode.Singleton);         Console.WriteLine("Remoting host started. Press enter to stop.");         Console.ReadLine();     } } } 

Now that the server is ready to go, you can change the Program.cs of the TcpClient project to match the code in Listing 41.5.

Listing 41.5. Program.cs for TcpClient

using System; using System.Collections.Generic; using System.Text; using SharedLibrary; namespace TcpClient { class Program { static void Main(string[] args) {     ISharedObject sharedObj = (ISharedObject)         Activator.GetObject(typeof(ISharedObject),         "tcp://localhost:8080/SharedObject.rem");     if (sharedObj == null)     {         Console.WriteLine("Failed to obtain remote reference.");     }     else     {         Customer cust = new Customer();         cust.ID = "newcust001";         cust.FirstName = "John";         cust.LastName = "Customer";         sharedObj.ProcessCustomer(cust);         Customer retrievedCust = sharedObj.GetCustomer("newcust002");         Console.WriteLine("Retrieved customer from remote location:  " +             retrievedCust.FirstName + " " + retrievedCust.LastName);     }     Console.ReadLine(); } } } 

When you compile and run this application the same way you ran the IPC sample, the host console output will contain the phrase:

Remoting host started. Press enter to stop. Processing customer newcust001 


The client output will look like this:

Retrieved customer from remote location:  Auto Generated 


The main thing to take away from these samples is that the choice of remoting channel is more a design decision than anything else because the task of using each channel varies only slightly with the specific channel. In addition, knowing where the code is executing and in which AppDomain the code executes can be extremely helpful when designing complex remoting solutions.



Microsoft Visual C# 2005 Unleashed
Microsoft Visual C# 2005 Unleashed
ISBN: 0672327767
EAN: 2147483647
Year: 2004
Pages: 298

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