WCF 消息队列通信实现

在开发分布式应用程序时,经常会遇到需要在客户端和服务端之间进行异步通信的场景。Windows Communication Foundation (WCF) 提供了强大的服务端推送功能,但实现起来可能会有些复杂。本文将介绍一种使用 WCF 和 Microsoft Message Queuing (MSMQ) 实现服务端推送的方法。

需求概述

需求如下:

  • 必须使用WCF
  • 客户端和服务端在消息传输时不需要同时运行。
  • 服务端需要有一个公共队列供所有客户端使用。
  • 需要服务端推送,不能使用轮询。

虽然 IIS WebService 是一个选项,但实现起来相对复杂,特别是需要一个公共客户端队列。

解决方案

找到了一个解决方案,即使用 Microsoft Message Queuing 系统。客户端和服务端都有一个消息队列,用于相互通信。双方在发送消息时不需要运行对方。

环境准备

为了实现这个解决方案,需要满足以下条件:

  • 在服务器和客户端上安装 MSMQ。可以通过控制面板 -> 程序和功能 -> 启用或关闭 Windows 功能,然后选择“消息队列”来安装。
  • 以管理员身份运行 Visual Studio。
  • 客户端和服务端能够相互通信(可以使用 IP 或主机名进行 ping 操作)。如果需要外部通信支持,请参考“公共通信修改”部分。

公共通信修改

如果需要 WebService 进行外部互联网通信,可以创建一个包装器,它提供 WebService 端点并创建内部队列消息。这样可以消除客户端和服务端需要看到对方消息队列的要求。

代码实现

客户端和服务端的通信流程如下:

  1. 客户端通过提供自己的 MSMQ 地址向服务器注册。
  2. 服务器端实现 RegisterClient 操作,该操作继承自相同的接口服务 RegisterClientIService。
  3. 在服务器端创建MSMQServiceHost。
  4. 客户端和服务端几乎相同,但客户端需要注册事件,以便在接收到新消息时通知 ServiceHost。

客户端通过以下代码注册:

C# 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(); clientInfo.PushMessageBoxAddress = "net.msmq://" + clientInfo.ClientName + "/private/WcfServerPush/ClientMessageBox"; registerClientServiceClient.RegisterClient(clientInfo); scope.Complete(); }

RegisterClientServiceClient 类继承自 RegisterClientIService,并使用 ClientBase 模板。这是标准的WCF实现。

服务器端的 RegisterClient 实现如下:

C# public class RegisterClientService : RegisterClientIService { [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] public void RegisterClient(ClientInfo clientInfo) { WcfServerPushServerBO.Instance.RegisterClient(clientInfo); } }

在服务器端创建 MSMQ ServiceHost 非常简单:

C# 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。

C# [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] public class PushMessageService : MessageBoxIService, INotifyPropertyChanged

配置文件

需要修改 App.Config 来定义服务。还可以给端点提供一个 http 绑定(不仅仅是 netMsmqBinding),以便将来提供元数据交换能力。

XML <service name="WcfServerPushServer.RegisterClientService" behaviorConfiguration="WcfServerPushServer.RegisterClientServiceBehavior"> <host> <baseAddresses> <add baseAddress="http://localhost:8001/WcfServerPush/RegisterClientServiceHttp/" /> </baseAddresses> </host> <endpoint address="net.msmq://localhost/private/WcfServerPush/RegisterClientService" binding="netMsmqBinding" bindingConfiguration="srmpBinding" contract="WcfServerPushIServices.RegisterClientIService" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service>

客户端需要识别端点:

XML <client> <endpoint name="RegisterClientEndPoint" address="net.msmq://localhost/private/WcfServerPush/RegisterClientService" binding="netMsmqBinding" bindingConfiguration="netMsmqBindingConfig" contract="WcfServerPushIServices.RegisterClientIService" /> </client>

解决方案架构

WcfServerPush - 一个 WPF 项目,既可以作为客户端也可以作为服务器应用程序。可以在任一侧安装应用程序,并自行决定运行什么。WPF 项目还负责创建消息队列(如果未找到)并启动服务主机。在实际开发项目中,将视图与引导程序分离非常重要。

WcfServerPushServer - 服务器端服务和业务对象,包括客户端的队列。

WcfServerPushClient - 客户端服务。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485