共用方式為


雙工服務

雙向服務合約是一種訊息交換模式,兩個端點都可以各自獨立地向對方傳送訊息。 因此,雙工服務可以將訊息傳回用戶端端點,並提供類似事件的行為。 當用戶端連線到服務時,雙工通訊就會發生,用戶端會提供一個通道,以便服務可以將訊息傳回給用戶端。 請注意,雙工服務的事件式行為僅限於會話期間有效。

若要建立雙工合約,請建立一組介面。 第一個是服務合約介面,描述用戶端可以叫用的作業。 該服務合約必須指定回呼合約ServiceContractAttribute.CallbackContract屬性中。 回呼合約是定義服務可以在用戶端端點上呼叫的作業的介面。 雙工合約不需要會話,雖然系統提供的雙工繫結會使用會話。

以下是雙工合約的範例。

[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 介面。 服務使用 PerSession 實例模式來維護每個會話的結果。 名為 Callback 的私用屬性會存取用戶端的回呼通道。 服務會使用回呼,透過回呼介面將訊息傳回用戶端,如下列範例程式代碼所示。


[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

客戶端必須提供一個類別來實作雙工合約的回調介面,以便接收來自服務的訊息。 下列範例程式碼顯示一個實作 CallbackHandler 介面的 ICalculatorDuplexCallback 類別。

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

    public void Equation(string eqn)
    {
        Console.WriteLine($"Equation({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 類別。 這個 InstanceContext 類別用於作為實作回呼介面的物件的場所,並負責處理從服務傳回的訊息。 類別 InstanceContext 是使用 類別的 CallbackHandler 實例所建構。 這個物件會處理從服務傳送至回呼介面上用戶端的訊息。

// 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)

必須設定服務的組態,以提供同時支援會話通訊和雙工通訊的系結。 這個 wsDualHttpBinding 元件支持會話通信,並通過同時提供兩個 HTTP 連線來實現雙向通訊,每個方向各一個。

在用戶端上,您必須設定伺服器可用來連線到客戶端的位址,如下列範例組態所示。

備註

無法使用安全交談進行驗證的非雙工用戶端通常會拋出 MessageSecurityException。 不過,如果使用安全交談的雙工客戶端無法驗證,客戶端便會改為接收 TimeoutException

如果您使用 元素建立用戶端/服務 WSHttpBinding ,但不包含用戶端回呼端點,您將會收到下列錯誤。

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

下列範例程式代碼示範如何以程序設計方式指定用戶端端點位址。

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/")

下列範例程式代碼示範如何在組態中指定用戶端端點位址。

<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>

警告

當服務或用戶端關閉其通道時,雙工模型不會自動偵測。 因此,如果用戶端意外終止,則預設不會通知服務,或者如果服務意外終止,則不會通知用戶端。 當您使用一個已中斷連線的服務時,CommunicationException 將會引發例外狀況。 客戶端和服務可以實作自己的協定,若選擇這樣做,便可以互相通知。 如需錯誤處理的詳細資訊,請參閱 WCF 錯誤處理

另請參閱