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


Программирование службы Channel-Level

В этом разделе описывается, как написать приложение службы 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-транспорта.

Создание 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: {message.Headers.Action}");
    string data=message.GetBody<string>();
    Console.WriteLine($"Message content: {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