首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WCF 實戰教學 (6):OneWay 非同步呼叫與 Duplex 雙向通訊實作全解析

     編輯:關於.NET

前言

WCF 提供兩種基本的非同步通訊模式:OneWay (單向/發後不理) 與 Duplex (雙向/回呼)。前者適合「通知類」作業 (如記錄日誌、發送郵件),後者適合「伺服器主動推播」情境 (如即時訊息、股價行情、進度通知)。本篇完整示範兩種模式的契約定義、繫結選擇、宿主設定與用戶端呼叫方式。

模式比較速覽

特性OneWay (IsOneWay=true)Duplex (CallbackContract)
方向用戶端 → 服務 (單向)用戶端 ↔ 服務 (雙向)
回傳值不可有 (void)可有,但建議搭配 IsOneWay=true
例外處理用戶端收不到例外 (需靠 IErrorHandler)雙向皆可拋回 FaultException
支援繫結所有繫結需支援雙工:netTcpBinding, wsDualHttpBinding, netNamedPipeBinding, netMsmqBinding
典型用途日誌、通知、非關鍵作業聊天室、即時行情、長時間作業進度回報

Part 1:OneWay 非同步呼叫

1. 服務契約與實作

using System;
using System.ServiceModel;

namespace WCF.ServiceLib.Message
{
    [ServiceContract]
    public interface IOneWay
    {
        /// 同步呼叫:用戶端會等待並接收例外
        [OperationContract]
        void WithoutOneWay();

        /// 非同步呼叫:用戶端發送後立即返回,不等待服務端處理完成
        [OperationContract(IsOneWay = true)]
        void WithOneWay();
    }

    public class OneWay : IOneWay
    {
        public void WithoutOneWay()
        {
            throw new Exception("同步模式拋出例外,用戶端可捕獲");
        }

        public void WithOneWay()
        {
            throw new Exception("OneWay 模式拋出例外,用戶端不會收到,服務端需自行記錄");
        }
    }
}

2. 宿主設定 (App.config)

<service name="WCF.ServiceLib.Message.OneWay" behaviorConfiguration="MessageBehavior">
  <endpoint address="" binding="basicHttpBinding" contract="WCF.ServiceLib.Message.IOneWay" />
  <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  <host>
    <baseAddresses>
      <add baseAddress="https://localhost:12345/Message/OneWay/" />
    </baseAddresses>
  </host>
</service>

3. 用戶端呼叫差異

public class OneWayClient
{
    // 同步:會拋出例外,MessageBox 顯示錯誤
    public void CallWithoutOneWay()
    {
        var proxy = new MessageSvc.OneWay.OneWayClient();
        try { proxy.WithoutOneWay(); proxy.Close(); }
        catch (Exception ex) { MessageBox.Show(ex.ToString()); }
    }

    // OneWay:不拋出例外,MessageBox 不會彈出 (若服務端拋出例外,通道會進入 Faulted 狀態)
    public void CallWithOneWay()
    {
        var proxy = new MessageSvc.OneWay.OneWayClient();
        try { proxy.WithOneWay(); proxy.Close(); }
        catch (Exception ex) { MessageBox.Show(ex.ToString()); } // 這裡捕獲不到服務端例外
    }
}
重要提醒:OneWay 作業若在服務端拋出未處理例外,通道會進入 Faulted 狀態,後續呼叫會失敗。生產環境務必搭配 IErrorHandler 統一記錄錯誤並避免通道錯誤。

Part 2:Duplex 雙向通訊 (以 netTcpBinding 為例)

1. 定義雙向契約

using System;
using System.ServiceModel;

namespace WCF.ServiceLib.Message
{
    /// 用戶端需實作的回呼介面
    public interface IDuplexCallback
    {
        [OperationContract(IsOneWay = true)]
        void HelloDuplexCallback(string name);
    }

    /// 服務介面,指定 CallbackContract
    [ServiceContract(CallbackContract = typeof(IDuplexCallback))]
    public interface IDuplex
    {
        [OperationContract(IsOneWay = true)]
        void HelloDuplex(string name);
    }

    public class Duplex : IDuplex
    {
        public void HelloDuplex(string name)
        {
            // 取得用戶端回呼通道
            var callback = OperationContext.Current.GetCallbackChannel();
            // 主動呼叫用戶端方法
            callback.HelloDuplexCallback(name);
        }
    }
}

2. 宿主設定 (支援雙工的繫結)

<service name="WCF.ServiceLib.Message.Duplex" behaviorConfiguration="MessageBehavior">
  <!-- netTcpBinding 原生支援雙工,單一連線即可雙向 -->
  <endpoint address="Message/Duplex"
            binding="netTcpBinding"
            contract="WCF.ServiceLib.Message.IDuplex" />
  <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  <host>
    <baseAddresses>
      <add baseAddress="https://localhost:12345/Message/Duplex" />
      <add baseAddress="net.tcp://localhost:54321/" />
    </baseAddresses>
  </host>
</service>
繫結選擇說明:wsDualHttpBinding 會建立兩個 HTTP 連線 (一個請求、一個回呼),穿透防火牆較易但效能較差;netTcpBinding 單一 TCP 連線雙向通訊,效能最佳,適合內網環境。

3. 用戶端實作回呼與呼叫

using System;
using System.ServiceModel;
using System.Windows.Forms;

namespace Client2.Message
{
    /// 實作回呼介面
    public class CallbackHandler : MessageSvc.Duplex.IDuplexCallback
    {
        public void HelloDuplexCallback(string name)
        {
            MessageBox.Show($"伺服器回呼:Hello {name}");
        }
    }

    public class DuplexClient
    {
        public void CallDuplex(string name)
        {
            // InstanceContext 綁定回呼實例
            var ctx = new InstanceContext(new CallbackHandler());
            // DuplexClient 建構子需傳入 InstanceContext
            var proxy = new MessageSvc.Duplex.DuplexClient(ctx);
            proxy.HelloDuplex(name);
            // 注意:OneWay 作業不需等待回傳,但通道需保持開啟以接收回呼
        }
    }
}

4. 用戶端 App.config

<client>
  <endpoint address="net.tcp://localhost:54321/Message/Duplex"
            binding="netTcpBinding"
            contract="MessageSvc.Duplex.IDuplex" />
</client>

進階主題與最佳實務

1. Duplex 連線生命週期管理

  • 用戶端需維持 InstanceContext 存活,若物件被 GC 回收,回呼將失效。
  • 服務端可透過 ICommunicationObject.Closed/Faulted 事件偵測用戶端斷線並清理資源。
  • 建議實作 IDuplexCallback 時加入 [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)] 允許重入,避免回呼中再呼叫服務端造成死結。

2. OneWay 可靠性增強

  • 搭配 netMsmqBinding 實現佇列傳輸,保証訊息不遺失。
  • 或自訂 IErrorHandler 記錄錯誤並回傳自訂 Fault,防止通道 Faulted。

3. 並發控制

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall)]
public class Duplex : IDuplex { ... }

高併發情境下建議 PerCall + Multiple,並確保回呼實作為線程安全。

執行結果驗證

  • OneWay:點擊「WithOneWay」按鈕 → 無彈窗 (非同步);點擊「WithoutOneWay」 → 彈出例外訊息 (同步)。
  • Duplex:點擊「Duplex」按鈕 → 彈出「伺服器回呼:Hello webabcd」。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved