双方向サービスDuplex Services

双方向サービス コントラクトは、両方のエンドポイントが互いに独立してメッセージを送信できるメッセージ交換パターンです。A duplex service contract is a message exchange pattern in which both endpoints can send messages to the other independently. 双方向サービスでは、クライアントのエンドポイントにメッセージを返信できるため、イベントのような動作を実現できます。A duplex service, therefore, can send messages back to the client endpoint, providing event-like behavior. 双方向通信は、クライアントがサービスに接続し、サービスからクライアントにメッセージを返信できるチャネルがサービスに提供されると発生します。Duplex communication occurs when a client connects to a service and provides the service with a channel on which the service can send messages back to the client. 双方向サービスにおけるイベントのような動作は、セッション内でのみ機能することに注意してください。Note that the event-like behavior of duplex services only works within a session.

双方向コントラクトを作成するには、インターフェイスのペアを作成します。To create a duplex contract you create a pair of interfaces. 最初のインターフェイスは、クライアントから呼び出すことのできる操作を記述したサービス コントラクト インターフェイスです。The first is the service contract interface that describes the operations that a client can invoke. サービス コントラクトを指定する必要があります、コールバック コントラクトで、ServiceContractAttribute.CallbackContractプロパティ。That service contract must specify a callback contract in the ServiceContractAttribute.CallbackContract property. このコールバック コントラクトが、サービスがクライアント エンドポイントで呼び出すことのできる操作を定義するインターフェイスになります。The callback contract is the interface that defines the operations that the service can call on the client endpoint. 双方向コントラクトではセッションは必要ありませんが、システム指定の二重バインディングではセッションを利用します。A duplex contract does not require a session, although the system-provided duplex bindings make use of them.

双方向コントラクトの例を次に示します。The following is an example of a duplex contract.

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required,
                 CallbackContract=typeof(ICalculatorDuplexCallback))]
public interface ICalculatorDuplex
{
    [OperationContract(IsOneWay = true)]
    void Clear();
    [OperationContract(IsOneWay = true)]
    void AddTo(double n);
    [OperationContract(IsOneWay = true)]
    void SubtractFrom(double n);
    [OperationContract(IsOneWay = true)]
    void MultiplyBy(double n);
    [OperationContract(IsOneWay = true)]
    void DivideBy(double n);
}


public interface ICalculatorDuplexCallback
{
    [OperationContract(IsOneWay = true)]
    void Equals(double result);
    [OperationContract(IsOneWay = true)]
    void Equation(string eqn);
}
<ServiceContract(Namespace := "http://Microsoft.ServiceModel.Samples", SessionMode:=SessionMode.Required, CallbackContract:=GetType(ICalculatorDuplexCallback))> _
Public Interface ICalculatorDuplex
    <OperationContract(IsOneWay := True)> _
    Sub Clear()
    <OperationContract(IsOneWay := True)> _
    Sub AddTo(ByVal n As Double)
    <OperationContract(IsOneWay := True)> _
    Sub SubtractFrom(ByVal n As Double)
    <OperationContract(IsOneWay := True)> _
    Sub MultiplyBy(ByVal n As Double)
    <OperationContract(IsOneWay := True)> _
    Sub DivideBy(ByVal n As Double)
End Interface


Public Interface ICalculatorDuplexCallback
    <OperationContract(IsOneWay := True)> _
    Sub Equals(ByVal result As Double)
    <OperationContract(IsOneWay := True)> _
    Sub Equation(ByVal eqn As String)
End Interface

CalculatorService クラスは、プライマリ ICalculatorDuplex インターフェイスを実装します。The CalculatorService class implements the primary ICalculatorDuplex interface. このサービスは PerSession インスタンス モードを使用して、各セッションの結果を保持します。The service uses the PerSession instance mode to maintain the result for each session. Callback というプライベート プロパティを使用して、クライアントへのコールバック チャネルにアクセスします。A private property named Callback accesses the callback channel to the client. 次のサンプル コードに示すように、サービスはこのコールバックを使用し、コールバック インターフェイスを介してメッセージをクライアントに返信します。The service uses the callback for sending messages back to the client through the callback interface, as shown in the following sample code.


[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class CalculatorService : ICalculatorDuplex
{
    double result = 0.0D;
    string equation;

    public CalculatorService()
    {
        equation = result.ToString();
    }

    public void Clear()
    {
        Callback.Equation(equation + " = " + result.ToString());
        equation = result.ToString();
    }

    public void AddTo(double n)
    {
        result += n;
        equation += " + " + n.ToString();
        Callback.Equals(result);
    }

    public void SubtractFrom(double n)
    {
        result -= n;
        equation += " - " + n.ToString();
        Callback.Equals(result);
    }

    public void MultiplyBy(double n)
    {
        result *= n;
        equation += " * " + n.ToString();
        Callback.Equals(result);
    }

    public void DivideBy(double n)
    {
        result /= n;
        equation += " / " + n.ToString();
        Callback.Equals(result);
    }

    ICalculatorDuplexCallback Callback
    {
        get
        {
            return OperationContext.Current.GetCallbackChannel<ICalculatorDuplexCallback>();
        }
    }
}

<ServiceBehavior(InstanceContextMode := InstanceContextMode.PerSession)> _
Public Class CalculatorService
    Implements ICalculatorDuplex
    Private result As Double = 0.0R
    Private equation As String

    Public Sub New()
        equation = result.ToString()
    End Sub

    Public Sub Clear() Implements ICalculatorDuplex.Clear
        Callback.Equation(equation & " = " & result.ToString())
        equation = result.ToString()
    End Sub

    Public Sub AddTo(ByVal n As Double) Implements ICalculatorDuplex.AddTo
        result += n
        equation &= " + " & n.ToString()
        CType(Callback, Object).Equals(result)
    End Sub

    Public Sub SubtractFrom(ByVal n As Double) Implements ICalculatorDuplex.SubtractFrom
        result -= n
        equation &= " - " & n.ToString()
        CType(Callback, Object).Equals(result)
    End Sub

    Public Sub MultiplyBy(ByVal n As Double) Implements ICalculatorDuplex.MultiplyBy
        result *= n
        equation &= " * " & n.ToString()
        CType(Callback, Object).Equals(result)
    End Sub

    Public Sub DivideBy(ByVal n As Double) Implements ICalculatorDuplex.DivideBy
        result /= n
        equation &= " / " & n.ToString()
        CType(Callback, Object).Equals(result)
    End Sub

    Private ReadOnly Property Callback() As ICalculatorDuplexCallback
        Get
            Return OperationContext.Current.GetCallbackChannel(Of ICalculatorDuplexCallback)()
        End Get
    End Property
End Class

クライアントは、サービスからのメッセージを受信するために、双方向コントラクトのコールバック インターフェイスを実装するクラスを提供する必要があります。The client must provide a class that implements the callback interface of the duplex contract, for receiving messages from the service. 次のサンプル コードに、CallbackHandler インターフェイスを実装する ICalculatorDuplexCallback クラスを示します。The following sample code shows a CallbackHandler class that implements the ICalculatorDuplexCallback interface.

public class CallbackHandler : ICalculatorDuplexCallback
{
    public void Equals(double result)
    {
        Console.WriteLine("Equals({0})", result);
    }

    public void Equation(string eqn)
    {
        Console.WriteLine("Equation({0})", eqn);
    }
}
Public Class CallbackHandler
    Implements ICalculatorDuplexCallback
    Public Overridable Shadows Sub Equals(ByVal result As Double) Implements ICalculatorDuplexCallback.Equals
        Console.WriteLine("Equals({0})", result)
    End Sub

    Public Sub Equation(ByVal eqn As String) Implements ICalculatorDuplexCallback.Equation
        Console.WriteLine("Equation({0})", eqn)
    End Sub
End Class

双方向コントラクトでは、用に生成される WCF クライアントをInstanceContext構築時に提供するクラス。The WCF client that is generated for a duplex contract requires a InstanceContext class to be provided upon construction. この InstanceContext クラスが、コールバック インターフェイスを実装するオブジェクトのサイトとして使用され、サービスから返信されるメッセージを処理します。This InstanceContext class is used as the site for an object that implements the callback interface and handles messages that are sent back from the service. InstanceContext クラスは、CallbackHandler クラスのインスタンスを使用して構築されます。An InstanceContext class is constructed with an instance of the CallbackHandler class. このオブジェクトは、コールバック インターフェイスでサービスからクライアントに送信されるメッセージを処理します。This object handles messages sent from the service to the client on the callback interface.

// Construct InstanceContext to handle messages on callback interface
InstanceContext instanceContext = new InstanceContext(new CallbackHandler());

// Create a client
CalculatorDuplexClient client = new CalculatorDuplexClient(instanceContext);
' Construct InstanceContext to handle messages on callback interface
Dim instanceContext As New InstanceContext(New CallbackHandler())

' Create a client
Dim client As New CalculatorDuplexClient(instanceContext)

サービスの構成は、セッション通信と双方向通信の両方をサポートするバインディングを提供するように設定する必要があります。The configuration for the service must be set up to provide a binding that supports both session communication and duplex communication. wsDualHttpBinding 要素はセッション通信をサポートし、どちらの方向にも HTTP 接続が 1 つ用意される双方向 HTTP 接続を提供して双方向通信を実現します。The wsDualHttpBinding element supports session communication and allows duplex communication by providing dual HTTP connections, one for each direction.

クライアントで、サーバーがクライアントへの接続に使用するアドレスを構成する必要があります。次のサンプル構成を参照してください。On the client, you must configure an address that the server can use to connect to the client, as shown in the following sample configuration.

注意

非双方向クライアントがセキュリティで保護されたメッセージ交換を使用して認証に失敗した場合、通常、MessageSecurityException がスローされます。Non-duplex clients that fail to authenticate using a secure conversation typically throw a MessageSecurityException. ただし、セキュリティで保護されたメッセージ交換を使用する双方向クライアントが認証に失敗した場合、クライアントは代わりに TimeoutException を受信します。However, if a duplex client that uses a secure conversation fails to authenticate, the client receives a TimeoutException instead.

WSHttpBinding 要素を使用するクライアントとサービスを作成しても、クライアントのコールバック エンドポイントが含まれていない場合は、次のエラーが発生します。If you create a client/service using the WSHttpBinding element and you do not include the client callback endpoint, you will receive the following error.

HTTP could not register URL
htp://+:80/Temporary_Listen_Addresses/<guid> because TCP port 80 is being used by another application.

次のサンプル コード方法を示します、クライアントを指定するエンドポイント アドレス プログラムを使用します。The following sample code shows how to specify the client endpoint address programmatically.

WSDualHttpBinding binding = new WSDualHttpBinding();
EndpointAddress endptadr = new EndpointAddress("http://localhost:12000/DuplexTestUsingCode/Server");
binding.ClientBaseAddress = new Uri("http://localhost:8000/DuplexTestUsingCode/Client/");
Dim binding As New WSDualHttpBinding()
Dim endptadr As New EndpointAddress("http://localhost:12000/DuplexTestUsingCode/Server")
binding.ClientBaseAddress = New Uri("http://localhost:8000/DuplexTestUsingCode/Client/")

次のサンプル コードは、構成でクライアントのエンドポイント アドレスを指定する方法を示しています。The following sample code shows how to specify the client endpoint address in configuration.

<client>
    <endpoint name ="ServerEndpoint"
          address="http://localhost:12000/DuplexTestUsingConfig/Server"
          bindingConfiguration="WSDualHttpBinding_IDuplexTest"
            binding="wsDualHttpBinding"
           contract="IDuplexTest" />
</client>
<bindings>
    <wsDualHttpBinding>
        <binding name="WSDualHttpBinding_IDuplexTest"
          clientBaseAddress="http://localhost:8000/myClient/" >
            <security mode="None"/>
         </binding>
    </wsDualHttpBinding>
</bindings>

警告

双方向のモデルには、サービスまたはクライアントがそのチャネルを閉じるときに自動的に検出しません。The duplex model doesn't automatically detect when a service or client closes its channel. したがって、クライアントが予期せず終了した場合は既定では、サービス通知されません、またはサービスが予期せず終了した場合、クライアントは通知されません。So if a client unexpectedly terminates, by default the service will not be notified, or if a service unexpectedly terminates, the client will not be notified. 切断されているサービスを使用する場合、CommunicationException例外が発生します。If you use a service that is disconnected, the CommunicationException exception is raised. クライアントとサービスは、独自のプロトコルを実装して、互いに通知するように選択できます。Clients and services can implement their own protocol to notify each other if they so choose. エラー処理の詳細については、次を参照してくださいWCF エラー処理。For more information on error handling, see WCF Error Handling

関連項目See also