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