雙工服務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.CallbackContractThat 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 連接 (一個方向一個連接) 允許雙工通訊。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.

注意

無法使用安全對話來進行驗證的非雙工用戶端通常會擲回 MessageSecurityExceptionNon-duplex clients that fail to authenticate using a secure conversation typically throw a MessageSecurityException. 不過,如果使用安全對話的雙工用戶端驗證失敗,則用戶端會收到 TimeoutExceptionHowever, 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