1. 程式人生 > >wcf使用netTcpBinding實現雙工通訊

wcf使用netTcpBinding實現雙工通訊

原來在學習Silverlight的時候使用過WCF的雙工通訊,不過是使用pollingDuplexHttpBinding來實現雙工,而且服務也是繫結在web程式中。也許是架構沒有設計好哭,在客戶端多了的時候有些訊息通道就會異常,覺得很不爽,剛好網上看到一個大俠的基於netTcpBinding的聊天室程式碼(參見http://www.cnblogs.com/ShadowLoki/archive/2012/08/30/2663931.html)。於是就想重新使用netTcpBinding優化一下。大俠提供了兩個程式程式碼,分別是WCF程式寄宿在控制檯程式中,下載回來後應該是SimplePush.rar檔案。另外一個程式則是silverlight +wcf,下載回來後的檔名是

Chat.rar。

好了,按照上面連結介紹的步驟建立service和contract單獨專案,這兩個專案型別為'WCF服務庫'。


一、我們定義一個基礎的WCF服務契約(contract

IPushService
/// <summary>
    /// 推送服務契約
    /// 
    /// Tips:
    /// 契約提供兩個服務,一個是訂閱,一個是退訂。
    /// 服務端會向訂閱的客戶端釋出訊息
    /// </summary>
    [ServiceContract(CallbackContract = typeof(IPushCallback))]
    public
interface IPushService { /// <summary> /// 訂閱服務 /// </summary> [OperationContract(IsOneWay = true)] void Regist(); /// <summary> /// 退訂服務 /// </summary> [OperationContract(IsOneWay = true)] void UnRegist(); }

在這裡定義了兩個行為,訂閱和退訂。

同時還有一個回撥契約如下

IPushCallback
 /// <summary>
    /// 回撥介面 IOC思想的體現
    /// </summary>
    public interface IPushCallback
    {
        [OperationContract(IsOneWay = true)]
        void NotifyMessage(string message);
    }

在這裡,我們對每一個行為都標記為OneWay,表示客戶端在呼叫完服務之後不需要等待服務的回覆,同理回撥時服務端只管廣播,同樣不需要理會客戶端


二 、下面就是我們服務的實現了,服務的實現很簡單,僅僅是捕獲到客戶端的回撥通道,對集合進行操作。這裡需要注意的是ServiceBehavior標記的InstanceContextMode屬性的設定。我們需要為每一個單獨的通道建立新的例項,但是在呼叫玩服務後,不對通道立刻進行回收,因此我們需要設定為InstanceContextModel.Single。

PushService 複製程式碼
 /// <summary>
    /// 服務的實現
    /// Tips:
    /// 實現釋出訂閱,要注意:每個通道在呼叫後不要回收,否則會在回撥時報錯
    /// </summary>
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class PushService : IPushService
    {
        public void Regist()
        {
            IPushCallback callbackChannel = OperationContext.Current.GetCallbackChannel<IPushCallback>();
            //新增到管理列表中
            ChannelManager.Instance.Add(callbackChannel);

        }

        public void UnRegist()
        {
            IPushCallback callbackChannel = OperationContext.Current.GetCallbackChannel<IPushCallback>();
            //從管理列表中移除
            ChannelManager.Instance.Remove(callbackChannel);
        }
    }
複製程式碼

這樣我們的服務基本就完成了,

三、建立服務宿主程式Hosting,程式需要新增對Chat.Hosting和Chat.Contracts的引用

namespace Chat.Hosting
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost host = new ServiceHost(typeof(ChatService));


            ServiceHost crossDomainHost = CrossDomainPolicy.GetHost();


            //註冊事件
            ChatChannelManager.Instance.Added += (sender, e) =>
            {
                Console.WriteLine("新增來自{0}:{1}的客戶端到列表中  {2}", e.Channel.IP, e.Channel.Port, DateTime.Now);
            };


            ChatChannelManager.Instance.Removed += (sender, e) =>
            {
                Console.WriteLine("移除來自{0}:{1}的客戶端從列表中  {2}", e.Channel.IP, e.Channel.Port, DateTime.Now);
            };


            ChatChannelManager.Instance.Faulted += (sender, e) =>
            {
                Console.WriteLine("Key為{0}的通道已經報廢啦,直接回收了  {1}\n錯誤內容:{2}", e.Name, DateTime.Now, e.ErrorMessage);
                ChatChannelManager.Instance.RemoveChannel(e.Name);
            };


            try
            {
                host.Open();
                crossDomainHost.Open();


                Console.WriteLine("服務啟動,按任意鍵退出");


                Console.ReadKey();


                host.Close();
                crossDomainHost.Close();
            }
            catch
            {
                Console.WriteLine("服務寄宿失敗");
            }


        }
    }
}

配置檔案App.config如下,需要注意回撥介面是contract="FIIS.Contracts.IChatCallback"而不是contract="FIIS.Services.IChatCallback"。否則會載入服務失敗

<?xml version="1.0"?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MessageBehavior">
          <serviceMetadata httpGetEnabled="false"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="MessageBehavior" name="Chat.Services.ChatService">
        <endpoint address="" binding="netTcpBinding" bindingConfiguration="Binding1" contract="FIIS.Contracts.IChatCallback"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:4503/Service"/>
          </baseAddresses>
        </host>
      </service>
    </services>
    <bindings>
      <netTcpBinding>
        <binding name="Binding1" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10" maxReceivedMessageSize="65536">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
          <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false"/>
          <security mode="None"></security>
        </binding>
      </netTcpBinding>
    </bindings>
  </system.serviceModel>
</configuration>

四、客戶端程式。在他的該示例中使用的是silverlight5,我的是4.0使用不了,不知道能否正常執行。所以就新建一個控制檯客戶端,名為Chat.Client。步驟:

1、新增System.ServiceModel引用

2、新增服務引用,地址為:net.tcp://localhost:4503/Service,服務引用名稱為MessageServiceRef。服務不能選用非同步方式,否則會有些問題。

3、新增Chat.Client.MessageServiceRef引用。

4、建立回撥介面類IChatCallback的實現,這裡需要注意繼承的介面必須是MessageServiceRef下面的回撥類,應該是IChatServiceCallback而不是直接引用Chat.Contracts.IChatCallback。否則就會報如下的錯誤“向 ChannelFactory 提供的 InstanceContext 包含未實現 
CallbackContractType”

class MessageCallback :IChatServiceCallback
    {

Console.WriteLine(chat.Message);

}

5、客戶端主程式程式碼

static void Main(string[] args)
        {
            InstanceContext instanceContext = new InstanceContext(new MessageCallback());
            MessageServiceRef.ChatServiceClient msgClient = new MessageServiceRef.ChatServiceClient(instanceContext);

            Console.WriteLine("歡迎來到測試聊天室,請輸入使用者名稱:");
            string name = Console.ReadLine();
            msgClient.LoginChat(name);

            string msg =Console.ReadLine();
            msgClient.Chat(msg);
            Console.ReadLine();
        }

---------------------------------

文章寫的匆忙,主要是為了記錄建立這個程式過程中遇到的錯誤。如服務非同步方式、回撥介面類的實現報錯等等