Remoting uses a multilayered architecture that allows developers to "snap-in" custom modules for different types of formatting, different communication channels, or additional services (such as logging or encryption). In many cases, this layered design means that you can switch the entire communication protocol of an application simply by modifying a single setting in the XML configuration file. This is a unique advantage of Remoting, and none of the lower-level approaches considered later in this book can provide anything like it.
The Remoting model shown in Figure 2-2 simplifies a few details and collapses the Remoting infrastructure down to a single layer. In reality, a Remoting call is routed through several channel sinks in a set order, each of which performs an important task. By default, the first channel sink is the formatter, which encodes the message in SOAP or binary representation. The final channel sink is always the transport channel, which routes the message using the appropriate transport protocol. This model is diagrammed in Figure 3-11.
The examples so far have used TCP communication and binary encoding. This is generally the most performance-optimal form of communication, although it can run into trouble in an Internet scenario, particularly when a firewall is involved. Firewalls are often configured to reject incoming TCP connections.
To switch to a more Internet-friendly HTTP channel, simply replace this line in the configuration file:
<channel ref="tcp"/>
with this one:
<channel ref="http"/>
You'll need to perform this change for both the client and server. An error will occur if the two parts of the system try to communicate using different formatters or protocols.
As with the TCP channel, there are three versions of the HTTP channel that you can use: http client, http server, and bidirectional http. These are all aliases to specific channel classes that are defined in your computer's machine.config file:
<channels> <channel id="http" type="System.Runtime.Remoting.Channels.Http.HttpChannel, System.Runtime.Remoting, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> <channel id="http client" type="System.Runtime.Remoting.Channels.Http.HttpClientChannel, System.Runtime.Remoting, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> <channel id="http server" type="System.Runtime.Remoting.Channels.Http.HttpServerChannel, System.Runtime.Remoting, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> <channel id="tcp" type="System.Runtime.Remoting.Channels.Tcp.TcpChannel, System.Runtime.Remoting, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> <channel id="tcp client" type="System.Runtime.Remoting.Channels.Tcp.TcpClientChannel, System.Runtime.Remoting, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> <channel id="tcp server" type="System.Runtime.Remoting.Channels.Tcp.TcpServerChannel, System.Runtime.Remoting, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> </channels>
You'll also need to change the URL used to request the object over the HTTP channel, by replacing the "tcp" prefix with "http," as shown here:
<wellknown url="http://localhost:8000/RemoteObject" type="RemoteLibrary.RemoteObject, RemoteLibrary"/>
The port number can be used in the same manner. Any available port can be used for TCP or HTTP communication.
By default, the TCP channel uses binary encoding. The HTTP channel, on the other hand, always defaults to SOAP (XML-based text) communication. These defaults can be changed. For example, you could use binary communication over an HTTP channel to allow .NET programs to communicate efficiently over the Internet and through a firewall. This would reduce the size of the message because binary encoding is much more compact than XML encoding, but it wouldn't sacrifice any of the connectivity. Similarly, you could use SOAP over a TCP channel. This is an unlikely choice, but might have some uses if you were creating a Remoting client using a non-.NET language such as Java.
In order to specify a formatter other than the default, you must add a <serverProviders> tag inside the <channel> tag, and a <formatter> tag inside the <serverProviders> tag. You then set the ref attribute of the <formatter> tag to "soap" or "binary." You must repeat this on both the client and server configuration file.
Here's a sample configuration file that combines HTTP transport with .NET's proprietary binary encoding:
<configuration> <system.runtime.remoting> <application name="Server"> <service> <wellknown mode="Singleton" type="RemoteLibrary.RemoteObject, RemoteLibrary" objectUri="RemoteObject" /> </service> <channels> <channel ref="http server" port="8080" > <serverProviders> <formatter ref="binary" > </serverProviders> </channel> </channels> </application> </system.runtime.remoting>
The last topic this chapter considers is dynamic registration with Remoting. In the examples presented so far, all the Remoting settings have been centralized in a configuration file. The server defines the channel type and registers an available object in one step, using the RemotingConfiguration.Configure() method.
However, it's also possible to perform these tasks exclusively through .NET code. The disadvantage of this approach is that it intermingles configuration details with the application code, and it may force you to recompile your code when you change the distribution of your system. However, dynamic registration also has a number of advantages:
It allows you to read and apply configuration information from another source, such as a database or a web service.
It allows you to change the objects that are available or the channels that are used during the lifetime of your application.
It allows you to make a conditional decision about what channels to use and which objects to expose.
It allows you to use interface-based programming, as shown in the next chapter.
Dynamic registration is easy. All you need to do is create at least one instance of one of the HTTP or TCP channel classes, register it using the shared ChannelServices.RegisterChannel() method, and register an object type that you want to make available using the shared RemotingConfiguration. RegisterWellKnownServiceType() method. This method also allows you to specify the activation type of the object.
RemotingConfiguration.ApplicationName = "Server" ' Define the channel. Dim Channel As New TcpServerChannel(8000) ' Register the channel. ChannelServices.RegisterChannel(Channel) ' Register the remote object type. RemotingConfiguration.RegisterWellKnownServiceType( _ GetType(RemoteLibrary.RemoteObject), _ "RemoteLibrary.RemoteObject", _ WellKnownObjectMode.Singleton)
The process on the client is much the same. The only difference is that you use the RegisterWellKnownServiceType() method, instead of the RegisterWellKnownClientType() method.
RemotingConfiguration.RegisterWellKnownClientType( _ GetType(RemoteLibrary.RemoteObject), _ "tcp://localhost:8000/RemoteObject ")