Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

.NET Remoting: Handling Remote Events Using Delegates – A Real World Example of 'Chat and File Send' Application

4.09/5 (14 votes)
14 May 2006CPOL10 min read 2   16.9K  
This document demonstrates how to handle events from a remote object using a real time ‘message and file transfer’ application.

Introduction

.NET Remoting is a simple programming model/framework which allows objects from different machines/processes/app-domains to communicate with each other. It allows the flexibility of using different types of communication protocols (TCP, HTTP, etc..) and message formatters (binary, SOAP, etc..). Communication among different app-domains is facilitated by Remoting objects. Remoting objects can be hosted using Managed Executables/IIS/.NET Component Services. It is also possible for .NET applications running on different machines to share the same instance of a Remoting object hosted on a server.

This document demonstrates how to handle events from a remote object using a real time ‘message and file transfer’ application.

Technologies covered in this document

This document demonstrates the following pattern and technologies:

  • Publisher Listener Design Pattern
  • Server activated singleton objects
  • Marshal by reference remote object access
  • Raising events from remote objects
  • Use of binary format over TCP channel

Publisher-Listener Design Pattern

Design patterns are an efficient way of reusing and sharing solutions to repeating problems. ‘Publisher-Listener’ is a behavioral Design Pattern. It is also known as ‘Observer’ pattern. By definition, a ‘Publisher-Listener’ pattern establishes a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Sample screenshot

As shown in the above diagram, in a ‘Publisher-Listener’ Design Pattern, when a publisher posts a message, all the listeners are automatically updated with the message. A publisher and listener can be the same object. There can be so many publishers and listeners. All the listeners have to register to the server to enable them to listen to the messages.

.NET Remoting terminologies

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 remote 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.

SingleCall

SingleCall objects service one and only one request coming in. SingleCall objects are useful in scenarios where the objects are required to do a finite amount of work. SingleCall objects are usually not required to store state information, and they cannot hold state information between method calls. However, SingleCall objects can be configured in a load-balanced fashion.

Singleton objects

Singleton objects are those objects that service multiple clients and hence share data by storing state information between client invocations. They are useful in cases in which data needs to be shared explicitly between clients and also in which the overhead of creating and maintaining objects is substantial.

For example, the following code snippet depicts a server-activated (well-known) type with activation mode set to SingleCall.

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

Client-Activated Objects (CAO)

Client-activated objects (CAO) are server-side objects that are activated upon request from the client. When the client submits a request for a server object using "new" operator or Activator.CreateInstance(), an activation request message is sent to the remote application. The server then creates an instance of the requested class and returns an ObjRef back to the client application that invoked it. A proxy is then created on the client side using the ObjRef. The client's method calls will be executed on the proxy. Client-activated objects can store state information between method calls for its specific client and not across different client objects. Each invocation of "new" returns a proxy to an independent instance of the server type. For example, the following code snippet depicts a Client-Activated type. Note that we no longer need a URL, as for client-activated types the type alone is sufficient for activation. Also, the well-known tag has been replaced by the activated tag.

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

Types of marshalling

Objects are valid only in the application domain where they are created. Any attempt to pass the object as a parameter or return it as a result will fail unless any of the following types of marshalling is used.

Marshal By Value (MBV)

For objects that are Marshal By Value (MBV), a complete copy of the object is made when the object is passed from one application to another. If the object is marked as Serializable, the object will automatically be serialized, transported from one application domain to the other, and then deserialized to produce an exact copy of the object in the second application domain.

Marshal By Reference (MBR)

For objects that are Marshal By Reference (MBR), a reference to the object is made when passed from one application to another. When the object reference (ObjRef) arrives in the remote application, it is turned into a "proxy" back to the original object. In order to marshal a Remoting object by reference, inherit it from the MarshalByRefObject class.

Proxy objects

When a client creates an instance of a remote object, it receives a proxy to the class instance on the server. All methods called on the proxy will automatically be forwarded to the remote class and any results will be returned to the client. From the client's perspective, this process is no different than making a local call.

Stateless and Stateful objects

The .NET Framework makes a provision for creating remote objects as stateless. When an object is configured as SingleCall, it will be created when a method is called on that object. The object processes the call, returns an optional result, and is then collected by the garbage collector. This way, the client is always connected to a fresh object with each call.

Configuring an object as a Singleton ensures that all clients will be connected to the same object whenever a call is made to that object.

Lease based lifetime

The lifetime of remote objects is controlled by a leasing mechanism. When an object is first created, it is given a lease time. When the lease time of the object reaches zero, the object will be disconnected from the Remoting infrastructure, and when all references to the object has been freed within the AppDomain, it will be collected by the garbage collector. A number of mechanisms are provided that allow the client to extend the lease on the object, thereby sustaining its life.

Raising events from remote objects

As with any normal .NET class, event handlers can be attached to Remoting objects so that a client can hook methods to serve the declared events. Whenever the event gets fired in the remote object, the event handler method is executed in the client. However, delegates require that the receiving object be able to obtain the type information for the class whose function is being wrapped by the delegate. In the case of Remoting, it means that the client assembly be available to the server. If the client assembly is not available to the server, that type information cannot be loaded.

To make remoted delegates work, an abstract class (MustInherit in Visual Basic .NET, abstract in C#) that contains the callback function must be defined in a common assembly that both the client and server have access to. The client can then derive a custom class from this abstract class to implement the logic in the callback. The abstract class needs to have a specific structure. The function to be used for callbacks must be a public function that cannot be overridden. This function must forward all calls to a protected abstract function that is overridden in the derived client classes. The reason for this architecture is that the delegate needs to be able to bind to a concrete implementation of the callback function, and this implementation must not be overridable.

Remoted delegates are implemented in the ‘Message and File Transfer Application’ (explained in section 6). You can refer the source code attached.

Message and File Transfer Application

Problem definition

Basically, it is a chat application for intranet users of a Windows network. The following screenshots explain the required functionality of this tool. When a user executes the client application, the user gets automatically logged into the application. It then loads a WinForm with a list of all the users who have already logged in. This list gets automatically updated in frequent intervals. In order to chat with a user, all that you have to do is to select the name of the user and type the message on the editable textbox. On entering the message, it gets published to the remote object with the ‘from’ and ‘to’ addresses and all the listening clients get it. On receiving a message, each client checks the ‘to’ address and decides whether to display that message or not.

Step 1

User ‘Joser’ logs in

Sample screenshot

Step 2

User ‘Vijiths’ logs in and sends a few messages and file to user ‘Joser’

Sample screenshot

Sample screenshot

Step 3

User ‘Joser’ gets a download-confirmation message

Sample screenshot

Sample screenshot

Sample screenshot

Solution architecture

The solution architecture is shown in the diagram below. There is class the object of which (the remote object) is hosted on a remote server by using a console application. Loading of this application can be configured using a Windows service. As with any client-server application, it is a must that the server application should be up and running before the clients can access it. This object is a singleton object. The client application is a WinForm which has reference to a local copy of the remote component. Using the local reference, the client application creates a proxy object. This proxy object then gets a reference of the remote object so that whenever the clients access the proxy object, it in turn accesses the remote object. Since the remote object is a singleton object, all the clients connecting to a single server accesses the same instance of the remote class. This facilitates the communication among different clients.

The remote object exposes certain event handlers such as OnLogin, OnLogout, OnMessagePublish, etc.. The client application can hook methods to these event handlers. So whenever these events happen, all the clients get notified about it.

Whenever a user sends some message to another user, the OnMessagePublish event is raised. The sender acts as a publisher. All the clients who have registered (added) a method to the OnMessagePublish event handler act as listeners. This is how the ‘Publisher-Listener’ Design Pattern is implemented in this design.

Sample screenshot

Configuration files

Server configuration file

XML
<configuration>
  <system.runtime.remoting>
      <application>
       <lifetime leaseTime="20D" sponsorshipTimeout="1H" 
                 renewOnCallTime="1D" leaseManagerPollTime="1H" />
         <service>
            <wellknown
               mode="Singleton"
               type="MessageShare.SharedMessage,MessageShare"
               objectUri="SharedMessage"/>
         </service>
         <channels>
           <channel ref = "tcp" port = "8080"> 
             <serverProviders>
               <formatter ref="binary" typeFilterLevel="Full" />
             </serverProviders>  
           </channel>
         </channels>
      </application>
  </system.runtime.remoting>
</configuration>

Client configuration file

XML
<configuration>
   <system.runtime.remoting>
      <application>
      <lifetime leaseTime="20D" sponsorshipTimeout="1H" 
               renewOnCallTime="1D" leaseManagerPollTime="1H" />
        <client>
        <wellknown type="MessageShare.SharedMessage,MessageShare" 
              url="tcp://10.201.33.229:8080/SharedMessage" />                                        
        </client>
         <channels>
            <channel ref="tcp" port="0" clientConnectionLimit="20" >
           </channel>
         </channels>        
       </application>
   </system.runtime.remoting>
  <appSettings>
    <add key="RemotingUrl" value="tcp://10.201.33.229:8080/SharedMessage"></add>
  </appSettings>
</configuration>

Please remember to change the IP address and port number - tcp://10.201.33.229:8080 to that of the server machine where the server application is running. The port number should be the same as that configured in the server configuration file.

Code snippets

Remoting class

Server application

Client application (WinForm)

Installation files

Installation notes

  • Please remember to change the IP address and port number - tcp://10.201.33.229:8080 in the client configuration file to that of the server machine where the server application is running.
  • The port number in the client configuration file should be same as that configured in the server configuration file.
  • Manually start the server application (executable console application).
  • Use the client application only after ensuring the following:
    • The configuration files are updated properly.
    • The server application is started.

Reference

Abbreviations

AbbreviationExpansion
MBVMarshal By Value
MBRMarshal By Reference
CAOClient Activated Objects
SOAPSimple Object Access Protocol
IISInternet Information Services

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)