65.9K
CodeProject 正在变化。 阅读更多。
Home

远程事件(适用于企业解决方案的简单高效)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (23投票s)

2006年8月6日

CPOL

2分钟阅读

viewsIcon

196316

downloadIcon

2160

本文包含最简单的解决方案,用于解决以下问题:DelegateSerializationHolder 的安全问题、IO 问题和消息传递速度问题。 注意:消息传递速度问题将在应用程序运行很长时间后出现。

demo project Image

背景

我一直对套接字编程感兴趣。 我创建了几个聊天应用程序和基于复杂套接字的应用程序。 当我了解 Remoting 中的事件时,我很高兴,我想到的第一件事就是创建一个聊天应用程序。 但在测试中,我遇到了一些问题。

问题 1

第一个问题是安全异常

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

解决方案 1

通过将 typeFilterLevel 属性(值为 Full)添加到两个配置文件的 formatter 元素,可以解决此问题。

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

问题 2

但是,它再次不起作用,并发生 IO 异常

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.

当在服务器端反序列化请求时,会发生此异常。 服务器尝试调用事件处理程序,而事件处理程序存在于客户端程序集中。 因为服务器上没有客户端程序集,所以会引发该异常。

IO exception Image

解决方案 2

一个中间包装器类 MarshalByRefObject 将解决这个问题。 这个包装器类位于共享程序集中,客户端和服务器都可以访问;因此,委托可以解析方法的签名。 在客户端应用程序中,我们将共享对象事件与包装器类的 WrapperMessageReceivedHandler 方法关联,并将客户端上的事件处理程序与包装器类的 MessageReceived 事件关联。

Sequense of events Image

为什么使用 [OneWay] 属性

如果不将远程方法定义为 [OneWay],则当客户端无法访问或已断开连接而未取消关联事件处理程序时,将发生异常。 通过使用 [OneWay],客户端上不会发生异常,但它将位于服务器的调用列表中,并且长时间运行会使您的服务器响应速度变慢。

解决方案 3

您必须自己调用每个委托,而不是使用正常的事件调用机制,并且如果发生异常,您必须从事件的调用列表中取消关联该委托。 最后,您可以删除 [OneWay] 属性。

共享类

/// <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;
                        }
                    }
                }
            }
        }
    }

客户端应用程序

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), 
                   "https://: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);
}

包装器类

/// <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);
}
© . All rights reserved.