You can implement very advanced solutions with .NET Remoting. XML Web Services is an example of a general .NET Remoting solution. If you are writing distributed applications you can often use XML Web Services as an easy form of Remoting. That said, let's talk about .NET Remoting.
If you need to interact with objects that reside on the server, if are working with objects that may be too large to serialize, or if you need to handle server events then you will need to use .NET Remoting. In this three part series we will look at the many code elements you will need to use to implement a .NET Remoting solution that includes server events. (A complete code listing will be available for downloading at the end of the series.)
Implementing the Chat Server
A great way to demonstrate a cool technology is to write a fun application. Everyone in my house uses a messenger service, so I wondered how hard it would be to use .NET Remoting to create a simplified chat program. In this three part series you will implement a chat program, starting with the server.
You should note that the .NET framework ships with a chat sample. The code in this three part series was developed independently by me, but you could look at the chat sample from Microsoft for some additional ideas or to compare styles.
For a chat program you need a server and clients that register with that server. When one client sends a message, the other clients need to be notified; this implies server events and shared code. In this article I will focus on the server and the elements of the server. Here is an overview of the topics I will demonstrate in this part:
- An executable server application
- Channel configuration using an App.config file (which describes the service and provides port information)
- A custom delegate that is in a separate assembly to permit sharing between client and server
- A remotable MarshalByRefObject
- And, the OneWayAttribute
Defining the Executable Server Code
The server can be simple or complex, a console application or Windows service; you just need something running on the server that can service requests from clients. For this chat program, you will use a console application project. Console applications are easy to create and will let us stay focused on Remoting.
Because you will be configuring the remote server using the App.config file, the code for the remote server is very easy. Listing 1 contains the entire listing for the server, and listing 2 contains the App.config file with the configuration information. I will talk about the configuration file after listing 2.
Listing 1: All the code you need for a running server.
Option Strict On Option Explicit On Imports System Imports System.Diagnostics Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Http Module Module1 Sub Main() Console.WriteLine("Reading configuration information...") RemotingConfiguration.Configure("Server.exe.config") Console.WriteLine("press [enter] to quit") Console.ReadLine() End Sub End Module
Listing 2: The App.config server configuration XML.
<?xml version="1.0" encoding="utf—8" ?> <configuration> <system.runtime.remoting> <application> <channels> <channel ref="http" port="6007"> <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="SharedCode.Chatter, SharedCode" objectUri="Chatter.soap" mode="Singleton" /> </service> </application> </system.runtime.remoting> </configuration>
You can programmatically configure your server or use the App.config file to configure the server. By using an external XML file you can change elements like the port number without recompiling and redistributing the server application.
Tip: You can copy and paste the above XML into the Toolbox—referred to as a code snippet&mdashpand drag and drop the XML each time you need to configure a server. Generally, you will only need to update the service section and the port.
The remote configuration information is defined between the <system.runtime.remoting> </system.runtime.remoting> tags—the namespace for remoting information. Nested in the namespace is the channel configuration and service configuration. Let me divide the discussion between these two blocks of XML.
Configuring the Channel
I think of a channel like a pipeline for moving data between clients and servers. Channels are configured inside of the <channels></channels> tag. The outer tag supports configuring multiple channels. You only need one channel for the sample.
To configure the channel you need to supply arguments for the ref and port attributes. Ref describes the name of channel, which can be tcp or http. If you provide a Ref attribute then the type attribute is not needed. The port can be any port value, but you should avoid common ports like 20, 21 (FTP), 23 (Telnet), 25 (SMTP), 110, and 80. You can check online for a list of reserved port numbers, but ports above 1024 are probably safe, and you can use numbers as high as 65520.
The inner <serverProvider> and <clientProviders> tags are required in .NET 1.1. If you forget these elements then you will get an exception when you try to run your remote server. The formatter describes the level of serialization for each of the soap and binary formatters, and the provider tag is used to describe elements that participate in channeling the data between client and server.
The best resource for .NET Remoting I have found is Ingo Rammer's website www.ingorammer.com. For more information about providers and formatters pick up a copy of Ingo's book Advanced .NET Remoting from Apress.
Configuring the Service
The service tags indicate the class and assembly that represent your remote service. Using the App.config file, the service configuration information is described with the <wellknown> tag (see listing 2). The type is a string that contains the namespace and class—SharedCode.Chatter—delimited by a comma and the assembly name. When you see a string like this think Reflection and dynamic assembly loading.
The objectUri attribute—"Chatter.soap"—uses a .soap or .rem by convention and is a uniform resource indicator that uniquely identifiers our service by name. The URI will be part of the URL when you connect to your service from the client.
Finally, you specify the mode attribute. The mode can be SingleCall or Singleton. The mode attribute is a subtle but critical part of the chat server. If you used the SingleCall mode then every client would get a different instance of the remote server object, which means the call to connect your event handler would get one instance of the server and subsequent calls to send messages would go to additional instances of the server. For the example, data will be shared between clients, so you need to use the Singleton mode. As a result it is possible that data from one client can be shared by other clients, which is precisely what is wanted in a chat program—one user writes some text and other users can see it.