Chapter 41. Remoting

IN THIS CHAPTER

Remoting is one of the core parts of the .NET Framework that enables code from one AppDomain to communicate with code in another AppDomain, enabling the creation of RPC-style distributed applications. This chapter provides you with an introduction to remoting and then gets right into configuring and coding with remoting. You will learn about the new Inter-Process Communication (IPC) channel as well as how to use object lifetime leases and even how the generics feature is completely supported within the remoting infrastructure.

This chapter is by no means a complete reference on remoting. It provides you with enough information to get started using remoting and following some best practices. If you want more information on remoting, one of the best sources of detail surrounding .NET remoting is Advanced .NET Remoting, Second Edition by Ingo Rammer and Mario Szpuszta, ISBN 1590594177 (Apress).

Overview of Remoting

Remoting is the system within the .NET Framework that enables developers to create distributed applications quickly and easily. Remoting provides a layer of abstraction that allows the developer to worry more about the content and design of a distributed system than about the mechanism by which the components communicate. The choice of transport layer (such as TCP/IP, Inter-Process Communication, and so on) has no significant impact on the use of the remoting classes. This means that remoting applications can communicate over TCP/IP or any other means without having to make significant changes to the application.

There are three aspects to any remoting system:

  1. A remotable object—Any .NET object that will be transmitted between remoting endpoints
  2. A remoting host—An application domain that has been configured to respond to requests for the object indicated in item #1
  3. A remoting client—An application domain that initiates a request for the object indicated in item #1

Remoting works in an extremely simple way. A host application domain informs the remoting infrastructure that it is hosting an object on a specific URL using a specific channel (you’ll see more on channels later in the chapter). A remoting client then requests that object by supplying the object’s URL to the remoting infrastructure.

The remoting client then works with a proxy, much the same way COM interoperability works. Rather than your code communicating directly with the remote object, your code instead works with a proxy that exposes properties and methods with the same names and types as the remote object. Each member then forwards the request over the channel to a proxy on the other end. Figure 41.1 illustrates how the proxies fit in between the real objects involved.

Figure 41.1 Remoting at a glance.

Image

Data transfer between application domains is at the core of remoting’s functionality. The management of remote object instances and the method of transferring data between application domains both warrant detailed discussion.

Introduction to the MarshalByRefObject Class

In Chapter 1, there is a discussion about the difference between value types and reference types. A value type is one in which the data is stored directly on the stack, whereas a reference type only maintains a pointer on the stack and the real data is contained within the managed heap.

A similar difference exists for objects transmitted via remoting. When you pass an object as a parameter to a remote method, that object can either be passed as a serialized copy, or it can be passed as a reference to the original. If your code passes an object by reference over remoting, the code on the other end of the channel can make changes to the object. Those changes will take place on the original object.

If you pass a standard object to a remoting host, that object will be serialized and a new copy of the object will be created by deserializing the object data. To pass an object reference without creating additional copies, you can use an object that is derived from the MarshalByRefObject class. When you use objects that derive from MarshalByRefObject, a proxy is created, like the one shown in Figure 41.1, that essentially forwards calls to the original object. This feature gives remoting quite a bit of power and flexibility.

You don’t have to do anything special to make your class a MarshalByRefObject other than inheriting from it, as shown in the following code:

public class MyRemotableObject: MarshalByRefObject
{
   // class implementation
}

To ensure that your object is copied rather than used as a reference, simply ensure that it is serializable:

[Serializable()]
public class MyObjectToCopy
{
    // class implementation
}

Single Call Versus Singleton Objects

When using remoting, the client code requests an instance of a remote object using the remoting infrastructure. The instance that the client is using is not the same as the instance on the remote host.

When using remote objects, sometimes you want the object to be created long enough to perform a single task and then be disposed, and sometimes you want objects to remain, state intact, between method calls from a client.

The first type of object is called a single call object. Single call objects are instantiated at the request of a client and belong to only that client. After the client is done with the object’s single call, the object is disposed of. This means that with a single remoting host, each client gets its own unique, temporary copy of that object, as shown in Figure 41.2.

Conversely, a singleton object is an object that is shared among all clients. After the object has been created, possibly by a client, it will then be shared by all clients by being retrieved as a reference. In this situation, when client A makes a change to data on the remote object, client B will see that change, as illustrated in Figure 41.3.

Figure 41.2 Single call objects in a remoting infrastructure.

Image

Figure 41.3 Singleton objects in a remoting infrastructure.

Image

If you want to obtain a remote object instance that belongs solely to the client requesting it, a single call object is the right choice. However, if you want multiple clients to have access to the same remote object and its shared state, use a singleton object.

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:

Image

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

Image

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

Image

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

Image

Image

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:

Image

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:

Image

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

Image

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

Image

Image

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.

Working with Lifetime Leases

The garbage collector is responsible for disposing of objects that are no longer being used by active code within a .NET application. Remoted objects present a bit of a dilemma: Which garbage collector is responsible for disposing of the object, and how exactly does the GC know when a remoted object is no longer in use?

Some other distributed systems use what is known as “reference counting” to determine whether an object is ready for disposal. Reference counting over a network can create a lot of network traffic and overhead that only gets worse with the number of clients consuming a given remote object.

What the .NET Framework does is use a lifetime lease. When an MBR (MarshalByRefObject class instance) is created, it is given an initial lease. This lease determines how long the object can remain active without being accessed by clients. If the object’s lease runs out, it will be disposed. The next time a remote client accesses a member of the MBR, that remote client will be provided with a new instance of the object.

This behavior can be unexpected and can often cause a lot of problems in production applications. In a testing environment, MBRs often live much longer than they do in production because the remote object is being accessed far more often than in a production environment. When this happens, it is easy for developers to assume that the MBR is always active and is maintaining state properly.

The most common way of customizing the lifetime lease behavior of an MBR is to override the InitializeLifetimeService method on the MarshalByRefObject class. This gives the developer the ability to either create an instance of an object that implements the ILease interface to modify the object’s lease properties or to return a null, indicating that the object will live forever (“forever” in this case means until the application hosting the object shuts down).

Take a look at this implementation of InitializeLifetimeService:

Image

In the preceding code, some default values are assigned to the lease when the lease is first being created. A lease is first created when the remoting object is first activated (either by client or server). If the lease has expired and is seeking renewal, the preceding code adds one more minute to the object’s lifetime.

If your remote object is stateless and does not need to maintain the values of properties between requests, there is very little need to control the object’s lifetime. However, if you have created a Singleton remote object that is maintaining state information such as a list of all connected clients, you might want to grant the object an infinite lifetime by overriding InitializeLifetimeService as follows:

public override object InitializeLifetimeService()
{
    return null;
}

Object lease management is one of those tasks that are often neglected when creating a remoting application. Awareness of lease lifetimes early in the development process can save you a lot of debugging and troubleshooting when it comes time to deploy the application in a production environment.

Remoting with Generics

Quite possibly one of the most powerful new features of remoting in the .NET Framework v2.0 is the support for generics. This chapter has illustrated that client code can create an instance of a remote object and manipulate that instance either as a serialized copy or as a reference to the remote object via the MarshalByRefObject class. What makes remoting even more powerful is that remote objects can make use of generics and still be fully supported on the client.

To illustrate how this works, you can rewrite the first IPC example in this chapter to work with generics. By now you should be pretty familiar with the mechanics of creating a remoting solution using the “Shared Library” pattern. The following code provides a generic interface for the shared library:

Image

The data type of the message being sent to the remote object is now controlled by the generics type argument T. The following code shows the implementation of the shared interface in the server project:

Image

Listings 41.6 and 41.7 show the Program.cs code for the server and client respectively.

Listing 41.6 Program.cs for Generics IPC Sample Server

Image

Listing 41.7 Program.cs for IPC Generics Client Sample

Image

Each concrete type (for example, ISharedObject<string> or ISharedObject<DateTime>) gets its own URI and the client can then create instances of each of the remotely hosted concrete types. When the application is put together and both sides are executed, the following is the output from the server console:

Image

In the .NET Framework 2.0, when you print the name of a generic type implementation, a `1 appears and then the name of the type argument is included in square brackets. So in the preceding output, you can see that Generics.SharedObjectImplementation is the generic class and System.String and System.DateTime are both type arguments.

Summary

This chapter provided you with a basic introduction to the world of distributed applications using remoting. Remoting allows developers to create applications that can consume objects remotely without having to do extra work to make the same code work over HTTP, TCP, and so on. Using the channels available within the remoting infrastructure, applications can expose objects to clients that could be on the same machine or halfway across the world, providing a framework to create some of the most powerful distributed applications available today.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.233.135