1. 程式人生 > >WCF伺服器向客戶端播送訊息和心跳檢測

WCF伺服器向客戶端播送訊息和心跳檢測

        在利用WCF服務的時候,通常只是在伺服器監聽客戶端呼叫服務。但是WCF服務本就是對TCP, HTTP等各種通訊方式的封裝了所有SOCKET能夠實現的東西,WCF服務應該也能實現。

前面寫過一次部落格,利用WCF服務的會話模式實時檢測客戶端異常掉線。但是對於拔出網線的異常並沒有反應。在這裡我們利用WCF服務的雙工通訊來在伺服器進行心跳檢測,以識別客戶端的掉線.。同時利用雙工通訊向多個已經連線客戶端播送訊息。

       所有的操作正如前面一篇部落格一樣都是建立在WCF服務會話模式的基礎上。

1。心跳

        前篇已經解釋過為何要用會話模式了,不再多說。直接看步驟

1>. 先在WCF服務 IService 裡面定義回撥介面。回撥函式在客戶端實現 這裡不再列出 如下

[ServiceContract(CallbackContract =(typeof(IDataServiceCallback)))]
 public interface IDataServiceCallback
    {
        [OperationContract(IsOneWay =true)]
        void SendBeat();
    }

2>.  在Service.svc 中開啟會話模式,並在建構函式中開啟心跳執行緒

 [ServiceBehavior(IncludeExceptionDetailInFaults =true,InstanceContextMode =InstanceContextMode.PerSession,ConcurrencyMode =ConcurrencyMode.Multiple)]
  OperationContext context;
        MessageProperties properties;
        RemoteEndpointMessageProperty endpoint;
        string loginID = null;
        IDataServiceCallback callback;
        private string loginState;
        Thread th;
        public DataService()
        {
            context = OperationContext.Current;    //獲取登入的時間和IP
            properties = context.IncomingMessageProperties;
            //獲取訊息傳送的遠端終結點IP和埠
            endpoint = properties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
            loginState = "登入成功";
            callback = context.GetCallbackChannel<IDataServiceCallback>();
            th = new Thread(TestBeat);
            th.IsBackground = true;
            th.Start();
        }
線上程中每隔5秒鐘呼叫一次回撥函式,並用try{}catch{}塊包圍
  public void TestBeat()
        {
            try
            {
                while (true)
                {
                    Thread.Sleep(5000);
                    try
                    {
                        callback.SendBeat();
                    }
                    catch(CommunicationException)
                    {
                        userInfoDAL.UpdateState(endpoint.Address, false, loginState);
                        UserInfoDataSource.UpdateRowOut(endpoint.Address, loginState);
                    }
                }
            }
            catch (ThreadAbortException) { }
        }
如上程式碼所示,如果C-S之間的網線被拔出 就能catch到錯誤此時會話就會終止,你便可以在catch中將你儲存的連線使用者剔除,或者如上篇所屬在Dispose()中剔除, 因為如果信道出錯會話終止,此例項就會呼叫Dispose()函式登出此類. 伺服器心跳先到這兒

2。伺服器向客戶端單播或多播訊息。

         同樣是利用雙工通訊。正如Socket一樣 伺服器儲存所有的連線的客戶端Socket WCF服務同樣也需要儲存這樣類似的東西。但是什麼,經過一番研究和推導,它就是服務例項Service 即Service.svc的例項化物件。下面看步驟

1>. 先在WCFService專案中新增一個類,用於儲存連線的服務例項

 public class Users
    {
        public static List<Service1> listChannel = new List<Service1>();
        public static void Add(Service1 service1)
        {
            listChannel.Add(service1);
        }
    }

2>. 然後再Service.svc的建構函式中儲存所有連線的服務例項
  public Service1()
        {
            callback = OperationContext.Current.GetCallbackChannel<ICalculatorCallback>();
     
            Users.Add(this);
        }

3>. 多播資料 這裡我只展示多播函式,單播只要找對應客戶端IP就好。

先定義多播契約函式

   [OperationContract]
        void BroadCast(string str);
實現多播函式
  public void BroadCast(string str)
        {
            Console.WriteLine("獲得[" + remp.Address + ":" + remp.Port + "]傳送過來的訊息:" + str);

            foreach (Service1 context in Users.listChannel)
            {
                context.callback.SendString(remp.Address, remp.Port, str);
            }
        }
然後我們就可以在客戶端利用多播函式向其他客戶端傳送訊息了。 下面是我試驗截圖



從左到右依次是三個客戶端,每個客戶端都呼叫一次多播向其他客戶端傳送訊息. 如第一個 先向服務傳送訊息,然後自己接到服務發的訊息,然後是我的一些測試。 在最後收到第二和第三個客戶端發來的訊息。第二個客戶端最後只接受到第三個客戶端發來的訊息。與Socket 程式設計效果完全一樣甚至更方便。