Поделиться через


Программирование служб на уровне канала

В данном разделе описывается, как создать приложение службы Windows Communication Foundation (WCF), не используя класс System.ServiceModel.ServiceHost и связанную с ним объектную модель.

Получение сообщений

Для того чтобы подготовиться к получению и обработке сообщений, необходимо выполнить следующие действия.

  1. Создайте привязку.

  2. Создайте прослушиватель каналов.

  3. Откройте прослушиватель каналов.

  4. Прочтите запрос и отправьте ответ.

  5. Закройте все объекты каналов.

Создание привязки

Первый шаг при работе с ожиданием приема данных и приемом сообщений — создание привязки. 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();
}
}
}