.NET Remoting: Handling Remote Server Events
- Implementing a Remote Server
- Defining Shared Code
- Handling Server Events on the Client
- Multithreading Caution
- Summary
There are so many technological factoids that it's easy to forget details. For me, this is true of .NET Remoting. There are dozens of small facts, but I create remote clients and servers so infrequently that it's easy to forget those facts. Thus, I write reminders for myself: small nuggets of code and articles that make it easy for me to remember forgotten facts.
Implementing a Remote Server
Jack Webb's Sgt. Joe Friday made the "Just the facts" statement a permanent part of Americana, and this article offers just the facts. In that vein, we'll begin with the idea that you know basically what a distributed application is; you know a bit about .NET Remoting; and you know that .NET Remoting servers can be Windows services, Windows Forms applications, console applications, or hosted by IIS.
Now, let's concentrate our focus on the mechanics of raising and handling remote events. To do this, we'll need a server; I'll use a console application because it permits us to keep our focus on the Remoting bits. To create the remote server, we'll need these elements:
An application
An app.config file that configures the channel
A remotable, marshal-by-reference singleton object
Creating the Console Application
The remote server needs to be available to service client requests. A simple console application is one way to create this server. (I explore other ways to create remote servers in Visual Basic .NET Power Coding.)
The console application configures the server to listen but otherwise just sits there. We can accomplish this in three lines of code (see Listing 1).
Listing 1 A Simple Remote Server
using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; namespace Server { class Class1 { [STAThread] static void Main(string[] args) { Console.WriteLine("press [enter] to quit"); RemotingConfiguration.Configure("Server.exe.config"); Console.ReadLine(); } } }
This server is your basic Hello World console application except for the statement that invokes the static RemotingConfiguration.Configure method. This single statement, with a properly defined XML app.config file, turns our simple application into a remote server.
Configuring the Server
You can configure both clients and servers either programmatically or with .config files. I prefer to use the .config file. The most important part of the .config file, as far as Remoting is concerned, is the configuration of the channel and service. The channel tells .NET how to transport data between clients and servers and how to treat the formatted data going back and forth. The service indicates the assembly and class that represent the remotable object.
There are variations on the XML elements, which you can look up in the .NET reference material, but the basic configuration shown in Listing 2 will get you started.
Listing 2 Using an app.config File To Configure Remotable Objects
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.runtime.remoting> <application> <channels> <channel ref="http" port="9999"> <serverProviders> <provider ref="wsdl" /> <formatter ref="soap" typeFilterLevel="Full" /> <formatter ref="binary" typeFilterLevel="Full" /> </serverProviders> <clientProviders> <formatter ref="binary" /> </clientProviders> </channel> </channels> <service> <wellknown type="General.Shared, General" objectUri="Shared.soap" mode="Singleton" /> </service> </application> </system.runtime.remoting> </configuration>
The <channels></channels> section describes the protocol (HTTP or TCP) and the port on which communications should occur. Avoid commonly used ports such as 20, 21, 23, 25, 80, 110; ports above 1024 should be reasonably safe. The <serverProviders> and <clientProviders> were required in .NET 1.1 and describe formatters that know how to treat the data going across the channel.
The <service> tag contains the <wellknown> tag. The <wellknown> tag contains attributes that tell the Remoting technology about the namespace, class, and assembly of the remotable object. The objectUri is how we uniquely refer to the service by name (.rem or .soap is used by convention).
There are two kinds of service modes: Singleton and SingleCall. With SingleCall, every server request will be handled by a different object. This means that the call to assign an event handler would return one server instance of the remotable object and subsequent calls to the server would return new instances, resulting in subsequent server objects not having client event handlers to call back to. The SingleCall mode won't work for us; we want the Singleton mode, which means that every server call is handled by the same instance of the remotable object. The end result is that the object to which the client's event handler was assigned will be the same object that raises the server events.
Next, we have to define the assembly and class, General.Shared, described in the <wellknown> section of the app.config file.