此存储库显示了我为回答类似问题而制作的双工 WCF 实现,它是一个完整的工作示例,尽可能少地添加额外内容。
https://github.com/Aelphaeis/MyWcfDuplexPipeExample
假设我们有这样的服务合同:
[ServiceContract(CallbackContract = typeof(IMyServiceCallback),SessionMode = SessionMode.Required)]
public interface IMyService
{
[OperationContract(IsOneWay=true)]
void DoWork();
}
请注意,我指定了 CallbackContract。
如果您想进行双工,您可能希望像这样实现上述合同的服务行为:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyService : IMyService
{
public void DoWork()
{
Console.WriteLine("Hello World");
Callback.WorkComplete();
}
IMyServiceCallback Callback
{
get
{
return OperationContext.Current.GetCallbackChannel<IMyServiceCallback>();
}
}
}
这里重要的是回调。这就是您的服务允许您访问客户端指定给您的方式。
你还需要定义回调接口,在我的例子中它很简单:
[ServiceContract]
public interface IMyServiceCallback
{
[OperationContract(IsOneWay = true)]
void WorkComplete();
}
现在我想创建一个客户端来使用这个双工服务。我需要做的第一件事是实现 IMyServiceCallback。我需要在客户端上执行此操作。在这种情况下,实现是这样的:
class Callback : IMyServiceCallback
{
public void WorkComplete()
{
Console.WriteLine("Work Complete");
}
}
现在,当我想打开与服务的双工连接时,我会创建一个类似这样的代理类:
public class MyServiceClient: IMyService, IDisposable
{
DuplexChannelFactory<IMyService> myServiceFactory { get; set; }
public MyServiceClient(IMyServiceCallback Callback)
{
InstanceContext site = new InstanceContext(Callback);
NetNamedPipeBinding binding = new NetNamedPipeBinding();
EndpointAddress endpointAddress = new EndpointAddress(Constants.myPipeService + @"/" + Constants.myPipeServiceName);
myServiceFactory = new DuplexChannelFactory<IMyService>(site, binding, endpointAddress);
}
public void DoWork()
{
myServiceFactory.CreateChannel().DoWork();
}
public void Dispose()
{
myServiceFactory.Close();
}
}
请注意,我指定了一个 InstanceContext。该实例上下文将是我创建的实现 IMyServiceCallback 的对象的实例。
这就是你需要做的!就这么简单!
更新:
回调对象就像任何其他对象一样。您可以将它们存储到一个集合中并根据某些条件遍历它们。
一种方法是在 IMyServiceCallback 中创建一个可以唯一标识它的属性。当客户端连接到服务时,它可以调用一个方法,该方法指定一个回调对象,然后可以缓存或保存以供以后使用。然后,您可以迭代回调,并根据某些条件调用特定客户端的方法。
这当然更复杂;但是,它肯定是可以管理的。稍后我会添加一个示例。
更新 2
这是您想要的一个工作示例;但是,它要复杂得多。我会尽可能简单地解释:https://github.com/Aelphaeis/MyWcfDuplexPipeExample/tree/MultiClient
以下是更改列表:
- 我已修改客户端代理(和服务),以便在初始化时调用 init 方法
- 我还修改了 Service 实现,现在它是一个处理所有请求的单个实例(为了方便起见)。
- 我在Service接口中添加了一个新的OperationContract,叫做Msg
- 我在 IMyServiceCallback 中添加了一个名为 RecieveMessage 的新方法。
- 我添加了一种识别客户端的方法。
在代理类中我有以下内容:
public MyServiceClient(IMyServiceCallback Callback)
{
InstanceContext site = new InstanceContext(Callback);
NetNamedPipeBinding binding = new NetNamedPipeBinding();
EndpointAddress endpointAddress = new EndpointAddress(Constants.myPipeService + @"/" + Constants.myPipeServiceName);
myServiceFactory = new DuplexChannelFactory<IMyService>(site, binding, endpointAddress);
Init();
}
public void Init()
{
myServiceFactory.CreateChannel().Init();
}
在我的服务中,我有以下几点:
public class MyService : IMyService
{
public List<IMyServiceCallback> Callbacks { get; private set; }
public MyService(){
Callbacks = new List<IMyServiceCallback>();
}
public void Init()
{
Callbacks.Add(Callback);
}
// and so on
我的 IMyServiceCallback 已重新定义为:
[ServiceContract]
public interface IMyServiceCallback
{
[OperationContract]
int GetClientId();
[OperationContract(IsOneWay = true)]
void WorkComplete();
[OperationContract(IsOneWay = true)]
void RecieveMessage(String msg);
}
通过指定号码,您可以联系与该号码对应的客户。如果两个客户具有相同的 ID,则会联系这两个客户。