Программирование служб на уровне канала
В этом разделе описывается, как написать приложение службы Windows Communication Foundation (WCF) без использования и связанной с ней System.ServiceModel.ServiceHost объектной модели.
получение сообщений
Для того чтобы подготовиться к получению и обработке сообщений, необходимо выполнить следующие действия.
Создайте привязку.
Создайте прослушиватель каналов.
Откройте прослушиватель каналов.
Прочтите запрос и отправьте ответ.
Закройте все объекты каналов.
Создание привязки
Первым шагом для прослушивания и получения сообщений является создание привязки. WCF поставляется с несколькими встроенными или системными привязками, которые можно использовать непосредственно путем создания экземпляра одного из них. Кроме того, всегда можно создать собственную пользовательскую привязку, создав класс CustomBinding, что и делает код в примере 1.
Пример кода, показанный ниже, создает экземпляр класса System.ServiceModel.Channels.CustomBinding и добавляет элемент System.ServiceModel.Channels.HttpTransportBindingElement в его коллекцию элементов, то есть коллекцию элементов привязки, используемую для построения стека канала. Поскольку коллекция элементов в этом примере состоит только из экземпляров класса HttpTransportBindingElement, у получившегося стека канала имеется только канал транспорта HTTP.
Создание прослушивателя каналов
После создания привязки вызывается метод Binding.BuildChannelListener, с помощью которого создается прослушиватель канала, параметр Type которого указывает форму создаваемого канала. В этом примере используется интерфейс 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