Click here to Skip to main content
15,887,083 members
Articles / Desktop Programming / Windows Forms
Article

A Remoting Event (Simple and Efficient for Enterprise Solutions)

Rate me:
Please Sign up or sign in to vote.
4.84/5 (25 votes)
21 Aug 2006CPOL2 min read 162.8K   2.1K   85   50
This article contains the simplest solutions for: the security problem for DelegateSerializationHolder, the IO problem, and the messaging speed problem. Note: Messaging speed problem will appear when your application has worked for a long time.

demo project Image

Background

I always had an interest in socket programming. I have created several chat applications and complex socket based applications. When I learned about events in Remoting I was so glad, and the first thing that I thought about was creating a chat application. But in the test, I met with some problems.

Problem 1

The first problem was a security exception:

System.Security.SecurityException: Type System.DelegateSerializationHolder 
and the types derived from it (such as System.DelegateSerializationHolder) 
are not permitted to be deserialized at this security level.

Security Exception Image

Solution 1

This problem was solved by adding the typeFilterLevel attribute with Full value to the formatter element of both configuration files.

XML
<formatter ref="soap" typeFilterLevel="Full" />

Problem 2

But again, it did not work and an IO exception occurred:

System.Reflection.TargetInvocationException: 
Exception has been thrown by the target of an invocation. ---> 
System.IO.FileNotFoundException: Could not load file or assembly 
'Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' 
or one of its dependencies. The system cannot find the file specified.

This exception occurs while the request is deserialized at the server. The server tries to call the event handler whereas the event handler exists in the client assembly. Because the client assembly is not available at the server, the exception is thrown.

IO exception Image

Solution 2

An intermediate wrapper class, MarshalByRefObject will solve this problem. This wrapper class is located in the shared assembly, accessible for both the client and the server; therefore, a delegate can resolve the method's signature. In the client application, we will associate the shared object event with the WrapperMessageReceivedHandler method of the wrapper class and associate an event handler on the client with the MessageReceived event of the wrapper class.

Sequense of events Image

Why [OneWay] Attribute

Without defining the remote method as [OneWay], an exception will occur when the client is unreachable or has been disconnected without disassociating the event handler. By using [OneWay], no exception will occur on the client, but it will be in the invocation list of the server and, in longtime, will make your server slower to respond.

Solution 3

Instead of using the normal event invocation mechanism, you must invoke each delegate on your own, and if an exception occurs, you must disassociate the delegate from the invocation list of the event. In the end, you can remove the [OneWay] attribute.

Shared Class

C#
/// <summary>
/// Represents the method that will handle
/// the Remotable.RemoteClass.MessageReceived event.
/// </summary>
/// <param name="message">Received message</param>

[Serializable]
public delegate void MessageHandler(string message);

/// <summary>
/// Shared remoting class that orchestrate messaging tasks 
/// </summary>

public class RemoteClass:MarshalByRefObject
{

    /// <summary>
    /// Occurs when a broadcast message received.
    /// </summary>

    public event MessageHandler MessageReceived;
    /// <summary>
    /// Initializes a new instance of the Remotable.RemoteClass class.
    /// </summary>

    public RemoteClass()
    {
       
    }

    /// <summary>
    /// Obtains a lifetime service object to control the lifetime policy for this
    /// instance.
    /// </summary>
    /// <returns>
    ///An object of type 
    ///System.Runtime.Remoting.Lifetime.ILease used to control
    ///the lifetime policy for this instance. This is the current lifetime service
    ///object for this instance if one exists; otherwise, a new lifetime service
    ///object initialized to the value of the 
    ///System.Runtime.Remoting.Lifetime.LifetimeServices.LeaseManagerPollTime
    ///property.
    ///null value means this object has to live forever.
    /// </returns>

    public override object InitializeLifetimeService()
    {
        return null;
    }

    /// <summary>
    /// Broadcast message to all clients
    /// </summary>
    /// <param name="message">message string</param>

    public void Send(string message)
    {
        if (MessageReceived != null)
        {
            MessageHandler messageDelegate = null;
            Delegate[] invocationList_ = null;
            try
            {
                invocationList_ = MessageReceived.GetInvocationList();
            }
            catch (MemberAccessException ex)
            {
                throw ex;
            }
            if (invocationList_ != null)
            {
                lock (this)
                {
                    foreach (Delegate del in invocationList_)
                    {
                        try
                        {
                            messageDelegate = (MessageHandler)del;
                            messageDelegate(message);
                        }
                        catch (Exception e)
                        {
                            MessageReceived -= messageDelegate;
                        }
                    }
                }
            }
        }
    }

Client Application

C#
RemoteClass remoteClass;
WrapperClass wrapperClass;

private void Form1_Load(object sender, EventArgs e)
{
    //Configure remoting.

    RemotingConfiguration.Configure(Application.StartupPath + 
                          "\\Client.exe.config",false);

    // Create a proxy from remote object.

    remoteClass = (RemoteClass)Activator.GetObject(typeof(RemoteClass), 
                   "http://localhost:8080/Chat");
    //Create an instance of wrapper class.

    wrapperClass = new WrapperClass();
  
    //Associate remote object event with wrapper method.

    remoteClass.MessageReceived += new 
          MessageHandler(wrapperClass.WrapperMessageReceivedHandler);
    //Associate wrapper event with current form event handler.

    wrapperClass.WrapperMessageReceived += new 
           MessageHandler(MessageReceivedHandler);
}

Wrapper Class

C#
/// <summary>
/// Occurs when a broadcast message received.
/// </summary>

public event MessageHandler WrapperMessageReceived;
/// <summary>
/// Wrapper method for sending message to the clients.
/// </summary>
/// <param name="sender">Caller object</param>
/// <param name="args">Message data</param>

public void WrapperMessageReceivedHandler(string message)
{
    // forward the message to the client

    if(WrapperMessageReceived != null)
        WrapperMessageReceived(message);
}

License

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


Written By
Program Manager System Group
Iran (Islamic Republic of) Iran (Islamic Republic of)
Hossein Ghahvei Araghi
Birth date: 1978
Birth place: Iran
Academic Credentials : BS(Tehran University)
Microsoft Credentials : MCP, MCAD, MCTS 2.0, MCTS 3.5, MCPD 2.0, MCPD 3.5

Comments and Discussions

 
Questionabout the NAT Pin
yonking23-Aug-06 22:41
yonking23-Aug-06 22:41 
AnswerRe: about the NAT Pin
shahrul6s20-Nov-07 21:37
shahrul6s20-Nov-07 21:37 
GeneralRe: about the NAT Pin
gio_ch28-Jul-11 7:37
gio_ch28-Jul-11 7:37 
GeneralMissing the point Pin
Alex_112-Aug-06 16:12
Alex_112-Aug-06 16:12 
GeneralRe: Missing the point Pin
Hossein Ghahvei Araghi16-Aug-06 0:33
Hossein Ghahvei Araghi16-Aug-06 0:33 
GeneralRe: Missing the point Pin
Alex_119-Aug-06 21:24
Alex_119-Aug-06 21:24 
GeneralRe: It's just for you Pin
Hossein Ghahvei Araghi21-Aug-06 1:00
Hossein Ghahvei Araghi21-Aug-06 1:00 
GeneralStill Missing the Point Pin
Alex_123-Aug-06 0:00
Alex_123-Aug-06 0:00 
Congratulations eraghi,

You just changed the communication model even without noticing that.

foreach (Delegate del in invocationList_)
{
messageDelegate = (MessageHandler)del;
WrapperDelegate wrDel = new WrapperDelegate(BeginSend);
AsyncCallback callback = new AsyncCallback(EndSend);
wrDel.BeginInvoke(message, messageDelegate, callback, wrDel);
}

Your communication from the server to the client became 100% asynchronous, exactly what I recommended (see the message #1). Do you want to know why? You just invalidated all “out” and “ref “ parameters on your delegate, also the return type of your delegate can not be other than “void”. Why? Because the call of the delegate returns before the actual execution(on the remote client) takes place. All parameters other than “in” are invalid.

Oops!

You have
public delegate void MessageHandler(string message);
Consider this:

public delegate string MessageHandler(ref string message, out int Cnt);

This will not work in your program.
Does it still look like a true remoting? If you want to have all types of the parameters [remoting style] , you have to run these calls synchronously in the separate threads.
See my message #1. If you follow these recommendations and eventually manage to create sort of working framework (not just a flimsy prototype), you (hopefully)
will get a free version of GenuineChannels – the framework based on events.
I don’t want to discuss the advantages and drawbacks of this framework. You can do that
with the creator of GenuineChannels. Anyway it is getting a bit boring to point to all your
mistakes. You should do that yourself. Create the client simulator, the server and the network simulator. Run it and see how it performs. Without that, having just one client and the server you will never achieve what you are claiming.
You keep changing your RemotingEvent_SRC_Asynchronous.ZIP without any explanation and any admittance that previous version (though the same as the current) was bad.
However this article as a tutorial on how to use Remoting in not traditional manner is OK.

GeneralInstructive points Pin
Hossein Ghahvei Araghi23-Aug-06 20:04
Hossein Ghahvei Araghi23-Aug-06 20:04 
GeneralRe: Instructive points Pin
Alex_123-Aug-06 23:54
Alex_123-Aug-06 23:54 
GeneralAbout arrogance and professionalism Pin
Kfir.C12-Sep-07 1:41
Kfir.C12-Sep-07 1:41 
QuestionWas anyone successful making work in v1.1 Pin
Sing Abend10-Aug-06 5:50
professionalSing Abend10-Aug-06 5:50 
AnswerRe: Was anyone successful making work in v1.1 Pin
Hossein Ghahvei Araghi11-Aug-06 19:47
Hossein Ghahvei Araghi11-Aug-06 19:47 
AnswerRe: Was anyone successful making work in v1.1 Pin
mihir250812-May-08 13:04
mihir250812-May-08 13:04 
GeneralGood Pin
Obaid ur Rehman8-Aug-06 7:10
Obaid ur Rehman8-Aug-06 7:10 
GeneralRe: Good Pin
Hossein Ghahvei Araghi8-Aug-06 21:44
Hossein Ghahvei Araghi8-Aug-06 21:44 
GeneralSingleton versus SingleCall mode Pin
Henk van der Geld8-Aug-06 0:26
Henk van der Geld8-Aug-06 0:26 
AnswerRe: Singleton versus SingleCall mode Pin
Obaid ur Rehman8-Aug-06 7:04
Obaid ur Rehman8-Aug-06 7:04 
GeneralTo make it even better... Pin
mav.northwind6-Aug-06 20:38
mav.northwind6-Aug-06 20:38 
GeneralRe: To make it even better... Pin
Hossein Ghahvei Araghi6-Aug-06 21:10
Hossein Ghahvei Araghi6-Aug-06 21:10 
GeneralRe: To make it even better... Pin
mav.northwind6-Aug-06 23:35
mav.northwind6-Aug-06 23:35 
GeneralRe: To make it even better... Pin
Hossein Ghahvei Araghi6-Aug-06 23:55
Hossein Ghahvei Araghi6-Aug-06 23:55 
GeneralRe: To make it even better... Pin
mav.northwind7-Aug-06 6:39
mav.northwind7-Aug-06 6:39 
GeneralRe: To make it even better... Pin
Hossein Ghahvei Araghi7-Aug-06 0:34
Hossein Ghahvei Araghi7-Aug-06 0:34 
GeneralCool :) Pin
leppie6-Aug-06 8:26
leppie6-Aug-06 8:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.