Blue Theme Orange Theme Green Theme Red Theme
 
Home | Forums | Videos | Photos | Downloads | Blogs | E-Books | Interviews | Jobs | Beginners | Training
 | Consulting  
Submit an Article Submit a Blog 
 Login Close
User Id:
Password:
 
Forgot Password
Forgot Username
Why Register
 Jump to
Skip Navigation Links
TechnologyExpand Technology
WebsiteExpand Website
 Resources  
Close
 Our Network  
Close
Search :       Advanced Search »
Home » Remoting » .NET Remoting - Events, Events? Events!

.NET Remoting - Events, Events? Events!

You want the server to fire an event and all of the clients or only some specific must receive it. This article describes several approaches to the problem using .NET Remoting events model.

Technologies: Remoting,Visual C# .NET
Total downloads : 2738
Total page views :  44954
Rating :
 3.33/5
This article has been rated :  3 times
   Print Read/Post comments Post a comment  Rate  
   Email to a friend  Bookmark  Similar Articles  Author's other articles  
Download Files:
RemotingSol2.zip | RemotingSol1.zip | RemotingSol3.zip
 
Become a Sponsor


Related EbooksTop Videos

Introduction

You have the server and several clients. You want the server to fire an event and all of the clients or only some specific must receive it. This article describes several approaches to the problem.

By events, I particular mean a process satisfying to the following statements:

  1. The caller sends the same message to several receivers.
  2. All calls are performed concurrently.
  3. The caller finally gets to know receivers replies.

I hope the first statement does not require any additional explanation.

All calls are performed concurrently. If connection to the specific client is slow (or is broken), sending to other clients will not be delayed until that specific client replies (or server recognizes clients unavailability via time-out).

The third statement stems from the real business needs. Usually a caller has to know whether recipients successfully receive the message. Also it would be good to gather recipients' replies also, when it is possible.

Using the code (the first solution) .NET Native Events

Let's study the first sample. Well-known layer contains delegate declaration and public available event.

///
/// Is called by the server when a message is sent.
///
public delegate void MessageDeliveredEventHandler(string message);
///
/// ChatRoom provides common methods for the chatting.
///
public interface IChatRoom
{
///
/// Sends the message to all clients.
///
/// Message to send.
void SendMessage(string message);
///
/// Message delivered event.
///
event MessageDeliveredEventHandler MessageDelivered;
}

In my implementation the caller calls SendMessage method, which, in its turn, fires the event. This call can be made directly by a client though.

Clients create delegate instances pointed to MarshalByRefObject-derived class and add handlers to the event. The only issue here is that delegate should point to well-known class, so as a work-around I declared well-known class that just calls late-bound method (MessageReceiver class).

IChatRoom iChatRoom = (IChatRoom) Activator.GetObject(typeof(IChatRoom), "gtcp://127.0.0.1:8737/ChatRoom.rem");
iChatRoom.MessageDelivered += new MessageDeliveredEventHandler(messageReceiver.MessageDelivered);
// ... ask user to enter the message
// and force the event
iChatRoom.SendMessage(str);

Server provides an instance implementing IChatRoom interface.

///
/// Sends the message to all clients.
///
/// Message to send.
public void SendMessage(string message)
{
Console.WriteLine("\"{0}\" message will be sent to all clients.", message);
if (this.MessageDelivered != null)
this.MessageDelivered(message);
}
///
/// Message delivered event.
///
public event MessageDeliveredEventHandler MessageDelivered;

What we've got finally with this approach?

The pros:

  • Very easy to implement if all business objects are located in well-known layer.

The cons:

  • Late-binding is required for business objects located in "unknown for clients" DLL.
  • Calls are made consecutively. The next client will be called only when the previous one returns a result.
  • If a client is unreachable or throws an exception, invoking is stopped and all remain clients will not receive the message.
  • You should manage sponsorship separately. 

You can mark MessageReceiver.MessageDelivered method with one-way attribute to solve the second problem. But you should understand that there is no way to get call results in this case. Disconnected clients will never get excluded from the events recipient list. It's like a memory leak.

[OneWay]
public void MessageDelivered(string message)
{
if (this.MessageDeliveredHandler != null)
this.MessageDeliveredHandler(message);
}

Summary

This scheme is completely unacceptable. It is slow, unreliable and does not fit to my conditions.

You can use this scheme for short-living affairs that do not have too many clients and each client should have a possibility to break an event process.

Using the code (the second solution). Interface-based approach

Let's study the second sample. Known layer contains event provider interface and client receiver interface:

///
/// Describes a callback called when a message is received.
///
public interface IChatClient
{
///
/// Is called by the server when a message is accepted.
///
/// A message.
object ReceiveMessage(string message);
}
///
/// ChatRoom provides common methods for chatting.
///
public interface IChatRoom
{
///
/// Sends the message to all clients.
///
/// Message to send.
void SendMessage(string message);
///
/// Attaches a client.
///
/// Receiver that will receive chat messages.
void AttachClient(IChatClient iChatClient);
}

IChatClient interface must be implemented by any object which wants to receive chat messages. Client class implements IChatClient interface.

namespace Client
{
class ChatClient : MarshalByRefObject, IChatClient
{
static void Main(string[] args)
{
// client attaches to the event
IChatRoom iChatRoom = (IChatRoom) Activator.GetObject(typeof(IChatRoom), "gtcp://127.0.0.1:8737/ChatRoom.rem");
iChatRoom.AttachClient(new ChatClient());
//... and asks user to enter a message.
// Then fires the event
iChatRoom.SendMessage(str);
//...
}
}
}

Server implements IChatRoom interface and allows attaching the clients. I keep clients in the hash only because I want to remove failed receivers quickly.

I added additional comments to the snippet below.

class ChatServer : MarshalByRefObject, IChatRoom
{
///
/// Contains entries of MBR uri => client MBR implementing IChatClient interface.
///
static Hashtable _clients = new Hashtable();
///
/// Attaches the client.
///
/// Client to be attached.
public void AttachClient(IChatClient iChatClient)
{
if (iChatClient == null)
return ;
lock(_clients)
{
//****************
// I just register this receiver under MBR uri. So I can find and perform an
// operation or remove it quickly at any time I will need it.
_clients[RemotingServices.GetObjectUri((MarshalByRefObject)
iChatClient)] = iChatClient;
}
}
///
/// To kick off the async call.
///
public delegate object ReceiveMessageEventHandler(string message);
///
/// Sends the message to all clients.
///
/// Message to send.
/// Number of clients having received this
/// message.
public void SendMessage(string message)
{
lock(_clients)
{
Console.WriteLine("\"{0}\" message will be sent to all clients.", message);
AsyncCallback asyncCallback = new AsyncCallback(OurAsyncCallbackHandler);
foreach (DictionaryEntry entry in _clients)
{
// get the next receiver
IChatClient iChatClient = (IChatClient) entry.Value;
ReceiveMessageEventHandler remoteAsyncDelegate = new ReceiveMessageEventHandler(iChatClient.ReceiveMessage);
// make up the cookies for the async callback
AsyncCallBackData asyncCallBackData = new AsyncCallBackData();
asyncCallBackData.RemoteAsyncDelegate = remoteAsyncDelegate;
asyncCallBackData.MbrBeingCalled = (MarshalByRefObject) iChatClient;
// and initiate the call
IAsyncResult RemAr = remoteAsyncDelegate.BeginInvoke(message, asyncCallback, asyncCallBackData);
}
}
}
// Called by .NET Remoting when async call is finished.
public static void OurAsyncCallbackHandler(IAsyncResult ar)
{
AsyncCallBackData asyncCallBackData = (AsyncCallBackData)
ar.AsyncState;
try
{
object result = asyncCallBackData.RemoteAsyncDelegate.EndInvoke(ar);
// the call is successfully finished and
// we have call results here
}
catch(Exception ex)
{
// The call has failed.
// You can analyze an exception
// to understand the reason.
// I just exclude the failed receiver here.
Console.WriteLine("Client call failed: {0}.", ex.Message);
lock(_clients)
{
_clients.Remove( RemotingServices.GetObjectUri(asyncCallBackData.MbrBeingCalled) );
}
}
}
}

The pros:

  • All calls are made concurrently.
  • Failed receivers do not affect other receivers.
  • You can conduct any policies about failed receivers.
  • You know results of the calls and you can gather ref and out parameters.

The cons:

  • Much more complicated than the first scenario. 

Summary

This is exactly a pattern you should use if you need to implement an event and you use native channels. I did not implement attaching and detaching sponsors here, but you should definitely consider it if your clients do not hold on receivers.

Using the code (the third solution). Broadcast Engine

Let's do the same with Genuine Channels now. This approach looks like the previous one. But it is easier at the server side and has absolutely different internal implementation.

Both known layer and client have absolutely the same implementation. We will find the difference only at the server.

Server constructs a Dispatcher instance that will contain list of recipients:

private static Dispatcher _dispatcher = new Dispatcher(typeof(IChatClient));
private static IChatClient _caller;

To perform absolutely async processing, server attaches a handler and switches on async mode.

static void Main(string[] args)
{
//...
_dispatcher.BroadcastCallFinishedHandler += newBroadcastCallFinishedHandler( ChatServer.BroadcastCallFinishedHandler );
_dispatcher.CallIsAsync = true;
_caller = (IChatClient) _dispatcher.TransparentProxy;
//...
}

Each time the client wants to receive messages, server puts it into dispatcher instance:

///
/// Attaches the client.
///
/// Client to attach.
public void AttachClient(IChatClient iChatClient)
{
if (iChatClient == null)
return ;
_dispatcher.Add((MarshalByRefObject) iChatClient);
}

When the server wants to fire an event, it just calls a method on the provided proxy. This call will be automatically sent to all registered receivers:

///
/// Sends message to all clients.
///
/// Message to send.
/// Number of clients having received this message.
public void SendMessage(string message)
{
Console.WriteLine("\"{0}\" message will be sent to all clients.",message); _caller.ReceiveMessage(message);
}

In my sample I ignore call results. Anyway Dispatcher will automatically exclude failed receivers after the 4th failure by default. But if I would like to do it, I will write something like this:

public void BroadcastCallFinishedHandler(Dispatcher dispatcher, IMessage message, ResultCollector resultCollector)
{
lock(resultCollector)
{
foreach(DictionaryEntry entry in resultCollector.Successful)
{
IMethodReturnMessage iMethodReturnMessage = (IMethodReturnMessage) entry.Value;
// here you get client responses
// including out and ref parameters
Console.WriteLine("Returned object = {0}", iMethodReturnMessage.ReturnValue.ToString());
}
foreach(DictionaryEntry entry in resultCollector.Failed)
{
string mbrUri = (string) entry.Key;
Exception ex = null;
if (entry.Value is Exception)
ex = (Exception) entry.Value;
else
ex = ((IMethodReturnMessage) entry.Value).Exception;
MarshalByRefObject failedObject = dispatcher.FindObjectByUri(mbrUri);
Console.WriteLine("Receiver {0} has failed. Error: {1}", mbrUri, ex.Message);
// here you have failed MBR object (failedObject)
// and Exception (ex)
}
}
}

You have all results gathered at one place, so you can make any decisions here.

Broadcast Engine has synchronous mode. In this mode all calls are made concurrently, but it waits while all clients reply or time out expires. Sometimes its very useful, but this mode consumes one thread until call will be finished. Take a look at Programming Guide which contains more details.

Summary

The pros:

  • Easier to use than the second approach.
  • All calls are made concurrently.
  • Failed receiver will not affect other receivers.
  • Broadcast Engine automatically recognizes situations when receiver is able to receive messages via true broadcast channel. If receivers did not receive a message via true broadcast channel, Broadcast Engine repeats sending of the message via usual channel. So you can utilize IP multicasting with minimum efforts.
  • Broadcast Engine takes care of the sponsorship of attached MBR receivers.

The cons:

You still have to declare well-known interface in order to implement events.


Login to add your contents and source code to this article
 [Top] Rate this article
 About the author
 
Dmitry Belikov
Looking for C# Consulting?
C# Consulting is founded in 2002 by the founders of C# Corner. Unlike a traditional consulting company, our consultants are well-known experts in .NET and many of them are MVPs, authors, and trainers. We specialize in Microsoft .NET development and utilize Agile Development and Extreme Programming practices to provide fast pace quick turnaround results. Our software development model is a mix of Agile Development, traditional SDLC, and Waterfall models.
Click here to learn more about C# Consulting.
 
Introducing MaxV - one click. infinite control. Hyper-V Hosting from MaximumASP.
Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Dynamic PDF
ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
Go.NET
Build custom interactive diagrams, network, workflow editors, flowcharts, or software design tools. Includes many predefined kinds of nodes, links, and basic shapes. Supports layers, scrolling, zooming, selection, drag-and-drop, clipboard, in-place editing, tooltips, grids, printing, overview window, palette. 100% implemented in C# as a managed .NET Control. Document/View/Tool architecture with many properties&events. Optional automatic layout.
Dundas Software
Dundas Chart for .NET is the most advanced .NET charting package available today.  With an extremely complete feature set, elegant architecture and easy implementation, Dundas Chart can quickly add advanced Charting functionality to enhance and transform ASP.NET and Windows Forms applications.  Whether you are implementing charting into internal projects, or building applications for clients, Dundas Chart offers advanced technology and advanced results to get the most out of data.
Clickatell's SMS Gateway
Clickatell's Developer Solutions allow you to SMS enable any website or application via a range of API's. Learn More about our API connections.
Free access to .NET Memory Management video
Everything you need to know about Garbage Collection, Temporary Objects, Fragmentation, Finalization and common causes of memory leaks in .NET. Watch the video here.
Microsoft Visual Studio 2010
Microsoft Visual Studio 2010 offers more to developers than any other Visual Studio release. Work more productively and collaboratively-with greater control over your work at every step. The Beta 2 can give you a head start on achieving efficiency.
 
   Print Read/Post comments Post a comment  Rate  
   Email to a friend  Bookmark  Similar Articles  Author's other articles  
Download Files:
RemotingSol2.zip | RemotingSol1.zip | RemotingSol3.zip
 
 Post a Feedback, Comment, or Question about this article
Subject:  
Comment:  
Become a Sponsor
 Comments
Thank you by kdpo1990 On March 15, 2007
Cool article.
Reply | Email | Delete | Modify | 
remove a Client in _clients hastable by aris On August 9, 2007
when a client disconect, we have event in public static void GenuineChannelsEventHandler(object sender, GlobalEventArgs e) how we can remove a client from _clients has table in this event thx
Reply | Email | Delete | Modify | 
question by Mark On February 12, 2009
Im new to the whole remoting thing and have a few questions. will the client constantly check on the server if there is an event that has been fired? if so, isnt it more eazy to make a ping system. The client will ping the server every x seconds/milliseconds, and if the server has a new message, event or whatever kind of action, the server will respond to the ping with the message or data that the client needs to have. This will solve the problem of clients being dependant on eachother. If a client disconnects, he also wont be able to ping. If a client doesnt ping in, lets say 5 seconds, the server can log him off.
Reply | Email | Delete | Modify | 

 Hosted by MaximumASP  |  Found a broken link?  |  Contact Us  |  Terms & conditions  |  Privacy Policy  |  Site Map  |  Suggest an Idea  |  Media Kit
Current Version: 5.2009.6.2
 © 1999 - 2009  Mindcracker LLC. All Rights Reserved