Home Search Contact us About us
Title Remoting callbacks and events in .NET 2.0
Summary This is the simplest application I could create to demonstrate remoting events. The client can call the server as in simple remoting or web services. However the magic of this article is in the servers ability to call the client.
Contributor John McTainsh
Published 20-Oct-2006
Last updated 20-Oct-2006
Page rating   Be the first to rate this page! Useless Brilliant
 Download Sample project files - 18 Kb
Introduction
A simple example
Conclusion

Introduction

This article is part two on .NET remoting. Part 1 is somewhere on this site and is a gentle introduction into .NET remoting. This article we will cover how to make the server callback into the client. We will do this in such a way the the server can (at any time) call the server. I wrote this article because there is too many big complex examples on the web and no little simple ones. This is the smallest example I could make. I hope you find it useful.

Using remoting or even web services, you can make called from the clien to the server. However only in remoting is it possible for the server to fire an event back into the server. I believe web services will be upgraded to enable events or callbacks in the not too distant future but at this point in time, .NET 2.0 does not support events or callbacks for web services. So we will be using remoting.

For this example I have created a simple client and server. objects in the project are.

  • JrmServer.exe. Server displaying client calls and firing events back to the client when the user enters text.
  • JrmClient.exe. Which connects to the server, continously send numbers to the server and displays messages fired from the server.
  • BidirectionShare.dll. Is a shared object used to define the interface between client and server.

We could make this as 2 executables without the dll but it is more like a real life design if we keep the interface in a shared d\DLL. The below is what the applicatrion look like running. You can have as many clients as you wish.

A simple example

I will show all the code here and explain how to add it to your own project. Please read my comments in the code as these are quite detailed. we will start with the shared code.

Create the shared DLL

Create a new "Class library" project in C#. and paste the following code into the auto generated program.cs.

using System;
using System.Runtime.Remoting;

namespace BidirectionalShare
{
    /// <summary>
    /// Delegate defines the metho call fromthe server to the client
    /// </summary>
    /// <param name="s">Pass a string for testing</param>
    public delegate void NotifyCallback( string s );

    /// <summary>
    /// Defines server interface which will be deployed on every client
    /// </summary>
    public interface ICallsToServer 
    {
        /// <summary>
        /// Function to call the server from the client
        /// </summary>
        /// <param name="n">Some number</param>
        /// <returns>Some interesting text</returns>
        string SomeSimpleFunction( int n);

        /// <summary>
        /// Add or remove callback destinations on the client
        /// </summary>
        event  NotifyCallback Notify;
    }
    
    /// <summary>
    /// This class is used by client to provide delegates to the server that will
    /// fire events back through these delegates. Overriding OnServerEvent to capture
    /// the callback from the server
    /// </summary>
    public abstract class NotifyCallbackSink : MarshalByRefObject
    {   
        /// <summary>
        /// Called by the server to fire the call back to the client
        /// </summary>
        /// <param name="s">Pass a string for testing</param>
        public void FireNotifyCallback( string s )
        {
            Console.WriteLine( "Activating callback" );
            OnNotifyCallback( s );
        }

        /// <summary>
        /// Client overrides this method to receive the callback events from the server
        /// </summary>
        /// <param name="s">Pass a string for testing</param>
        protected abstract void OnNotifyCallback( string s );
    }
}

Create the Server

Create a new "Console Application" project in C#. and paste the following code into the auto generated program.cs file.

using BidirectionalShare;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Serialization.Formatters;
using System.Text;

namespace JrmServer
{
    class JrmServer
    {
        /// <summary>
        /// Main application starts here
        /// </summary>
        /// <param name="args">Maybe pass in the port number</param>
        static void Main( string[] args )
        {
            // Creating a custom formatter for a TcpChannel sink chain.
            BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
            provider.TypeFilterLevel = TypeFilterLevel.Full;

            // Creating the IDictionary to set the port on the channel instance.
            IDictionary props = new Hashtable();
            props["port"] = 8392;                   // This must match number on client

            // Pass the properties for the port setting and the server provider
            TcpChannel m_tcpChannel = new TcpChannel(props, null, provider);
            ChannelServices.RegisterChannel( m_tcpChannel, false );

            // Create the server object for clients to connect to
            RemotingConfiguration.RegisterWellKnownServiceType(
                typeof( ClientComms ),
                "RemoteServer",
                WellKnownObjectMode.Singleton );

            // Loop here until the uers is done
            Console.WriteLine( "Type some text to send to the client, or just press enter to exit" );
            string s;
            while( ( s = Console.ReadLine() ) != "" )
                ClientComms.FireNewBroadcastedMessageEvent( s );

            Console.WriteLine( "Sooo long and thanks for all the fish." );
        }
    }

    /// <summary>
    /// An instance of this class is exposed to the client allowing it to call into the server here
    /// </summary>
    public class ClientComms : MarshalByRefObject, ICallsToServer
    {
        /// <summary>
        /// Function to call the server from the client. This is not in the GUI thread.
        /// </summary>
        /// <param name="n">Some number</param>
        /// <returns>Some interesting text</returns>
        public string SomeSimpleFunction( int n )
        {
            Console.Write( "  Client sent : {0}      \r", n );
            return "Server says : " + n.ToString();
        }

        /// <summary>
        /// Local copy of event holding a collection
        /// </summary>
        private static event NotifyCallback s_notify;

        /// <summary>
        /// Add or remove callback destinations on the client
        /// </summary>
        public event NotifyCallback Notify
        {
            add    { s_notify = value; }
            remove { Console.WriteLine( "TODO : Notify remove." ); }
        }

        /// <summary>
        /// Call this method to send the string to the client. This call will throw an exception
        /// if the client has gone or network is down
        /// </summary>
        /// <param name="s">Some test to send to client</param>
        public static void FireNewBroadcastedMessageEvent( string s )
        {
            Console.WriteLine( "Broadcasting... Sending : {0}", s );
            s_notify( s );
        }
    }
}

You will need to add System.Runtime.Remoting to the references of you project. Do this by selecting Project->Add Reference from the menu. You will also need to add a reference to the class library dll you creatred in the previous step.

Create the Client

Create a new "Console Application" project in C#. and paste the following code into the auto generated program.cs file.

using BidirectionalShare;
using System;
using System.Collections.Generic;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Text;

namespace JrmClient
{
    class JrmClient
    {
        /// <summary>
        /// Client application starts here
        /// </summary>
        /// <param name="args">Maybe pass in the port number and IP address</param>
        static void Main( string[] args )
        {
            // Sleep for a moment to let the sewrver start
            System.Threading.Thread.Sleep( 2000 );

            // Use a defined port to receive callbacks or 0 to receive available port
            TcpChannel m_TcpChan = new TcpChannel( 0 );
            ChannelServices.RegisterChannel( m_TcpChan, false );

            // Create the object for calling into the server
            ICallsToServer m_RemoteObject = (ICallsToServer)
                Activator.GetObject( typeof( ICallsToServer ),
                "tcp://127.0.0.1:8392/RemoteServer" );      //  Must match IP and port on server

            // Define sink for events
            RemotingConfiguration.RegisterWellKnownServiceType(
                typeof( NotifySink ),
                "ServerEvents",
                WellKnownObjectMode.Singleton );
            NotifySink sink = new NotifySink();

            // Assign the callback from the server to here
            m_RemoteObject.Notify += new NotifyCallback( sink.FireNotifyCallback );

            // Keep calling till the server is gone (This is not necessary)
            Console.WriteLine( "Client is now ready to send and receive data..." );
            int n = 1;
            while( true )
            {
                try
                {
                    m_RemoteObject.SomeSimpleFunction( n++ );
                    System.Threading.Thread.Sleep( 1000 );
                }
                catch
                {
                    Console.WriteLine( "Server is not responding. Go home." );
                    return;
                }
            }
        }

        /// <summary>
        /// Derived from the wellknown object shared by the two applications. This is used to expose
        /// methods for calling backing into client from the server
        /// </summary>
        class NotifySink : NotifyCallbackSink
        {
            /// <summary>
            /// Events from the server call into here. This is not in the GUI thread.
            /// </summary>
            /// <param name="s">Pass a string for testing</param>
            protected override void OnNotifyCallback( string s )
            {
                Console.WriteLine( "Message from the server : {0}", s );
            }
        }
    }
}

You will need to add System.Runtime.Remoting to the references of you project. Do this by selecting Project->Add Reference from the menu. You will also need to add a reference to the class library dll you creatred in the previous step.

That is it. You can now build all three project. Run the server first and then a couple of clients. Type some text into the server to see it sent to the client. Also try terminating one of the client then send some text from the server again. You will still need to add some exception handlers to manage these situations.

Also try running the client on another machine and change 127.0.0.1 to the IP address of the server. See what happens when you pull the network connection between the client and the server.

Conclusion

To make things simpler, no object clean up has been done. You should take the time to remove unwanted event handlers and connections.

May things can go wrong with remoting so be ready for them. Simple calls to methods that simply add two number can throw exceptions because of network failure or missing servers. Make sure you test these situations and try to gracefully handle them. Also watch out for threads. Most of the events triggered by remoting calls are not in the GUI thread so you need to be sure you dont call the GUI before decoupling from the input thread with something like begin invoke.

Comments Date
Multi Clients 27-Aug-2009 Sadaqat
Hi,
This is indeed a good article. But this is not working for multi clients. If I register a new client then it only broad cast message the for new client not for existing client.
Can explain how to handle multi clients.
Does not work outside of the App domain 28-Sep-2009 john-paul renno
Thsi example does not appear to be working across different machines, however the callbacks do work for multiple clients on the same machine.
Worked great! 6-Oct-2009 Sean McHugh
This example worked for me locally and across the internet (one computer on cable internet and the other connected via dial up). I already had port forwarding on my router set up so there was no need for that.

The one thing that tripped me up for a bit was having to add the interface name before the method names or events (e.g. myInterface.NotifyCallback not just NotifyCallback).

But all in all for the amount of effort I had to put in compared to the results this was a great tutorial!

Do you have any suggestions on how to callback to one specific host instead of sending a broadcast message?
Fixing Multi-Client Notification 27-Oct-2009 Paul Morash
Great demo. It was very useful in helping me pull together some problems I had with understanding Remoting. The fix to make multiple clients work is to simply change the Notify event to chain the clients together.

public event NotifyCallback Notify
{
add { s_notify += value; }
remove { s_notify -= value; }
}
Principal Engineer 22-Apr-2010 Wayne Wright
Thank you for this very helpful and SIMPLE example of how to broadcast and listen to events via .NET remoting. I found many other examples before running across this one, but they either didn`t work at all, or were needlessly complicated.
Considerations 24-Nov-2011 Ivan Lamaddalena
Great article !!
www.mctainsh.com/Articles/Csharp/RemoteCallback.aspx 3-Jun-2012 Jean Claude
Hello i found this turoral real good.I tried to run the application in different machines its work.but after few minutes-I think 3 minutes the application crashed while trying to send messages.I think this is the 3 minutes Timeout of the .Net remoting.How could this problem be resolved?
Problem 2:
I added the following function:
public event NotifyCallback Notify
{
add { s_notify += value; }
remove { s_notify -= value; }
}
and it didnt work at all.
I would be happy if u do answer to the questions asked aboved.
Thank u very much
www.mctainsh.com/Articles/Csharp/RemoteCallback.aspx 3-Jun-2012 Jean Claude
Hello i found this turoral real good.I tried to run the application in different machines its work.but after few minutes-I think 3 minutes the application crashed while trying to send messages.I think this is the 3 minutes Timeout of the .Net remoting.How could this problem be resolved?
Problem 2:
I added the following function:
public event NotifyCallback Notify
{
add { s_notify += value; }
remove { s_notify -= value; }
}
and it didnt work at all.
I would be happy if u do answer to the questions asked aboved.
Thank u very much
Remoting callbacks and events in .NET 2.0 3-Jun-2012 Jean Claude
Hello i found this turoral real good.I tried to run the application in different machines its work.but after few minutes-I think 3 minutes the application crashed while trying to send messages.I think this is the 3 minutes Timeout of the .Net remoting.How could this problem be resolved?
Problem 2:
I added the following function:
public event NotifyCallback Notify
{
add { s_notify += value; }
remove { s_notify -= value; }
}
and it didnt work at all.
I would be happy if u do answer to the questions asked aboved.
Thank u very much
Home Search Contact us About us