我有两个进程,第二个进程需要在第一个进程中访问单例。所以我写了一个应该帮助共享实例的服务器。
但是有些事情是错误的,似乎客户端获得了自己的单例版本而不是原始实例。
最小的例子有两个项目。这是客户:
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;
}
}
}
示例运行,这是我系统上的输出:
为什么我没有从服务器进程获取Singleton。 为什么我的事件没有在服务器中引发。
编辑:服务器打开主机并订阅单例。客户端连接后,它通过成员函数引发事件。 Event有两个订阅者,一个在构造函数中,另一个在服务器端。只处理Module内部订阅 - 服务器端没有事件处理 - 服务器端没有触发任何事件。模块侦听器被触发但不在主机进程内。此事件在客户端处理。
答案 0 :(得分:2)
为什么我没有从服务器进程中获取我的Singleton
命名管道使用Serialization将对象从服务器传递到客户端。这意味着客户端必须重新运行构造函数并复制现有属性
public string id;
是一个字段,因此无法复制&#39;所以保留了构造函数设置的随机值。这就是为什么你有不同的ID为&#34;相同&#34;宾语。
要解决此问题,您可以将其更改为:
[DataContract]
public class Module
{
[DataMember]
public string id {get; set;}
}
为什么我的事件没有在服务器中引发。
这不是WCF命名管道的工作方式,因为您只有客户端的重复版本。我建议你阅读Duplex Channel