双向通信

双向示例演示如何通过 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

设置、生成和运行示例

  1. 请确保已执行 Windows Communication Foundation 示例的一次性安装过程

  2. 若要生成 C# 或 Visual Basic .NET 版本的解决方案,请按照 Building the Windows Communication Foundation Samples中的说明进行操作。

  3. 要使用单机配置或跨计算机配置来运行示例,请按照运行 Windows Communication Foundation 示例中的说明进行操作。

    注意

    如果使用 Svcutil.exe 为该示例重新生成配置,请确保在客户端配置中修改终结点名称以与客户端代码匹配。

默认情况下使用 NetMsmqBinding 启用传输安全。 MSMQ 传输安全性有两个相关的属性,即 MsmqAuthenticationModeMsmqProtectionLevel.。默认情况下,身份验证模式设置为 Windows,保护级别设置为 Sign。 为了使 MSMQ 提供身份验证和签名功能,MSMQ 必须是域的一部分,并且必须安装 MSMQ 的 Active Directory 集成选项。 如果在不满足这些条件的计算机上运行此示例,将会收到错误。

在加入到工作组或在没有 Active Directory 集成的计算机上运行示例

  1. 如果计算机不是域成员或尚未安装 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>
    
  2. 关闭客户端配置的安全性将生成下面的内容:

    <?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>
    
  3. 本示例中的服务在 OrderProcessorService 中创建一个绑定。 实例化该绑定后添加一行代码以将安全模式设置为 None

    NetMsmqBinding msmqCallbackBinding = new NetMsmqBinding();
    msmqCallbackBinding.Security.Mode = NetMsmqSecurityMode.None;
    
  4. 确保在运行示例前更改服务器和客户端上的配置。

    注意

    security mode 设置为None 等效于将 MsmqAuthenticationModeMsmqProtectionLevelMessage 安全设置为 None