WCF 客户端服务器推送( 使用 MSMQ)






4.88/5 (6投票s)
使用 MSMQ 和 WCF 的客户端服务器内部推送技术
引言
寻找一个使用 WCF 的简单服务器推送技术解决方案曾经很麻烦。
我有一些要求
- 必须使用 WCF
- 当消息被传输时,服务器和客户端应用程序不必运行
- 服务器端所有客户端的公共队列
- 需要服务器推送 - 不能使用轮询
IIS Web服务是一个选项,但用一个公共客户端队列实现起来很复杂。
使用 Microsoft 消息队列系统找到了一个解决方案。 客户端和服务器都有一个消息框用于通信。这两个应用程序在向对方发送消息时不必运行。
注意
本项目的目的不是为了解释绑定、契约和服务。 而是为了提供一个简单的服务器推送设计概述。
要求
- 安装 MSMQ (服务器和客户端) - 转到“控制面板” -> “添加删除程序” -> “添加 Windows 组件”,然后选择“消息队列”
- 以管理员身份运行 Visual Studio。
- 客户端和服务器必须能够互相看到(使用 IP 或主机名相互 ping)。 如果您需要外部通信支持 - 请参阅“公共通信的修改”部分。
概述

公共通信的修改
如果需要 Web服务
- 用于外部互联网通信,可以创建一个包装器,提供一个 Web服务
端点并创建一个内部队列消息。 这将消除客户端和服务器相互看到彼此消息队列的要求。
Using the Code
流程如下
客户端通过向服务器提供其自己的 MSMQ 地址来注册自己。
RegisterClientServiceClient registerClientServiceClient =
new RegisterClientServiceClient("RegisterClientEndPoint");
using(TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
ClientInfo clientInfo = new ClientInfo();
clientInfo.GroupName = ConfigurationManager.AppSettings["GroupName"];
clientInfo.ClientName = System.Net.Dns.GetHostName();
// ClientName can be changed to ip address
clientInfo.PushMessageBoxAddress = "net.msmq://" +
clientInfo.ClientName + "/private/WcfServerPush/ClientMessageBox";
registerClientServiceClient.RegisterClient(clientInfo);
scope.Complete();
}
RegisterClientServiceClient
类派生自 RegisterClientIService
并使用 ClientBase
模板。 这是常规的 WCF 实现。
public class RegisterClientServiceClient :
ClientBase<RegisterClientIService>, RegisterClientIService
{
public RegisterClientServiceClient(string endpointConfigurationName) :
base(endpointConfigurationName)
{
}
public void RegisterClient(ClientInfo clientInfo)
{
base.Channel.RegisterClient(clientInfo);
}
}
在服务器端,RegisterClient
实现为一个常规操作,它派生自相同的接口服务 RegisterClientIService
public class RegisterClientService : RegisterClientIService
{
[OperationBehavior(TransactionScopeRequired =
true, TransactionAutoComplete = true)]
public void RegisterClient(ClientInfo clientInfo)
{
WcfServerPushServerBO.Instance.RegisterClient(clientInfo);
}
}
在服务器端创建 MSMQ ServiceHost
很容易
if (!MessageQueue.Exists(registerClientMSMQName))
{
MessageQueue versionManagerQueue =
MessageQueue.Create(registerClientMSMQName, true);
versionManagerQueue.SetPermissions
(@"Everyone", MessageQueueAccessRights.FullControl);
versionManagerQueue.SetPermissions(@"ANONYMOUS LOGON",
MessageQueueAccessRights.ReceiveMessage |
MessageQueueAccessRights.PeekMessage |
MessageQueueAccessRights.WriteMessage);
}
客户端几乎与服务器端相同,但有一个小的修改。 因为我们希望 WPF 能够使用 ServiceHost
注册一个事件 - 每当收到新消息时,我们需要创建实际的服务并将其实例提供给 ServiceHost
。 每当我们向 ServiceHost
提供服务的实例时,我们必须向行为添加 InstanceConextMode.Single
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class PushMessageService : MessageBoxIService, INotifyPropertyChanged
最后,我们需要修改 *App.Config* 来定义服务。 我们还可以为端点提供 http 绑定(不仅仅是 netMsmqBinding
) - 如果将来我们想提供元数据交换能力。
<service name="WcfServerPushServer.RegisterClientService"
behaviorConfiguration="WcfServerPushServer.RegisterClientServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="https://:8001/WcfServerPush/
RegisterClientServiceHttp/" />
</baseAddresses>
</host>
<endpoint address="net.msmq:///private/WcfServerPush/RegisterClientService"
binding="netMsmqBinding"
bindingConfiguration="srmpBinding"
contract="WcfServerPushIServices.RegisterClientIService">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
客户端需要按如下方式识别端点
<client>
<endpoint name="RegisterClientEndPoint"
address="net.msmq:///private/WcfServerPush/RegisterClientService"
binding="netMsmqBinding" bindingConfiguration="netMsmqBindingConfig"
contract="WcfServerPushIServices.RegisterClientIService"/>
</client>
解决方案架构
WcfServerPush
- 一个 WPF 项目,既充当客户端应用程序,又充当服务器应用程序。 您可以在任何一侧安装该应用程序,并自行决定运行什么。 WPF 项目还负责在未找到时创建消息队列并启动服务主机。 在实际的开发项目中 - 将视图与启动程序分离非常重要。WcfServerPushServer
- 服务器端服务和业务对象,包括客户端的队列WcfServerPushClient
- 客户端服务WcfServerPushIServices
- 消息框服务接口和注册客户端服务接口。 服务器端和客户端的公共项目。
历史
- 2011年2月6日:首次发布