服务通道级编程

本主题介绍如何在不使用 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

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

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

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

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

侦听传入消息

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

在此示例中,侦听器返回一个实现 IReplyChannel 的通道。 为了在此通道上接收消息,我们首先对其调用 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("http://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,
        "http://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();
}
}
}
Imports System.ServiceModel.Channels

Namespace ProgrammingChannels
    Friend Class Service

        Private Shared Sub RunService()

            'Step1: Create a custom binding with just TCP.
            Dim bindingElements(1) As BindingElement = {New TextMessageEncodingBindingElement(), _
                                                        New HttpTransportBindingElement()}

            Dim binding As New CustomBinding(bindingElements)

            'Step2: Use the binding to build the channel listener.         
            Dim listener = binding.BuildChannelListener(Of IReplyChannel)(New Uri("http://localhost:8080/channelapp"), _
                                                                          New BindingParameterCollection())

            'Step3: Listening for messages.
            listener.Open()
            Console.WriteLine("Listening for incoming channel connections")

            'Wait for and accept incoming connections.
            Dim 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.
            Dim request = channel.ReceiveRequest()

            'Step4: Reading the request message.
            Dim message = request.RequestMessage
            Console.WriteLine("Message received")
            Console.WriteLine("Message action: {0}", message.Headers.Action)
            Dim data = message.GetBody(Of String)()
            Console.WriteLine("Message content: {0}", data)
            'Send a reply.
            Dim replymessage = Message.CreateMessage(binding.MessageVersion, _
                                                     "http://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()
        End Sub

        Public Shared Sub Main()

            Service.RunService()
            Console.WriteLine("Press enter to exit")
            Console.ReadLine()

        End Sub

    End Class
End Namespace