|
Overview
The Microsoft Windows .NET Framework offers an easy way to develop highly distributed network applications by using .NET remoting. With .NET remoting you can develop applications that seamlessly access classes on one or more computers across a network, without having to deal with the complexities of programming to the network layer itself. This chapter will examine how .NET remoting relates to the network and how remoting and the network enable you to build applications that communicate across a network to access class types in other application domains on other computers. We will focus on how to set up remoting to communicate over a network by selecting an appropriate channel. We’ll assume you are familiar with core concepts. In this chapter, we’ll describe application domains and how .NET remoting allows you to access class types across application domains between two processes. You’ll learn that this technique requires first setting up a .NET remoting communication channel to form a link (or channel) between application domains and that setting up a communication channel requires using a built-in channel in the .NET Framework, such as the Hypertext Transfer Protocol (HTTP) channel. Developing a custom channel is also an option. We’ll describe the built- in channels and their features and discuss their strengths and weaknesses. We’ll also explain how to develop a custom channel that can communicate across a network. Understanding how to develop a channel will give you insight into how .NET remoting communicates between application domains. Further, developing a custom channel will help you build distributed applications that can run across almost any data transmission medium, such as a satellite link.
Application Domains
.NET remoting allows you to easily develop highly distributed applications in which an application running in one application domain can access the properties and methods of another class running in a different application domain. An application domain is an isolated operating environment where a .NET assembly can execute. You can think of an application domain as a process in a traditional operating system. The application domain provides isolation for your application and its operating system resources from other applications. However, an application domain is not a process at all, because a process in the .NET world potentially can have more than one application domain. In fact, every process has at least one application domain that is known as the default domain. The .NET remoting infrastructure provides a mechanism that allows your application to seamlessly access class types across application domains. In Figure 12-1 you can see that one or more application domains can operate within a process. Process A has two application domains, X and Y. Figure 12-1 also shows you that class type 1 in application domain X is accessing class type 2 in application domain Z. .NET remoting provides the channel of communication needed for class type 1 to access class type 2. |
Figure 12-1: Application domain overview
The nice thing about application domains is that you can isolate the running of class types. If a class is running in another application domain in your application and crashes, it will not harm the rest of your application running in another application domain, such as the default domain. A remote class type is a class library running in another application domain. The ability to communicate over a network to access remote class types in another application domain without having to deal with the semantics of the network layer is one of the greatest strengths of .NET remoting. When an application accesses properties and methods of a class in another application domain, the process is known as crossing application domain boundaries. Crossing application domain boundaries can be accomplished only by using .NET remoting channels.
To access a remote class type in another application domain, you have to make a class remotable. Making a class remotable requires telling the .NET remoting infrastructure to marshal the class type in order to access it across application domain boundaries. Marshaling is a process of packaging up a class in one application domain, sending it to another application domain, and un-packaging the class for use in the other application domain. The two styles of marshaling are marshal by value and marshal by reference.
Marshal by value occurs when a class type is copied from one application domain to another. When a calling application domain references a class in another application domain, the caller receives a copy of the class from the remoting infrastructure and the caller can access only the methods and properties of the copied class in the calling application domain. Marshal by value is useful when you need to pass a data structure, such as an array of strings, from one application domain to another. To marshal a class by value requires adding the Serializable attribute to a class definition as shown in the following code:
[Serializable]
public class MyData
{
public int Field1;
public string Field2
}
<Serializable()> _
Public Class MyData
Public Field1 As Integer
Public Field2 As String
End Class
When a class type is passed from one application domain to another, adding the Serializable attribute informs the .NET remoting infrastructure to package up and copy the class type to another application domain. For more information on how class types are serialized, see Chapter 4.
Marshal by reference occurs when you remotely access the properties and methods of a class in another application domain. To marshal a class by reference requires that a class inherit the MarshalByRefObject class. The following code demonstrates a class named DemoClass that can be marshaled by reference across application domains.
namespace Demo
{
public class DemoClass : MarshalByRefObject
{
public int m_TheValue = 0;
public void PrintMessage(string Message)
{
Console.WriteLine(Message);
}
}
}
Namespace Demo
Public Class DemoClass : Inherits MarshalByRefObject
Public m_TheValue As Integer = 0
Public Sub PrintMessage(ByVal Message As String)
Console.WriteLine(Message)
End Sub
End Class
End Namespace
Marshal by reference allows an application on computer A to invoke class methods and access class properties on computer B. For example, if there is an application on computer A that references DemoClass on computer B, then computer A can call PrintMessage and make the method run on computer B. The .NET remoting architecture performs marshal by reference by setting up a transparent proxy in the calling application domain that handles making method calls and accessing properties. The proxy makes the remote class look as if it is available in the client’s application domain when in reality it is running in another application domain. A proxy is established when an application instantiates the reference to the remote class. Once a proxy is established it will communicate with a peer by passing messages across a .NET remoting channel. We’ll discuss remoting channels in more detail later in the chapter. A major difference between marshal by value vs. marshal by reference is that marshal by value does not allow you to access remote properties and run remote methods. Instead, if you have a remote method (marshal by reference) that returns a class type, the class type must be marked with the Serializable attribute (marshal by value) so that the return type can be copied from a remote domain to your local domain.
When your application is ready to access a class using marshal by reference and marshal by value across application domains, the remoting communication infrastructure must be prepared to handle the marshaling using the class RemotingConfiguration. The RemotingConfiguration class allows you to set up the communications for the remoting infrastructure programmatically or with configuration files. RemotingConfiguration also allows you to identify what class types are available for remoting. We’ll first show how to set up remoting communications programmatically and then we’ll show how to set up remoting communications using configuration files.
Remoting communications have two sides, which are the client side (message sender) and the server side (message receiver). For a client to cross application domain boundaries and access the remotable class, a server must be available to sponsor the remoting infrastructure and communications.
On the server side, you’ll need to select a remoting channel that will listen for communications from a client. The ChannelServices class allows you to select and register a channel programmatically by calling the RegisterChannel method. The RegisterChannel method accepts an instance of a remoting channel and registers the channel to the remoting infrastructure. Once a channel is registered, you’ll need to register class types that can be accessed on the server. Registering class types on the server can be accomplished by calling either RegisterActivatedServiceType or RegisterWellKnownServiceType. Both methods make a class type available to a remoting client. RegisterActivatedServiceType makes a class available as a simple class data type, while RegisterWellKnownServiceType registers a class as a Uniform Resource Identifier (URI) where the client has to specify a URI to access the type. When a server registers a class type using RegisterWellKnownServiceType, the class type becomes server-activated, which means that the server side of the channel publishes the remoted class and makes an instance available to a client. By comparison, when RegisterActivatedServiceType is used, the class type becomes client-activated, which means that the client has to tell the server when to instantiate the class. Remoted classes that are server-activated can run in either singleton or in single-call mode. In singleton mode, all client calls to the remoted class type are handled by one instance of that object. This means that all client calls share a single instance of the remoted class. In single-call mode, a new instance of the class is created every time a call from the client comes in. When a class is client-activated, a new instance of the remoted class is created each time a call from the client arrives.
The following code sample shows how to set up the .NET Framework Transmission Control Protocol (TCP) channel to listen for remoting communication on TCP port 5150. The code also shows how to register the Demo.DemoClass presented earlier as a client-activated service type.
TcpChannel Channel = new TcpChannel(5150);
ChannelServices.RegisterChannel(Channel);
ActivatedServiceTypeEntry MyActivatedServiceTypeEntry = new ActivatedServiceTypeEntry(typeof(Demo.DemoClass));
RemotingConfiguration.RegisterActivatedServiceType(MyActivatedServiceTypeEntry);
Dim Channel As TcpChannel = New TcpChannel(5150)
ChannelServices.RegisterChannel(Channel)
Dim MyActivatedServiceTypeEntry As ActivatedServiceTypeEntry = New ActivatedServiceTypeEntry(GetType(Demo.DemoClass))
RemotingConfiguration.RegisterActivatedServiceType(MyActivatedServiceTypeEntry)
Once this code runs, the TcpChannel will begin listening for remoting requests to access the DemoClass type. This code can be run from almost any type of application; for example, you could develop a Windows application that will host the TcpChannel. Or you could develop a simple console-style application that runs this code and waits for the user to stop the console application. The choice is yours. As long as the server-side application is running, the TcpChannel will continue to process remote requests.
Accessing a remote class from the client side requires that you identify a class type to the client side remoting infrastructure as being available as a remote type. Two methods allow you to inform the remoting infrastructure that a class type is available as a remote type. They are RegisterActivatedClientType and RegisterWellKnownClientType. The RegisterActivatedClientType method will identify a remote class by type, while RegisterWellKnownClientType will identify a remote class as a URI. As discussed earlier, remote types on a remote server can be identified as simple types or as a URI depending how they are registered.
Once a class type has been identified as a type or as a URI to the client remoting infrastructure, your client application can begin accessing the class across a remoting channel. When your client application creates an instance of a registered class using the new operator, the client remoting infrastructure will set up a transparent proxy on the client that will be used to access methods and properties of the remote class on the server. The following code sample shows how to register the DemoClass described earlier as a client-activated type that will connect to a server using TcpChannel.
TcpChannel Channel = new TcpChannel();
ChannelServices.RegisterChannel(Channel);
ActivatedClientTypeEntry MyActivatedClientTypeEntry = new ActivatedClientTypeEntry(typeof(Demo.DemoClass), "tcp://MyServer:5150");
// Register DemoClass on the client end so that it can be activated on the server.
RemotingConfiguration.RegisterActivatedClientType(MyActivatedClientTypeEntry);
// Activate the DemoClass as a remote object
Demo.DemoClass TryDemo = new Demo.DemoClass();
Dim Channel As TcpChannel = New TcpChannel()
ChannelServices.RegisterChannel(Channel)
Dim MyActivatedClientTypeEntry As _
ActivatedClientTypeEntry = New ActivatedClientTypeEntry(GetType(DemoClass), "tcp://MyServer:5150")
' Register DemoClass on the client end so that it
' can be activated on the server.
RemotingConfiguration.RegisterActivatedClientType(MyActivatedClientTypeEntry)
' Activate the DemoClass as a remote object
Dim TryDemo As DemoClass = New DemoClass()
In this example, specific parameters such as remote channel port information and class type information are hard-coded for the remoting configuration. Hard-coded parameters do not make the application very flexible, especially if the application is designed to run in different operating environments. As an alternative, in this code we could have supported command-line parameters and set up the remoting communication. However, the RemotingConfiguration class offers a better way to configure the remoting infrastructure parameters by using configuration files.
The most flexible way to set up communications in .NET remoting is by using XML configuration files on both the client and server to activate class types. A configuration file enables you to control every aspect of setting up remoting channels and object-activation activities without having to change your application directly. In this section we’ll show how to set up a remoting channel that uses TCP to communicate over the network so a client can access the remotable class named Demo.DemoClass described earlier.
Setting up remoting communications on both the client side and server side requires calling the RemotingConfiguration.Configure method and passing an XML file that describes remoting settings for each side of a channel. An XML configuration file should have tags that describe specific remoting settings such as what classes you are remoting and what communications channel you plan to use. There are many tags, consult the Configuration File Schema for the .NET Framework (SDK) for a complete listing.
Setting up a remoting client requires constructing a client-configuration XML file and passing the file name to RemotingConfiguration.Configure. The syntax is:
RemotingConfiguration.Configure(filename, ensureSecurity)
The filename parameter is String type and the ensureSecurity is Boolean/bool. The ensureSecurity is set to true to enable security; otherwise, set to false. The name of the remoting configuration file can be null a null reference (Nothing in Visual Basic and nullptr in C++). The following code fragment demonstrates how to set up client remoting using an XML file named client.config.
// Reads the client.config configuration file and configures the remoting infrastructure
RemotingConfiguration.Configure("client.config", false);
' Reads the client.config configuration file and configures the remoting infrastructure
RemotingConfiguration.Configure("client.config", true)
In the earlier discussion of programmatically configuring a remoting client, we configured the Demo.DemoClass to use the TCP remoting channel. The following XML script performs the same settings using an XML file.
|
|
As this example shows, configuring settings such as channel information by means of XML configuration files allows you to make changes to your remoting application’s behavior without changing any code.
The server side is as easy to configure as the client side. In your application you also must invoke the RemotingConfiguration.Configure method and pass a server configuration XML file. Using the programmatic server configuration example described earlier, the following XML script shows how to register the demo class as an activated client type and use the TCP channel. The sample also tells the remoting server to listen for TCP communication on port 5150.
<configuration> <system.runtime.remoting> <application> <service> <activated type="Demo.DemoClass, demo" /> </service> <channels> <channel ref="tcp" port="5150" /> </channels> </application> </system.runtime.remoting> </configuration> |
In the configuration examples provided so far, we selected the TCP remoting channel to use across application domains. We also called out specific parameters such as port settings. The next section describes all the available remoting channels in the .NET Framework that are used to sponsor communications between application domains. As we describe each channel, we also will list its configuration properties. The following examples show how to use marshal by reference and marshal by value as well as how to programmatically control remoting configurations. The sample programs are given in C#, VB .NET and C++/CLI.