サービス チャネル レベルのプログラミング

ここでは、Windows Communication Foundation (WCF) サービス アプリケーションを System.ServiceModel.ServiceHost とこれに関係するオブジェクト モデルを使用しないで作成する方法を説明します。

受信、メッセージ

メッセージの受信と処理の準備を整えるには、次の手順に従う必要があります。

  1. バインディングを作成します。

  2. チャネル リスナーをビルドします。

  3. チャネル リスナーを開きます。

  4. 要求を読み取り、応答を送信します。

  5. すべてのチャネル オブジェクトを閉じます。

バインディングの作成

メッセージのリッスンと受信の最初の手順として、バインディングを作成します。 WCF には、組み込みバインディングまたはシステム標準のバインディングが複数付属しており、そのうちの 1 個をインスタンス化することによって直接使用できます。 また、CustomBinding クラスをインスタンス化することにより、独自のバインディングを作成することもできます。手順 1. のコードは、この処理を実行します。

後のコード例は、System.ServiceModel.Channels.CustomBinding のインスタンスを作成し、その Elements コレクションに System.ServiceModel.Channels.HttpTransportBindingElement を追加します。Elements コレクションは、チャネル スタックを作成するために使用されるバインド要素のコレクションです。 この例では、Elements コレクションには HttpTransportBindingElement しか含まれないため、チャネル スタックは HTTP トランスポート チャネルだけを持ちます。

ChannelListener のビルド

バインディングを作成したら、Binding.BuildChannelListener を呼び出してチャネル リスナーをビルドします。型パラメーターは、作成するチャネル形状です。 この例では、要求/応答メッセージ交換パターンで受信メッセージをリッスンする必要があるため、System.ServiceModel.Channels.IReplyChannel を使用します。

IReplyChannel は、要求メッセージを受信し、応答メッセージを返信するために使用されます。 IReplyChannel.ReceiveRequest を呼び出すと、要求メッセージの受信と応答メッセージの返信に使用できる System.ServiceModel.Channels.IRequestChannel が返されます。

リスナーを作成する際には、リッスンするネットワーク アドレス (この場合は http://localhost:8080/channelapp) を渡します。 一般に、各トランスポート チャネルは 1 つまたは複数のアドレス スキームをサポートします。たとえば、HTTP トランスポートは、http スキームと https スキームの両方をサポートします。

また、リスナーを作成する際には、空の System.ServiceModel.Channels.BindingParameterCollection を渡します。 バインディング パラメーターは、リスナーのビルド方法を制御するパラメーターを渡すしくみです。 この例では、このようなパラメーターは使用しないため、空のコレクションを渡します。

受信メッセージのリッスン

次に、ビルドしたリスナーで ICommunicationObject.Open を呼び出し、チャネルの受け入れを開始します。 IChannelListener<TChannel>.AcceptChannel の動作は、トランスポートが接続指向であるか、コネクションレスであるかによって異なります。 接続指向トランスポートの場合、AcceptChannel は新しい接続要求が届くまでブロックし、接続要求が届いた時点で、その新しい接続を表す新しいチャネルを返します。 HTTP などのコネクションレス トランスポートの場合、AcceptChannel は、トランスポート リスナーが作成する唯一のチャネルを直ちに返します。

この例では、リスナーは、IReplyChannel を実装するチャネルを返します。 このチャネルでメッセージを受信するには、まず、チャネルで ICommunicationObject.Open を呼び出して通信できる状態にします。 次に、メッセージが到着するまでブロックする ReceiveRequest を呼び出します。

要求の読み取りと応答の送信

ReceiveRequestRequestContext を返すときに、その RequestMessage プロパティを使用して受信メッセージを取得します。 メッセージのアクションと本文のコンテンツ (文字列であることを前提とします) を書き込みます。

応答を送信するには、新しい応答メッセージを作成します。この場合は、要求で受信した文字列データを渡します。 次に、Reply を呼び出して、その応答メッセージを送信します。

オブジェクトの終了

リソースのリークを避けるには、通信に使用したオブジェクトが不要になったら閉じることが重要です。 この例では、要求メッセージ、要求コンテキスト、チャネル、およびリスナーを閉じます。

次のコード例は、チャネル リスナーがメッセージを 1 つだけ受け取る基本的なサービスです。 実際のサービスでは、サービスが終了するまでチャネルの受け入れとメッセージの受信を続けます。

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