.NET Remoting Architectural Assessment

 

Pat Martin
Microsoft Corporation

May 2003

Applies to:
    Microsoft® .NET Framework
    Microsoft® .NET Remoting

Summary: This article is intended for anyone who is considering using .NET Remoting as part of a distributed multitier application design. It describes the capabilities of the technology from the perspective of a developer who has both benefited from the convenient RPC mechanism it provides and suffered a little from its shortcomings. It is assumed that the reader is familiar with .NET Remoting, at least in conceptual terms if not practical usage.

The Product Features section is useful for someone who wants to design something (possibly) using Remoting. The Best Practices section is useful for anyone who wants to build something using Remoting. The Remoting and Web Services section is an attempt to dispel some of the confusion that exists surrounding when to use which technology. The Executive Summary is an attempt to condense the content into a single page.

Contents

Overview
Product Features
Best Practices for Using Remoting
Remoting and ASP.NET Web Services
Executive Summary
Additional Resources

Overview

.NET Remoting has been described as the chosen technology for managing RPC between application domains. Application domains are the unit of isolation for the common language runtime. They are created and run inside a process. This is in contrast to Inter Process Communication between CLR and non-CLR managed processes (Interop). This latter type of RPC communication, particularly over the "Web," is seen to be the domain of (to use the generic term) Web services. Unfortunately, this seemingly clear distinction has been clouded by the possibility of hosting .Net Remoting servers under IIS, as discussed in the article An Introduction to Microsoft .NET Remoting Framework:

".NET Remoting objects can be exposed as a Web Service by hosting them in IIS..."

There may be a degree of confusion on the part of some Microsoft customers with respect to .NET Remoting. Some common questions I have been asked are "When should we use Remoting?" "When will Remoting support NTLM?" "How can I secure Remoted conversations?" "What about COM+?" and "How does Remoting manage transactions?"

In addition to addressing some of these questions, this article describes some best practices for using .NET Remoting and presents an overview of the currently provided functionality. The Executive Summary suggests possible future directions for the technology, particularly in relation to Web services and the emerging Global XML Web Services Architecture (GXA) specifications.

The Product Features section is largely drawn from information presented at TechED N.Z. 2002. This presentation highlighted the different ways in which Remoting can be used in a distributed solution and helped clarify the advantages of Remoting together with hinting at its shortfalls.

The Best Practices section is drawn from personal experience in using Remoting in a multitier .NET application. During the development a number of simple best practices evolved, which are listed here.

Some sections include material based on informal conversations with people within Microsoft who have a sound understanding of the technology and where it is heading. The information presented is in no way representative of future product delivery plans or schedules.

Product Features

This section describes the functionality and product features offered by .NET Remoting.

Client/Server Communication

.NET Remoting provides a useful way of managing both synchronous and asynchronous RPC conversations across application domains. The remote object code either runs at the server, as is the case with both server-activated and client-activated objects; or, at the client, where the remote object has been serialized across the client/server connection. In either case, once initialization and configuration has been achieved, which is not difficult, the programmatic idiom is a very simple one, for which coding requirements are minimal. The use of a remote object (proxied in the case of Marshal by Reference) is transparent to the programmer. Earlier Windows RPC mechanisms, for example, required intimate type and marshalling knowledge using IDL tools and exposed the management of RPC client and server stubs to the developer. Remoting does a much cleaner job of providing RPC for .NET, and the use of readily and commonly understood .NET data types removes the very real threat of type mismatches that existed with earlier RPC mechanisms.

By default, Remoting can be configured to communicate using either HTTP or TCP protocols with message formats being either XML encoded SOAP or native binary. Custom protocols (channels) or message formats (formatters) can be built by the developer and used by the Remoting framework if required. Ports can be selected by both server and client components, as can the choice of communication protocols. A big win here is that getting this basic communication up and running is very easy to achieve.

There are, however, choices to be made in the type of communication selected in terms of state management. The remainder of this section describes the different choices for communication offered by Remoting, together with their associated design implications.

Server-Activated Objects

Server-activated objects are objects whose lifetimes are controlled by the server. They are created by the server only as needed when the client invokes the first method on the object. Server-activated objects only support default constructors. To use a remoted object with parameterized constructors you can use client activation or dynamic publication (see below). Server-activated objects are also referred to as well-known object types, as their location (URL) is published and known in advance. There are two activation modes for server-activated objects, Singleton and SingleCall, both of which are described below. To create an instance of a server-activated type, you can either configure your application programmatically or via static configuration. Server activation configuration is pretty straightforward. For example, the following code snippet

<service>
  <wellknown mode="SingleCall" type="Hello.HelloService, Hello" 
                   objectUri="HelloService.soap" />
</service>

depicts a server-activated (wellknown) type with activation mode set to SingleCall. Refer to .Net Framework Developer's Guide 'Server-Side Registration' on MSDN for details on configuring server-activated Remoting.

Singleton

These objects follow the classic Singleton design pattern in that there is never more than one instance in memory at any point in time and all clients are served by that instance. Note, however, that these types do have a default lifetime associated with them (see the Object Lifetime Management section below). This means that clients will not necessarily always receive a reference to the same instance of the remotable class. This latter fact has interesting implications for state management and is where this Remoting pattern diverges from the classic Singleton model (which requires object identity to be the same). There are two ways of solving this problem if the classic Singleton state management pattern is required by your design. One option is to override the default object leasing behavior such that the object is kept in memory as long as the host application domain is running. The following code snippet illustrates how this can be achieved:

public class MyClass : MarshalByRefObject
{
  public override Object InitializeLifetimeService()
  {
      return null;
  }
}

As mentioned, this mechanism will lock the object in memory and prevent the object being recycled, but only for as long as the host application is running. For IIS hosting, should IIS or the IIS process hosting the Remoting session be recycled (which can occur for a number of reasons), then the object will be destroyed.

To fully depend on thread-safe Singleton state data with Remoting we need to do three things:

  1. Override the leasing mechanism such that the lease is infinite, as shown above.
  2. Host the remoted server inside a process of our own devising—for example, a System Service over which we have full lifetime control. While this process can also be recycled, this would be a more explicit action and would be less likely to go unnoticed than an IIS worker process being recycled. Refer to the Product Features section below for more detail on this mechanism.
  3. Develop the remoted server to be thread-safe as multiple threads will be used to fulfill concurrent requests from clients. This implies, for example, managing concurrent writes to shared resources and generally taking care of shared access to static memory.

SingleCall

SingleCall remote server types always have one instance per client request. The next method invocation will be serviced by a different instance. In design terms the functionality provided by SingleCall types is very straightforward. No state management is provided using this mechanism, which is bad if you want some but ideal if you don't. Maybe you only care about load balancing and scalability rather than state, in which case this mode is an ideal choice as you get exactly one instance per request. The developer can, if he wishes, provide his own state management for SingleCall objects, but this state data would not reside within the objects themselves as a new object identity is instantiated with each new method call.

Dynamic Publication

The final type of server activation method to be considered is dynamic publication. This is a type of server activation that offers more control over object construction by providing a programmatic publication mechanism. This allows for publishing a particular object at a particular URL, optionally with a parameterized constructor. Architecturally this should be viewed as a subtle variant of a server-activated Singleton type. Information on dynamic publication can be found in the .NET Framework Developer's Guide.

Client-Activated Objects

Client-activated objects are created on the server when the client calls new or Activator.CreateInstance(). The client itself, using the lifetime leasing system, can participate in the lifetime of these instances. This activation mechanism offers the greatest scope in terms of design flexibility. With client activation an activation request is sent to the server when a client attempts to activate the object. This mechanism allows for both parameterized constructors and unique per-client connection state management. With client activation each client is served by its own specific server instance, which facilitates the saving of object state over multiple calls. One needs to be prudent in the use of these objects, however, it is easy to forget that the conversation is distributed and that the object is actually not only out of process, but in the case of a multitier app probably out of machine as well—setting a property over the Internet wouldn't be too prudent. Chunky rather than chatty interfaces should be the rule here: We may need to trade off high cohesion / loose coupling for performance. To create an instance of a client-activated type, you can either configure your application programmatically or use static configuration. Client activation configuration at the server is pretty straightforward. For example, the following code snippet

<service>
  <activated type="Hello.HelloService, Hello" 
             objectUri="HelloService.soap" />
</service>

depicts a ClientActivated type. Note that we no longer need an URL, as for client-activated types the type alone is sufficient for activation. Also, the wellknown tag has been replaced by the activated tag. Refer to .Net Framework Developer's Guide 'Registering Remote Objects Using Configuration Files' on MSDN for details on configuring client-activated Remoting.

Extensibility

During the processing of a remote method call .NET Remoting sends formatted "messages" from client to server along Remoting "channels." Both the message formats and the channels themselves are fully extensible and customizable. Either the default channel or formatter can be replaced by custom-built components. While the message is in transit it can be intercepted at various "sink points" and altered, allowing for custom processing of the message (for example, message encryption). The customization mechanism is described in the .NET Framework Developer's Guide (Sinks and Sink Chains) and a number of custom channels and formatters are already appearing on the Internet (for example, there is a Named Pipe channel implementation). This extensibility will not be interesting for the most part, as the default formatters and channels provided with the technology are already aimed at the broadest reach (that is, TCP and HTTP specifically together with SOAP message formatters). It is worth remembering that this capability exists, however, during the initial design phase where various solution options are being considered.

Exception Propagation

.NET Remoting fully supports exception propagation across Remoting boundaries. This is a major improvement over the use of error codes—for example, as is the case with DCOM.

With Remoting exceptions it is a good idea to mark your exception class as serializable and to implement the ISerializable interface. This allows the exception to be correctly serialized across the Remoting boundary and also allows for custom data to be added to the exception during construction. A good practice is to define your own exception class for exceptions that need to be remoted and to be consistent in their use. Ensure that all exceptions are caught and correctly propagated in this way, and do not allow unhandled exceptions across the Remoting boundary.

Object Lifetime Management

.NET Remoting provides a rich mechanism for managing the lifetime of remote objects. If our server object does not hold any state (for example, as is the case with a SingleCall object), then we don't need to be concerned in any way with this process. We simply allow the Remoting infrastructure to do what it does and our objects will be garbage collected when needed. If we are holding state, either server-activated Singletons or client-activated objects, we may need to participate in the lifetime management process: object leasing. We have already seen one simple (and useful) way of minimal involvement, which is to override the InitializeLifetimeService method, as shown in the description of Singletons above. This lets us keep our objects around as long as the processes hosting them are running. How does this object lifetime process work?

The mechanism provided by Remoting for object management is based on the principle of leasing: You never own an object, you just borrow it, and as long as you keep up the payments you get to continue using it. This process is further described below. First, though, a few words on how the COM world deals with object cleanup. DCOM uses a combination of pinging and reference counting methods to determine whether objects are still running. This is both error prone and network-bandwidth intensive. The whole principle of reference counting was at worst never fully understood and at best fragile. There were (and still are) a number of simple rules that had to be applied for reference counting to work. The IUnknown interface for a COM object includes AddRef and Release methods, which need to be called by the developer at the appropriate times. Sometimes programmers got this wrong, resulting in objects not being removed and in associated memory leaks.

In contrast, the Remoting leased-based lifetime management system uses a combination of leases, sponsors, and a lease manager. Each application domain contains a Lease Manager, which holds references to a lease object for each Singleton or client-activated object within its domain. Each lease can have zero or more associated sponsors that are capable of renewing the lease when the Lease Manager determines that the lease has expired. This lease functionality is provided by the Remoting infrastructure through the ILease interface and is acquired through the call to InitializeLifetimeService, which we have already seen above. The ILease interface defines a number of properties that are used to manage an objects lifetime:

  • InitialLeaseTime. Determines how long the lease is initially valid.
  • RenewOnCallTime. After each method call the lease is renewed for this time unit.
  • SponsorshipTimeout. How long Remoting will wait after sponsor lease expiry notification.
  • CurrentLeaseTime. How long until the lease expires (read only).

When a lease expires the Lease Manager will notify any lease sponsors asking if they wish to renew the lease. If none do, then the associated object references will be released.

Sponsors are objects that can renew leases for remote objects. To become a sponsor your class must derive from MarshalByRefObject and implement the ISponsor interface. A lease can have many sponsors. A sponsor may participate in many leases.

The mechanics of this lease management in terms of programming against these interfaces is described in the .NET Framework Developer's Guide documentation on Lifetime Leases and therefore is not reproduced here. What is worth noting, however, is that this rich mechanism exists for managing the lifetime of stateful, remoted objects. As mentioned, you can either ignore it totally, use it to hold an object in memory while its process container is running, or become fully involved in the leasing mechanism.

Remote Server Hosting

There are a number of choices for hosting a .NET Remoted server, which fit into two broad categories, described next.

IIS Hosting under ASP.NET

Available as standard functionality is the ability to host a remoted server-side object under IIS. This brings with it many advantages, including support for security and scalability.

To host your object under IIS:

  1. Develop your remote class and inherit from MarshalByRefObject (or declare the class as serializable).
  2. Create a virtual Web application using the IIS Administration Manager.
  3. Place the assembly containing your class in the bin subfolder of your virtual Web application.
  4. Create a web.config file to hold your Remoting server configuration definition and place it in the virtual root of your Web application.

That's it. There are, however, some constraints that you should be aware of:

  • You cannot specify an application name for IIS hosting—it is the virtual app name.
  • You must use the httpchannel.
  • If your Remoting client is also a Web app, you must call RemotingConfiguration.Configure during startup—usually this will be in the Application_Start method of your Global.asax file. You cannot use the <client> tag to configure your client Web application automatically.
  • Do not specify a port, as IIS takes care of port allocation. You can still use IIS admin to specify a port for the virtual application if required.

The Remoting application domain will be hosted inside the Aspnet_wp.exe worker process and by default will assume the identity of that process.

Note There is currently a bug in ASP.NET that requires the process identity for the Aspnet_wp.exe worker process be set to either "system" or a local machine account; the default setting, "machine" in machine.config is not correctly configured, causing ASP.NET applications to fail with an error 500, "internal server error" when hosted under IIS on a domain controller. Arguably, the bug is the lack of documentation describing how to appropriately configure the machine account.

Hosting under IIS brings a number of functional advantages; scaling, threading, auditing, authentication, authorization, and secure communication features are available by default. The ASP.NET worker process is always running and is subject to fine-tuning in terms of thread and fault management using the <processModel> element in machine.config. In short, the advantages and functionality available to IIS are open to the remoted server.

There are a number of disadvantages, however: You must use HTTP, which is slower than TCP. Additionally, IIS may cycle the ASP.NET worker process which destroys any Singleton state; this may or not be an issue depending on your design needs—the next call from a client will start the Singleton up again. It is possible to configure IIS not to recycle a worker process, however this capability is limited, particularly in IIS 5, and may have some far-reaching consequences. The bottom line here, though, is that if security of your remoted server is a requirement, then IIS hosting is definitely the way to go. Performance is only an issue when it is actually perceived to be a problem during system testing / usage, and there is always the option to solve the problem in hardware.

Authentication Considerations under IIS

Authentication options

.NET Remoting does not have its own security model: Authentication and authorization are performed by the channel and host process, in this case IIS. Windows authentication is supported for Remoting and configured within web.config by setting <authentication mode="Windows"/>. Neither Forms nor Passport authentication are supported, as Remoting clients cannot access cookies, nor can they be redirected to a logon page—remote servers are designed for non-interactive use.

Passing credentials to remote objects

If the remote object is IIS hosted (within the ASP.NET worker process) and is configured for Windows authentication, you must specify the credentials to be used using the credentials property of the channel. Failure to do this will result in the remote call being made without any credentials being passed. This omission is a common cause of http access denied responses. To use the credentials of the process hosting the remote object proxy (the Remoting client process) set the credentials property of the channel to the DefaultCredentials maintained by the process credential cache. This can either be done declaratively using the channel element (for a Web client)—that is, <channel ref="http" useDefaultCredentials="true"/> or programmatically using the following code:

IDictionary channelProperties;
channelProperties = ChannelServices.GetChannelSinkProperties(proxy);
channelProperties["credentials"] = CredentialCache.DefaultCredentials;

To pass specific credentials along with a remote object call, disable the use of default credentials—that is, set <channel ref="http" useDefaultCredentials="false"/> and use the following code:

IDictionary channelProperties =
ChannelServices.GetChannelSinkProperties(proxy);
NetworkCredential credentials;
credentials = new NetworkCredential("username", "password", "domain");
ObjRef objectReference = RemotingServices.Marshal(proxy);
Uri objectUri = new Uri(objectReference.URI);
CredentialCache credCache = new CredentialCache();
// Substitute "authenticationType" with "Negotiate", "Basic", "Digest",
// "Kerberos" or "NTLM"
credCache.Add(objectUri, "authenticationType", credentials);
channelProperties["credentials"] = credCache;
channelProperties["preauthenticate"] = true;

Note Setting the preauthenticate property to true (as above) causes a WWW-Authenticate header to be passed with the initial request. This stops the Web server denying access on the original request and performing authentication on the subsequent request.

Hosting Outside of IIS

There are a number of options for remote hosting outside of IIS. These are listed next.

Hosting inside a console application

The developer can write a console application that starts up the Remoting infrastructure and then just "hangs around." The only reason it needs to hang around is because it contains the application domain, which is hosting the remoted calls. Writing one is extremely simple: just call the RemotingConfiguration.Configure method passing it the name of your Remote Host configuration file and then just wait for some event to cause the process to be terminated—for example, a key press or a specific message received.

This approach has the advantage of not requiring IIS on the middle tier, but is not production ready; it is useful for demos and development and testing. This is not to say that it is of no use whatsoever, just that it is very limited in scope.

Hosting inside a GUI application

The developer can also write a Windows GUI application that starts up the Remoting infrastructure and then just "hangs around." Again , the only reason it needs to continue executing is that it contains the application domain, which is hosting the remoted calls. The development approach here is as for the console application: The Remoting host can either start directly or start based on user interaction. Again, this approach has the advantage of not requiring IIS on the middle tier and is useful for demos and testing. A variation on this would be a peer-to-peer (logically) winforms app—for example, a chat type application. Again, this would be of limited use.

Hosting inside a system service

This possibility is more interesting, as the functionality is not so much provided by the Remoting infrastructure as by the notion of a system service itself. System services can be configured to start when the machine is started and to stay around until you tell them to go away, which is ideal for remote hosting. Note that IIS applications can also be configured to behave similarly by setting "High Isolation Mode" for the Virtual Application. This has a number of implications, however, which are not discussed in this article. Customers have asked some hard questions about this mechanism, which calls its usefulness into question. First, some advantages: We have already mentioned the benefits of a service itself. Furthermore, we do have full control over the activation of the host process—for example, we might elect to use dynamic publication or client activation. We don't need IIS, since we can get our user profile loaded and we get good performance with binary-encoded messages over TCP.

The disadvantages weigh pretty heavily, however. To start with, you need to build your own authentication and authorization mechanisms, should you require them. The article .NET Remoting Security Solution, Part 1: Microsoft.Samples.Security.SSPI Assembly provides a very full and detailed description of a security solution for .NET Remoting that " ... implements a managed wrapper around SSPI, providing the core functionality needed to authenticate a client and server as well as sign and encrypt messages sent between the two." This is definitely a great asset and provides a "façade" for adding this functionality in a useful way. The problem is that it is not a supported product but an "informal" attempt to supply missing functionality. Also, it's a little intimidating for a developer, as the solution relies on the extensible nature of formatters and channels. All this needs to be hidden and the functionality surfaced by just adding an entry to the Remoting configuration, which depicts, for example, the use of Windows NT Challenge/Response (NTLM). It is likely, however, that such security mechanisms will be incorporated into a future version of .NET Remoting.

A System Service would also need to be scalable and reentrant to be useful as a Remoting server, as these features will be required in a multitiered distributed application. Without IIS, for example, the hosting service would have to manage its own auditing and authorization, both of which come standard with IIS.

For these reasons the System Service hosting mechanism is of limited use, perhaps in a constrained environment where messages are queued to a single exchange, security is not an issue, or IPSec is available over TCP.

Enterprise Services Management

In order for a remoted component to participate in a COM+ environment (and run in a COM+ context) it needs to inherit from ServicedComponent. ServicedComponent, along with other functionality provided in the System.EnterpriseServices namespace, allows a CLR component to specify a number of COM+ attributes, such as those denoting transaction requirements and server process execution attributes. Together with strong naming and the use of the regsvcs command, a remote component can be part of the general COM+ environment.

Given that a remoted component needs to inherit from MarshalByRefObject and a COM+ component needs to inherit from ServicedComponent (and we don't have multiple inheritance in .NET managed code), how is this going to be achieved? Luckily, ServicedComponent derives from ContextBoundObject, which derives from our required MarshalByRefObject. Building COM+ integration directly on top of Remoting is certainly possible, then, and does offer the obvious benefits provided by Enterprise Services—for example, object pooling, distributed transaction support, and role-based security. Missing, though, are good examples of how to do this and how such an approach stacks up architecturally with respect to future proofing.

It would be reasonable to expect that the COM+ context infrastructure and the Remoting context infrastructure will come closer together over time. Precisely when or how this will happen is unclear at this stage.

Best Practices for Using Remoting

As always, developing and testing distributed components is not without associated project costs and developer headaches. The following guidelines represent information learned the hard way.

Getting Started

The article Basic Remoting Task List provides a good starting point for a checklist of the tasks that need to be carried out when setting up Remoting for the first time. It would be a good idea to use the above article as reference material when going through the process. The following is a very brief summary of the steps you will need to follow:

Host tasks

  • Design the service choosing app domain, activation model, channel, port and publication.
  • Implement the Remoting host app domain (for example, IIS / System Service).
  • Configure the host activation, channel, and protocol settings. It is recommended that you use a configuration file, which you load by calling RemotingConfiguration.Configure.
  • Publish your interface for client use (refer to the section below, 'Interface Publication Choices' for details).

Client tasks

  • Design client choosing app domain and activation mode.
  • Consider whether you need to register a channel and port.
  • Obtain the remote types metadata.
  • Implement the client application domain.
  • Configure the client activation mode and other type information, such as the application name, channel, and object URI. It is recommended that you use a configuration file, which you load by calling RemotingConfiguration.Configure.

Formatting Choices

As standard, Remoting can be configured to use either a SOAP or binary formatter over an HTTP channel or a binary formatter over a TCP channel. This configuration is typically effected by making the appropriate entries in the client configuration file and calling the static RemotingConfiguration.Configure method.

For example, to configure a Remoting connection to use a binary formatter over HTTP, a config entry would be made as follows:

<channel ref="http" useDefaultCredentials="true" port="0">
  <clientProviders>
    <formatter ref="binary"/>
  </clientProviders>
</channel>

Here the "channel ref" identifies the HTTP protocol and the "formatter ref" identifies the message format to be sent over the channel, in this case binary.

Unfortunately, using a binary formatter with an HTTP channel during development has the undesirable side effect of masking server-side errors. For example, a general server error or an access violation will both be misreported to the client. This is due to the fact that when using a binary formatter the client-side Remoting component expects a message to be returned in binary format. Plain-text error results are not interpreted correctly and are reported as follows:

An unhandled exception of type 'System.Runtime.Serialization. SerializationException' occurred in mscorlib.dll. Additional information: BinaryFormatter Version incompatibility. Expected Version 1.0. Received Version 1008738336.1684104552.

This error is almost always not due to version incompatibility, but due to an inability to parse textual error responses at the client. While it is expected that this protocol deficiency will be addressed in future versions of the product, it is strongly suggested that the SOAP formatter be used during the development cycle. Once proven, the formatter may be switched to binary for performance reasons, although this should only be done where performance benefits are both substantial and required.

Interface Publication Choices

Once the Remoting server has been designed and built, the interface it provides has to be published for client usage in order to resolve compile time references and allow for dynamic proxy object creation. There are a number of ways for doing this that are worth repeating here. First, however, a few reminders:

  • Static fields and methods are never remoted, .NET Remoting always deals with instance members of some form.
  • Private methods / types cannot be remoted.
  • MarshalByRef types are remoted by reference, serializable types are value copied and code executes in the client process.
  • Object virtual methods Equals, GetHashCode, MemberwiseClone, etc., execute locally.

With these design caveats the options for publishing an interface exported by a Remoting server are as follows:

  • Provide the server-side assembly to clients for use at compile time—this is not recommended and not necessary as merely the interface is required, not the implementation.
  • For SOAP / HTTP clients (here our Remoting server is functioning as a Web service, although this unfortunately serves to confuse rather than clarify) the Remoting server can provide a Web Services Description Language (WSDL) file that describes the server object and its methods. The SOAPSUDS utility that ships with the .NET Framework SDK can be used to generate these WSDL files to serve as metadata. This approach is really more appropriate for a Web service (in the strict asmx sense) than for Remoting, as the WSDL for a Remoting interface is not strictly compatible with that of a Web service. The*.NET Framework Tools* documentation on the Soapsuds Tool describes the Soapsuds utility in some detail.
  • Declare an interface in a separate library and deploy that library with the client. Publish a server class that implements the interface and the client will be able to use it by acquiring a proxy to the interface it implements. This is a very clean design choice, as it is specifically the interface that is of interest. This approach can only be used for server-activated objects (see the Product Features section) as one cannot create an instance of an interface.
  • Use SOAPSUDS to build a stand-in class for the client to serve as metadata. You can run SOAPSUDS against your Remoting server assembly and generate either an output assembly, which is directly usable as metadata, or a source file, which can be included in your application directly. This mechanism is useful for building multitier applications in which objects in one tier want to access remote objects in another tier. This approach is interesting and is the one used on the multitier application referenced in the introduction section above.

Assuming we have a command window open in the following folder:

$FRAMEWORKSDK\Samples\Technologies\Remoting\Basic\RemotingHello\Service

we can write: soapsuds -id:. -types:Hello.HelloService,Hello -oa:HelloInterface.dll

This creates an output assembly HelloInterface.dll, which contains metadata only based on the Remoting server Hello.HelloService found in the Hello assembly in the current directory. This assembly can then be used directly by the client. The Remoting server location is derived based on configuration data supplied at runtime, as per standard Remoting configuration. The MSIL produced for the client assembly

ldfld object [System.Runtime.Remoting]System.Runtime.Remoting.Services.RemotingClientProxy::_tp

shows clearly that we are not using the Remoting server implementation but the proxy class built using the metadata produced by SOAPSUDS.

SOAPSUDS is not guaranteed/supported to work with binary formatting—it embeds/maps some SOAP specific "stuff" in the output assembly metadata.

The recommendation is to keep the Remoting interface as simple as possible. Use "chunky" vs. "chatty" interfaces—that is, try to limit the number of remote calls in your design; potentially this may involve passing redundant parameters in some situations. Keep the remoted interface in a separate class from the actual implementation. This allows for a "façade" type pattern where the Remoting layer can be easily substituted for another technology if desired.

Managing Errors

This section describes a number of possible error scenarios that could be encountered during the development (and usage) of a Remoting solution. In all cases it is worth remembering that standard instrumentation and monitoring practices still apply. Event logs are still a valuable source of information, as are tools such as Network Monitor. Network Monitor in particular can be used to view client / server Remoting conversations in fine detail. Middle-tier Remoting servers can still be debugged using the standard debugging tools provided with Visual Studio .NET. For example, where a Remoting server is hosted by IIS, breakpoints may be set (providing source is available) by attaching the debug session to the ASP.NET worker process (Visual Studio .Net | Debug | Processes | Attach). Remoting does, however, bring with it its own unique set of errors, some of which are listed below. Note that all errors have been reproduced using versions of the Basic Remoting Hello Sample provided with the .NET Framework SDK. Server and client have been running on a single machine. The symptoms are the same over network links but take considerably longer to fail based on http / TCP timeout settings.

Missing MarshalByRef

For Remoting by reference to work for a given class, that class has to do one thing and one thing only, and that is to inherit from MarshalByRefObject. Suppose the developer forgot to do this. We will get an exception of type System.Runtime.Remoting.RemotingException stating that we have a 'Missing MarshalByReference'.

Whether this RemotingException is caught and handled correctly will be programmer dependent. (Recall that this developer forgot the one thing he had to remember.)

The resolution is to remember to inherit from MarshalByRefObject!

Incorrect server endpoint for well-known server activation

For server activation (see the Product Features section) the Remoting server declares the endpoint at which it is listening. This endpoint typically includes an object URI (the well-known name of the remote object), a protocol, and a port number. All of these can, of course, be configured incorrectly.

Bad URI

The URI for the Basic Remoting Hello Sample supplied by the service is HelloService.soap, as specified within its associated web.config file:

<configuration>
  <system.runtime.remoting>
    <application>
      <service>
        <wellknown mode="SingleCall" type="Hello.HelloService, Hello"
                   objectUri="HelloService.soap" />
      </service>
    </application>
  </system.runtime.remoting>
</configuration>

This service is IIS hosted. IIS hosting requires that the URI be suffixed by either .rem or .soap. Let's use .rope at the server. In this instance, we will again receive a RemotingException this time with the text 'Object </Hello.soap> has been disconnected or does not exist at the server'.

Ensure that the URIs match! Also ensure that when IIS hosts the Remoting server the URIs end in .rem or .soap.

Mismatched protocol / port

For this test we switch to a console-hosted server, the configuration file for which is as follows:

<configuration>
  <system.runtime.remoting>
    <application name="RemotingHello">
      <service>
        <wellknown mode="SingleCall" type="Hello.HelloService, Hello"
                   objectUri="HelloService.soap" />
      </service>
      <channels>
        <channel ref="http" port="8000" />
      </channels>
    </application>
  </system.runtime.remoting>
</configuration>

Suppose we were to change the protocol to TCP at the server and leave the client talking http.

Here again we receive a RemotingException. This time the text will be 'The underlying connection was closed: An unexpected error occurred on a receive'.

Getting the Port wrong results in the same exception being thrown, the only difference being that it takes a little bit longer to fail. Ports and protocols must match across server and client.

Missing URI

Another possibility is that the remote server is not running—for example, the server is hosted by IIS and the virtual application or related assembly is simply missing. Using our Basic Hello Remoting server once again, we expect a virtual app, RemotingHello, to be running; if not, we receive an unhandled exception (depending on the calling code). This time, however, the exception will be: 'Cannot load type clr:Hello.HelloService, Hello'.

In these cases, ensure that the virtual application is running and that the required assembly is correctly located in the associated bin subfolder.

In summary, server-defined endpoints must be correctly referenced at the client for server activation, which means that ports, protocols, and URI definitions all have to match. Getting this wrong is all too easy. So, for example, if the server location is defined as:

<service>
   <wellknown mode="SingleCall" type="Hello.HelloService, Hello" 
              objectUri="HelloService.soap" />
</service>

then the client setting must be:

<client url="https://localhost/RemotingHello">
   <wellknown type="Hello.HelloService, Hello" 
              url="https://localhost/RemotingHello/HelloService.soap" />
</client>

where the URL denotes the IIS virtual app hosting the Remoting service and the type denotes the class and assembly name.

Remoting and ASP.NET Web Services

One of the best and worst things about IT design is that there are so many architectural components from which to choose. Web services and .NET Remoting fit into this category, and it is sometimes difficult to decide which technology to use for what purpose. The correct answer is, of course, to choose the one that works best for the problem to be solved. Prescriptive comments such as "always use Web services" or "Web services are a subset of Remoting so it's all Remoting anyway" are to be avoided. This section positions these two technologies in terms of why picking one technology rather than the other might make sense in a given scenario.

ASP.NET Web Service vs. .NET Remoting

Let's start with a definition of a Web service as being a service available over the Web. Not a very useful definition? We could further refine it as being "An addressable unit of processing that is accessed via SOAP and HTTP. This unit of processing is described via WSDL and may be published via UDDI." This is more useful as it serves to distinguish a Web service from say a Web server sending HTML back to a browser. For the purposes of a comparison with .NET Remoting we are focusing specifically on the definition of a Web service as opposed to a programmatic service available over the Web. A remoted host accessible over HTTP from a client using WSDL, for example, is, according to our definition, a Web service. With this preamble (and a focus on the Microsoft ASP.NET Web service implementation), what factors should we consider in choosing an ASP.NET Web service over .NET Remoting as "glueware: in a distributed solution?

Interoperability

A recurring Microsoft message is that if you need interoperability between heterogeneous systems, then a Web services approach that uses open standards (SOAP, XML, HTTP) is the right choice, and the use of .NET Remoting is never an interop solution. For homogeneous systems where all participants are CLR managed, .NET Remoting may be the right choice. This is a pretty broad brush, but a useful distinction to make. Clients of .NET remoted objects need to be .NET clients. If your functionality has to be addressable over the Web (by Web, here I mean Internet) by loosely coupled SOAP clients (for example, a Unix process), then Web services are the correct choice. The intranet, of course, is not subject to the same limitations: All clients may be .NET clients and in this configuration .NET Remoting is not precluded. Similarly, for an environment where the middle (app) tier is behind a firewall and communicates with the Web tier directly, .NET Remoting may still be an option.

Strong Type Support

.Net Remoting supports all managed types, classes, interfaces, enums, objects, etc. This is often categorized as "Rich Type Fidelity." A key point here is that where both client and server components are CLR-managed objects running in application domains, interoperability in terms of datatypes is a non issue. Essentially, we have a closed-world system. Both ends of the conversation are fully understood and, therefore, we can take advantage of this fact with respect to the types of data and objects we use to communicate.

Where we have heterogenous systems we need to consider interoperability between those systems. . With respect to interoperable datatypes we need to tread carefully. Web service datatype definition, for example, relies on XML Schema Definitions (XSD) for datatype semantics. Any type that can be described using XSD and that interoperates over SOAP can be used. This does, however, preclude the use of some datatypes—for example, there is no W3C XSD representation of unsigned char types or enums; collections are handled differently across differing Web service implementations, as are exceptions and datasets. Another issue is that private fields and properties are not passed across Web service calls. These are not critical issues in and of themselves, but are rather factors to be taken into consideration when designing and testing systems that are required to interoperate across differing technologies; just because you can send something doesn't mean you can receive it.

Again, where interopability between heterogeneous systems is a requirement, .NET Remoting should not be considered as an enabling technology. In a closed-world CLR-managed solution, it might be.

State Management

We have already seen the number of ways in which state management can be achieved using .Net Remoting based on activation modes being either client activated or Singleton. Managing per-client connection state across HTTP (a stateless protocol with indefinite timeout) would be non trivial and impractical for both .NET Remoting and Web services. Nonetheless, if you need to maintain state then Remoting offers a solution on a per-object basis. Web services do not offer this per-client connection state management; they do, however, still provide access to the ASP.NET session and application objects.

Lifetime Management

Related to state management is lifetime management. Remoting offers, as we have seen, a functionally rich mechanism for managing the lifetime of the remote object. Web service objects come and go with the Web service call (this is the case conceptually for both synch and asynch). In this respect, then, Web services function as single call types with respect to Remoting. Remoting provides much greater control over the activation and termination of the remote object. This may or may not be of interest to your design.

Call by Value vs. Call by Reference

Objects passed to Web service calls are serialized and passed by value. Objects passed to Remoting or the called objects themselves may be passed by value or by reference. Serialized remoted object methods are processed at the client. These semantic differences should be considered when choosing between Remoting and Web services. Again, it depends on the nature of the problem being solved as to whether these considerations are important to you.

Supported Protocols

Web service calls are limited to SOAP-encoded XML over HTTP. Remoting can use TCP transports or the infrastructure can be extended to support custom protocols. For example, an implementation of Remoting using Named Pipes is available at www.gotdotnet.com in the jhawk user samples section.

Here's a snippet from the NamedPipe readme illustrating the extensible nature of Remoting:

The channel plugs into .NET Remoting using the pluggable channel architecture by implementing the IChannel* interfaces.

The Named Pipes channel supports the following features:

* Communication over named pipes

* Synchronous messages

* Asynchronous messages

* One way messages

* Callbacks

* Channel Sinks

* Channel Properties

* Automatic pipe name generation'

So, if you need Named Pipes, Remoting offers a solution. Once again, however (as is the case with the SSPI NTLM authentication solution), this solution is currently unsupported by Microsoft. It is likely that Microsoft may address this requirement at some future date.

Performance

If performance really is a key concern for your design, then Remoting over TCP using a binary message format does offer some significant performance benefits. A full description of the environment used to produce the results described in this article and of the test themselves can be found in the article Performance Comparison: .NET Remoting vs. ASP.NET Web Services

Here are some summarized performance statistics from that article:

Legend: ASMX – Web Service, all others depict Remoting solutions
WS denotes a Windows service hosting the remoted component.

Figure 1. Performance statistics

The article goes on to explain the performance figures as follows:

"WS_TCP_Binary, as shown above, in which the object is configured to use the TCP channel and the Binary formatter with host being a Windows service, outperforms other distribution techniques. The reason for this is that this approach transmits binary data over raw TCP sockets, which are more efficient than HTTP. and since the data does not have to be encoded/decoded, there is less processing overhead. We see around 60% performance spread between WS_TCP_Binary and the slowest approach.

Though IIS_HTTP_Binary produces the same binary payload as WS_HTTP_Binary does, it is slower, as there is an extra process hop from IIS (Inetinfo.exe) to Aspnet_wp.exe. The same reason explains the performance difference between IIS_HTTP_SOAP and WS_HTTP_SOAP.

WS_HTTP_Binary and WS_TCP_SOAP offer very similar performance. Although the former has the additional overhead of parsing HTTP, and the latter has the additional overhead of parsing SOAP, it seems the overhead of HTTP parsing and SOAP parsing come out about the same in this case.

The ASP.NET Web service outperforms IIS_HTTP_SOAP and WS_HTTP_SOAP, since ASP.NET XML serialization is more efficient than .NET Remoting SOAP serialization. And as can be seen above, the ASP.NET Web service is very similar to IIS_HTTP_Binary in performance."

If raw speed really is of primary importance, then this "60% performance spread" may be significant. The drawback is the need to host the server inside a Windows service in order to use the TCP protocol (see the earlier section on Remote Hosting). Effectively, this trades off security for performance, and as an approach is "not recommended over the Internet or a non secure Intranet."

Summary

ASP.NET Web services are XML based, for practical purposes requiring the use of HTTP (given that they are hosted by IIS), and provide a simple programming model and strong cross-platform support. They provide for some degree of extensibility through the use of SoapExtensions—for example, encrypting the datastream. Remoting has a more complex programming model but provides some clear advantages in terms of type fidelity, state management, and extensibility, both in terms of protocols and message formats. Remoting cannot be used for non-.NET clients, so direct Internet clients to remote host connectivity is not viable. Remoting does not offer a security model when hosted outside of IIS. When IIS hosted, Remoting offers the same security features as ASP.NET, including the use of secure protocols such as SSL. If interoperability with other platforms is not a consideration and both client and server configuration are under your full control, then consider .NET Remoting. When using Remoting, prefer IIS hosting using the HTTP channel to non IIS hosting in order to benefit from the associated security and scalability infrastructure. This does, of course, mean that you need to be in a position to mandate IIS in your solution. If this is not possible, then Remoting may be too arduous a task to take on; this depends on the nature of the problem being solved. As .NET Remoting requires a .NET client, it makes sense to use the fastest available formatter, so choose binary in preference to SOAP as this yields better performance. Bear in mind the Best Practices section suggestion above that this formatter be applied at release time and not during development.

Executive Summary

.NET Remoting is a useful tool to employ in certain types of distributed solutions. It offers an extensible model in terms of the protocols and message formats it can support and can offer performance advantages in specific scenarios. It should not be deployed directly on the Internet and its server objects should be hosted under IIS in order to benefit from the security and performance features that IIS provides to processes running under its control.

Remoting should be considered for "closed world" distributed solutions where both client and server are CLR-managed processes. Examples are as a component at any tier within an intranet solution either over a secure TCP channel such as IPSec or over HTTP or as a middle-tier application component conversing with a .NET Web tier component over a firewall; in this case, the HTTP channel with the binary formatter should be chosen after the solution has been proven using the SOAP formatter.

For systems that need to interoperate with non-CLR clients, use ASMX Web services, taking care to avoid some of the pitfalls with respect to datatypes (refer to the section on Strong Type Support).

Note that hosting outside of IIS using TCP brings with it performance benefits, but at the cost of requiring custom security.

Design and Implementation

Implementing and configuring Remoting is a relatively easy process. Choosing the Remoting host, protocol, and activation model should be the first task in the process. Keep the design and implementation as simple as possible and think carefully about which interface publication mechanism makes most sense for your solution. The recommended approach is to publish the interface only as this is the cleanest conceptual model; this does, however, preclude the use of client-activated objects. The debugger, event logs, and network monitor are all useful tools during the development process, and all can be used to good effect when developing remote components.

Future of Remoting

Questions such as when to use Remoting versus when to use Web services are difficult questions, compounded by the lack of clarity with respect to definition of terms. For example, the definition of a Web service is unclear and Remoting may be configured to function as a Web service.

It is likely that in the future Remoting and ASMX technologies will be merged closer together. For the moment, though, we do have at least a reasonable picture of when to use which technology, as described above.

The development focus currently is on providing GXA implementations of routing, security, and transaction support. This support is based on the use of SOAP headers and is currently targeted directly at extending the functionality of Web services. While GXA does not address .NET Remoting in the classical sense as described in this article, it does address many of the issues that one would use Remoting for, such as state and transaction management. Although the current GXA implementation sets out to solve many of the problems faced by Web services, it is fundamentally targeted at solving these problems in as technologically agnostic a manner as possible. It will be interesting to see the effect that the development of GXA has on both Web services and .Net Remoting.

Additional Resources