通过命名管道访问Singleton会创建第二个单例。未在主机端触发的事件

时间:2017-02-15 10:28:50

标签: c# events singleton ipc named-pipes

我有两个进程,第二个进程需要在第一个进程中访问单例。所以我写了一个应该帮助共享实例的服务器。

但是有些事情是错误的,似乎客户端获得了自己的单例版本而不是原始实例。

最小的例子有两个项目。这是客户:

Program

using IPCServer;
using System;
using System.Diagnostics;
using System.ServiceModel;
using System.Threading;

namespace IPCEventTest
{
    class IPCClient
    {
        static void Main(string[] args)
        {
            Process ipcserver = Process.Start("IPCServer.exe");
            Thread.Sleep(2000);
            Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main Start Connect");
            Module module = Connect("WellKnownName"); //this name is used by the host
            Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main End Connect");
            Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main Start Raise");
            module.RaiseEvent(); //raise event should raise within the server process
            Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Main End Raise");
            while (true) ;
        }
        public static Module Connect(string id)
        {
            Console.WriteLine("Start Connect");
            ChannelFactory<IModuleServer> pipeFactory =
                  new ChannelFactory<IModuleServer>(
                    new NetNamedPipeBinding(),
                    new EndpointAddress($"net.pipe://localhost/{id}")
                    );
            IModuleServer serverProxy = pipeFactory.CreateChannel();
            Module ret = serverProxy.GetModule();
            Console.WriteLine("End Connect");
            return ret;
        }
    }
}

以下文件设置主机:

Program

using System;

namespace IPCServer
{
    class Program
    {
        static HOST host;

        static void Main(string[] args)
        {
            host = new HOST("WellKnownName");
            Module.Instance.myevent += Instance_myevent;
            Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Server Subscribed to {Module.Instance.id}");
            while (true) ;
        }

        private static void Instance_myevent(object sender, EventArgs e)
        {
            Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Server Event Fired from {(sender as Module).id}");
        }
    }
}

Module

using System;
using System.Linq;

namespace IPCServer
{
    public class Module
    {
        public static Module Instance { get; } = new Module();
        public event EventHandler myevent = delegate { };
        public string id;

        private Module()
        {
            var guid4 = Guid.NewGuid().ToString().Take(4);
            id = new String(guid4.ToArray());
            Console.WriteLine($"Module Constructor {id}");
            myevent += Module_myevent;
        }

        private void Module_myevent(object sender, EventArgs e)
        {
            Console.WriteLine($"Module Listener {(sender as Module).id}");
        }

        public void RaiseEvent()
        {
            Console.WriteLine($"Module Start Raise {id}");
            myevent(this, EventArgs.Empty);
            Console.WriteLine($"Module End Raise {id}");
        }
    }
}

Host

using System;
using System.ServiceModel;

namespace IPCServer
{
    internal class HOST
    {
        ServiceHost host;

        internal HOST(string id)
        {
            host = new ServiceHost(typeof(ModuleServer), new Uri[] { new Uri("net.pipe://localhost") });
            host.AddServiceEndpoint(typeof(IModuleServer), new NetNamedPipeBinding(), id);
            host.Open();
            Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Host Opened");
        }

        ~HOST()
        {
            if (host.State == CommunicationState.Opened)
            {
                host.Close();
            }
            host = null;
            Console.WriteLine($"{DateTime.Now.ToUniversalTime()} Host Destructed");
        }
    }

    [ServiceContract]
    public interface IModuleServer
    {
        [OperationContract]
        Module GetModule();
    }

    public class ModuleServer : IModuleServer
    {
        public Module GetModule()
        {
            Console.WriteLine($"{DateTime.Now.ToUniversalTime()} ModuleServer start GetModule");
            Module ret = Module.Instance;
            Console.WriteLine($"{DateTime.Now.ToUniversalTime()} ModuleServer end GetModule");
            return ret;
        }
    }
}

示例运行,这是我系统上的输出:

example output

为什么我没有从服务器进程获取Singleton。 为什么我的事件没有在服务器中引发。

编辑:服务器打开主机并订阅单例。客户端连接后,它通过成员函数引发事件。 Event有两个订阅者,一个在构造函数中,另一个在服务器端。只处理Module内部订阅 - 服务器端没有事件处理 - 服务器端没有触发任何事件。模块侦听器被触发但不在主机进程内。此事件在客户端处理。

1 个答案:

答案 0 :(得分:2)

  

为什么我没有从服务器进程中获取我的Singleton

命名管道使用Serialization将对象从服务器传递到客户端。这意味着客户端必须重新运行构造函数并复制现有属性 public string id;是一个字段,因此无法复制&#39;所以保留了构造函数设置的随机值。这就是为什么你有不同的ID为&#34;相同&#34;宾语。

要解决此问题,您可以将其更改为:

[DataContract]
public class Module
{
    [DataMember]
    public string id {get; set;}

}
  

为什么我的事件没有在服务器中引发。

这不是WCF命名管道的工作方式,因为您只有客户端的重复版本。我建议你阅读Duplex Channel