双向通信
双向示例演示如何通过 MSMQ 执行事务处理双向排队通信。 本示例使用 netMsmqBinding
绑定。 在本例中,服务是一个自承载的控制台应用程序,通过它可以观察服务接收排队消息。
注意
本主题的最后介绍了此示例的设置过程和生成说明。
该示例基于事务处理 MSMQ 绑定。
在排队通信中,客户端使用队列与服务进行通信。 客户端向队列发送消息,服务从队列接收消息。 因此不必同时运行服务和客户端便可使用队列进行通信。
本示例演示使用队列的双向通信。 客户端从事务范围内向队列发送采购订单。 服务接收订单、处理订单,然后在事务范围内使用队列中订单的状态回调客户端。 为了便于双向通信,客户端和服务都使用队列以便将采购订单和订单状态排入队列。
服务协定 IOrderProcessor
定义适合使用队列的单向服务操作。 服务操作包括用于向其发送订单状态的答复终结点。 答复终结点是用于将订单状态发回客户端的队列的 URI。 订单处理应用程序实现下面的协定。
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")]
public interface IOrderProcessor
{
[OperationContract(IsOneWay = true)]
void SubmitPurchaseOrder(PurchaseOrder po, string
reportOrderStatusTo);
}
用于发送订单状态的答复协定由客户端指定。 客户端实现订单状态协定。 服务使用下面协定的生成的代理将订单状态发回客户端。
[ServiceContract]
public interface IOrderStatus
{
[OperationContract(IsOneWay = true)]
void OrderStatus(string poNumber, string status);
}
服务操作处理提交的采购订单。 对服务操作应用 OperationBehaviorAttribute 以在用于从队列中接收消息的事务中指定自动登记,并指定在服务操作完成时事务自动完成。 Orders
类封装了订单处理功能。 在本例中,它将采购订单添加到字典。 Orders
类中的操作可以使用服务操作登记的事务。
服务操作除了处理提交的采购订单之外,还向客户端答复有关订单状态的信息。
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void SubmitPurchaseOrder(PurchaseOrder po, string reportOrderStatusTo)
{
Orders.Add(po);
Console.WriteLine("Processing {0} ", po);
Console.WriteLine("Sending back order status information");
NetMsmqBinding msmqCallbackBinding = new NetMsmqBinding("ClientCallbackBinding");
OrderStatusClient client = new OrderStatusClient(msmqCallbackBinding, new EndpointAddress(reportOrderStatusTo));
// Please note that the same transaction that is used to dequeue the purchase order is used
// to send back order status.
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
client.OrderStatus(po.PONumber, po.Status);
scope.Complete();
}
//Close the client.
client.Close();
}
MSMQ 队列名称是在配置文件的 appSettings 节中指定的。 服务的终结点是在配置文件的 System.ServiceModel 节中定义的。
注意
MSMQ 队列名称和终结点地址使用略有不同的寻址约定。 MSMQ 队列名称为本地计算机使用圆点 (.),并在其路径中使用反斜杠分隔符。 Windows Communication Foundation (WCF) 终结点地址指定一个 net.msmq: 方案,使用“localhost”来表示本地计算机,并在其路径中使用正斜杠。 若要从在远程计算机上承载的队列读取数据,请将“.”和“localhost”替换为远程计算机名称。
服务是自承载服务。 使用 MSMQ 传输时,必须提前创建所使用的队列。 可以手动或通过代码完成此操作。 在此示例中,该服务检查队列是否存在并在必要时创建队列。 从配置文件中读取队列名称。 ServiceModel 元数据实用工具 (Svcutil.exe) 使用基址生成服务代理。
// Host the service within this EXE console application.
public static void Main()
{
// Get MSMQ queue name from appSettings in configuration.
string queueName = ConfigurationManager.AppSettings["queueName"];
// Create the transacted MSMQ queue if necessary.
if (!MessageQueue.Exists(queueName))
MessageQueue.Create(queueName, true);
// Create a ServiceHost for the OrderProcessorService type.
using (ServiceHost serviceHost = new ServiceHost(typeof(OrderProcessorService)))
{
// Open the ServiceHost to create listeners and start listening for messages.
serviceHost.Open();
// The service can now be accessed.
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
}
}
客户端创建事务。 与队列的通信在事务范围内进行,从而可以将事务范围视为所有消息在其中成功或失败的原子单元。
// Create a ServiceHost for the OrderStatus service type.
using (ServiceHost serviceHost = new ServiceHost(typeof(OrderStatusService)))
{
// Open the ServiceHostBase to create listeners and start listening for order status messages.
serviceHost.Open();
// Create the purchase order.
...
// Create a client with given client endpoint configuration.
OrderProcessorClient client = new OrderProcessorClient("OrderProcessorEndpoint");
//Create a transaction scope.
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
string hostName = Dns.GetHostName();
// Make a queued call to submit the purchase order.
client.SubmitPurchaseOrder(po, "net.msmq://" + hostName + "/private/ServiceModelSamplesTwo-way/OrderStatus");
// Complete the transaction.
scope.Complete();
}
//Close down the client.
client.Close();
Console.WriteLine();
Console.WriteLine("Press <ENTER> to terminate client.");
Console.ReadLine();
// Close the ServiceHost to shutdown the service.
serviceHost.Close();
}
客户端代码实现 IOrderStatus
协定以便从服务接收订单状态。 在本例中,它输出订单状态。
[ServiceBehavior]
public class OrderStatusService : IOrderStatus
{
[OperationBehavior(TransactionAutoComplete = true,
TransactionScopeRequired = true)]
public void OrderStatus(string poNumber, string status)
{
Console.WriteLine("Status of order {0}:{1} ", poNumber ,
status);
}
}
订单状态队列在 Main
方法中创建。 客户端配置包括订单状态服务配置,以便承载订单状态服务,如下面的示例配置所示。
<appSettings>
<!-- Use appSetting to configure MSMQ queue name. -->
<add key="queueName" value=".\private$\ServiceModelSamplesTwo-way/OrderStatus" />
</appSettings>
<system.serviceModel>
<services>
<service
name="Microsoft.ServiceModel.Samples.OrderStatusService">
<!-- Define NetMsmqEndpoint -->
<endpoint address="net.msmq://localhost/private/ServiceModelSamplesTwo-way/OrderStatus"
binding="netMsmqBinding"
contract="Microsoft.ServiceModel.Samples.IOrderStatus" />
</service>
</services>
<client>
<!-- Define NetMsmqEndpoint -->
<endpoint name="OrderProcessorEndpoint"
address="net.msmq://localhost/private/ServiceModelSamplesTwo-way/OrderProcessor"
binding="netMsmqBinding"
contract="Microsoft.ServiceModel.Samples.IOrderProcessor" />
</client>
</system.serviceModel>
运行示例时,客户端和服务活动将显示在服务和客户端控制台窗口中。 您可以看到服务从客户端接收消息。 在每个控制台窗口中按 Enter 可以关闭服务和客户端。
服务显示采购订单信息并指示正在将订单状态发回订单状态队列。
The service is ready.
Press <ENTER> to terminate service.
Processing Purchase Order: 124a1f69-3699-4b16-9bcc-43147a8756fc
Customer: somecustomer.com
OrderDetails
Order LineItem: 54 of Blue Widget @unit price: $29.99
Order LineItem: 890 of Red Widget @unit price: $45.89
Total cost of this order: $42461.56
Order status: Pending
Sending back order status information
客户端显示由服务发送的订单状态信息。
Press <ENTER> to terminate client.
Status of order 124a1f69-3699-4b16-9bcc-43147a8756fc:Pending
设置、生成和运行示例
若要生成 C# 或 Visual Basic .NET 版本的解决方案,请按照 Building the Windows Communication Foundation Samples中的说明进行操作。
要使用单机配置或跨计算机配置来运行示例,请按照运行 Windows Communication Foundation 示例中的说明进行操作。
注意
如果使用 Svcutil.exe 为该示例重新生成配置,请确保在客户端配置中修改终结点名称以与客户端代码匹配。
默认情况下使用 NetMsmqBinding 启用传输安全。 MSMQ 传输安全性有两个相关的属性,即 MsmqAuthenticationMode 和 MsmqProtectionLevel.
。默认情况下,身份验证模式设置为 Windows
,保护级别设置为 Sign
。 为了使 MSMQ 提供身份验证和签名功能,MSMQ 必须是域的一部分,并且必须安装 MSMQ 的 Active Directory 集成选项。 如果在不满足这些条件的计算机上运行此示例,将会收到错误。
在加入到工作组或在没有 Active Directory 集成的计算机上运行示例
如果计算机不是域成员或尚未安装 Active Directory 集成,请将身份验证模式和保护级别设置为
None
以关闭传输安全性,如下面的示例配置所示:<configuration> <appSettings> <!-- Use appSetting to configure MSMQ queue name. --> <add key="queueName" value=".\private$\ServiceModelSamplesTwo-way/OrderProcessor" /> </appSettings> <system.serviceModel> <services> <service name="Microsoft.ServiceModel.Samples.OrderProcessorService"> <!-- Define NetMsmqEndpoint --> <endpoint address="net.msmq://localhost/private/ServiceModelSamplesTwo-way/OrderProcessor" binding="netMsmqBinding" bindingConfiguration="TransactedBinding" contract="Microsoft.ServiceModel.Samples.IOrderProcessor" /> </service> </services> <bindings> <netMsmqBinding> <binding name="TransactedBinding" > <security mode="None" /> </binding> </netMsmqBinding> </bindings> </system.serviceModel> </configuration>
关闭客户端配置的安全性将生成下面的内容:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <!-- Use appSetting to configure MSMQ queue name. --> <add key="queueName" value=".\private$\ServiceModelSamplesTwo-way/OrderStatus" /> </appSettings> <system.serviceModel> <services> <service name="Microsoft.ServiceModel.Samples.OrderStatusService"> <!-- Define NetMsmqEndpoint --> <endpoint address="net.msmq://localhost/private/ServiceModelSamplesTwo-way/OrderStatus" binding="netMsmqBinding" bindingConfiguration="TransactedBinding" contract="Microsoft.ServiceModel.Samples.IOrderStatus" /> </service> </services> <client> <!-- Define NetMsmqEndpoint --> <endpoint name="OrderProcessorEndpoint" address="net.msmq://localhost/private/ServiceModelSamplesTwo-way/OrderProcessor" binding="netMsmqBinding" bindingConfiguration="TransactedBinding" contract="Microsoft.ServiceModel.Samples.IOrderProcessor" /> </client> <bindings> <netMsmqBinding> <binding name="TransactedBinding" > <security mode="None" /> </binding> </netMsmqBinding> </bindings> </system.serviceModel> </configuration>
本示例中的服务在
OrderProcessorService
中创建一个绑定。 实例化该绑定后添加一行代码以将安全模式设置为None
。NetMsmqBinding msmqCallbackBinding = new NetMsmqBinding(); msmqCallbackBinding.Security.Mode = NetMsmqSecurityMode.None;
确保在运行示例前更改服务器和客户端上的配置。
注意
将
security mode
设置为None
等效于将 MsmqAuthenticationMode、MsmqProtectionLevel 或Message
安全设置为None
。