服务通道级编程
本主题介绍如何在不使用 System.ServiceModel.ServiceHost 及其关联的对象模型的情况下编写 Windows Communication Foundation (WCF) 服务应用程序。
接收消息
若要准备接收并处理消息,必须执行下列步骤:
创建绑定。
生成通道侦听器。
打开通道侦听器。
读取请求并发送答复。
关闭所有通道对象。
创建绑定
侦听并接收消息的第一步是创建绑定。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();
}
}
}