服务通道级编程

本主题介绍如何在不使用 System.ServiceModel.ServiceHost 及其关联的对象模型的情况下编写 Windows Communication Foundation (WCF) 服务应用程序。

接收消息

若要准备接收并处理消息,必须执行下列步骤:

  1. 创建绑定。

  2. 生成通道侦听器。

  3. 打开通道侦听器。

  4. 读取请求并发送答复。

  5. 关闭所有通道对象。

创建绑定

侦听并接收消息的第一步是创建绑定。WCF 附带有若干内置绑定或系统提供的绑定,可以通过对其中之一进行实例化来直接使用它们。另外,您还可以创建自己的自定义绑定,方法是实例化 CustomBinding 类,这同时也是列表 1 中的代码所执行的操作。

下面的代码示例创建了 System.ServiceModel.Channels.CustomBinding 的一个实例,并将 System.ServiceModel.Channels.HttpTransportBindingElement 添加到其 Elements 集合(这是用于生成通道堆栈的绑定元素的集合)。在此示例中,由于元素集合只有 HttpTransportBindingElement,所以生成的通道堆栈仅具有 HTTP 传输通道。

生成 ChannelListener

在创建绑定后,我们调用 System.ServiceModel.Channels.Binding.BuildChannelListener.Uri,System.ServiceModel.Channels.BindingParameterCollection) 来生成通道侦听器,其中类型参数就是要创建的通道形状。在此示例中使用 System.ServiceModel.Channels.IReplyChannel,这是因为我们希望以请求/答复消息交换模式侦听传入的消息。

IReplyChannel 用于接收请求消息,并发回答复消息。调用 System.ServiceModel.Channels.IReplyChannel.ReceiveRequest 会返回一个 System.ServiceModel.Channels.IRequestChannel,可以用于接收请求消息以及发回答复消息。

当创建侦听器时,我们传递发生侦听的网络地址,在此示例中为 https://localhost:8080/channelapp。通常,每个传输通道支持一个或可能多个地址方案,例如,HTTP 传输对 http 和 https 方案均予以支持。

在创建侦听器时,我们还传递空的 System.ServiceModel.Channels.BindingParameterCollection。绑定参数是一种机制,用于传递那些控制侦听器生成方式的参数。在此示例中,我们不使用任何此类参数,所以我们传递的是一个空集合。

侦听传入消息

然后,我们对侦听器调用 System.ServiceModel.ICommunicationObject.Open,开始接受通道。System.ServiceModel.Channels.IChannelListener.AcceptChannel 的行为取决于传输是面向连接还是与连接无关。对于面向连接的传输,AcceptChannel 会一直阻止,直到新的连接请求传入(此时它会返回一个表示该新连接的新通道)。对于与连接无关的传输(如 HTTP),AcceptChannel 会立即返回传输侦听器创建的唯一通道。

在此示例中,侦听器返回一个实现 IReplyChannel 的通道。为了在此通道上接收消息,我们首先对其调用 System.ServiceModel.ICommunicationObject.Open,以便将其置于一个准备进行通信的状态。然后,我们调用 ReceiveRequest,它会处于阻止状态,直到消息达到。

读取请求并发送答复

ReceiveRequest 返回一个 RequestContext,我们使用其 RequestMessage 属性获取收到的消息。我们写出消息的操作和正文内容(假设它是一个字符串)。

为了发送答复,我们在此例中创建一个新的答复消息,它会将我们在请求中收到的字符串数据传递回去。然后,我们调用 Reply 以发送答复消息。

关闭对象

为避免泄漏资源,很重要的一点就是关闭通信中不再需要使用的对象。在此示例中,我们关闭了请求消息、请求上下文、通道和侦听器。

下面的代码示例演示通道侦听器仅接收一条消息所用的基本服务。实际的服务会一直接受通道及接收消息,直到服务退出。

using System;
using System.ServiceModel.Channels;
namespace ProgrammingChannels
{
class Service
{
static void RunService()
{
    //Step1: Create a custom binding with just TCP.
    BindingElement[] bindingElements = new BindingElement[2];
    bindingElements[0] = new TextMessageEncodingBindingElement();
    bindingElements[1] = new HttpTransportBindingElement();

    CustomBinding binding = new CustomBinding(bindingElements);


    //Step2: Use the binding to build the channel listener.         
    IChannelListener<IReplyChannel> listener =
          binding.BuildChannelListener<IReplyChannel>(
             new Uri("https://localhost:8080/channelapp"),
           new BindingParameterCollection());

    //Step3: Listening for messages.
    listener.Open();
    Console.WriteLine(
           "Listening for incoming channel connections");
    //Wait for and accept incoming connections.
    IReplyChannel channel = listener.AcceptChannel();
    Console.WriteLine("Channel accepted. Listening for messages");
    //Open the accepted channel.
    channel.Open();
    //Wait for and receive a message from the channel.
    RequestContext request= channel.ReceiveRequest();
    //Step4: Reading the request message.
    Message message = request.RequestMessage;
    Console.WriteLine("Message received");
    Console.WriteLine("Message action: {0}",
                          message.Headers.Action);
    string data=message.GetBody<string>();
    Console.WriteLine("Message content: {0}",data);
    //Send a reply.
    Message replymessage=Message.CreateMessage(
        binding.MessageVersion, 
        "https://contoso.com/someotheraction", 
         data);
    request.Reply(replymessage);
    //Step5: Closing objects.
    //Do not forget to close the message.
    message.Close();
    //Do not forget to close RequestContext.
    request.Close();
    //Do not forget to close channels.
    channel.Close();
    //Do not forget to close listeners.
    listener.Close();
}
public static void Main()
{
    Service.RunService();
    Console.WriteLine("Press enter to exit");
    Console.ReadLine();
}
}
}