公司的系統需要有系統消息提醒,即別的用戶發的在線短信或者手機短信的提醒。按照以往的做法,一般是客戶端發請求,去服務器查詢數據庫,然后返回信息,有消息則提醒。這么做的弊端就是,不管用戶是否有新消息,客戶端每隔一段時間總要發送請求,這樣做的話既消耗帶寬又占用服務器資源,增加數據庫負擔,而且大多數情況用戶是沒有任何消息,都是無用功,并且消息提醒也不即時,只有客戶端去查詢才能獲知,所以這種做法并不好。而Remoting允許客戶端訂閱,服務器監聽客戶端的,如果服務器端有消息即刻通知客戶端,既不會浪費帶寬增加負擔也達到了即時提醒的效果。
說了這么多了,現在開始說如何實現的。我的實驗例子創建了3個工程,CommonLib、MessageClient、MessageServer。MessageClient就是客戶端,用來發布和接收消息,MessageServer是服務端,用來派發收到的消息,CommonLib是一個公用的類庫,被客戶端和服務端分別引用的。
CommonLib里包含了一個接口 IMessageBody 和一個事件類 MessageEvent。
IMessageBody:
{
[System.Runtime.Remoting.Messaging.OneWay]
void SendMsg(string strText, DateTime datTime);
void AddEvent(string strKey, MessageEvent cEvent);
void RemoveEvent(string strKey, MessageEvent cEvent);
}
MessageEvent:
MessageServer里很簡單,啟動時加載了一個配置文件,還有一個接口IMessageBody的實現類MessageBody。
MessageBody:
客戶端的一個窗體ClientForm:
以上就是示例的所有代碼,其他沒太多解釋的地方,說一下那個MessageBody類吧,這個類浪費了我不少時間來實現。
大家可以看到在AddEvent和RemoveEvent這兩個方法中,我并沒有像一般的做法那樣直接綁定事件(ReceiveMsg += new ReceiveMessageHandler(cEvent.OnReceive); 和 ReceiveMsg -= cEvent.OnReceive),而是用了一個 IDictionary<string, ReceiveMessageHandler> _EventList 的集合來存放客戶端的訂閱事件,然后只是在發送消息的時候才去綁定,也就是Subscribe()的方法,當消息發送了之后即刻取消綁定(ReceiveMsg -= receicve; )。這樣做的好處在于,改進一下 Subscribe() 方法即可對特定的人發送消息(只觸發特定的人的訂閱事件),而不用像廣播一樣全體發送。
大家還可以看到在SendMsg的方法中有一句被注釋的語句,ReceiveMsg(strText + " " + datTime);。這句話其實是系統自動做遍歷把消息發送到每個訂閱的客戶端。正常情況下如果客戶端正常退出取消訂閱則系統不會出問題,但如果客戶端非正常退出,沒有取消訂閱,那服務端在遍歷到這里的時候就會報錯,導致后面的消息無法發送。后來我在MessageEvent的OnReceive方法上,加上了[System.Runtime.Remoting.Messaging.OneWay]這個屬性,他的作用是標明此方法是單向的,執行后不用等回復,并摒棄錯誤信息。這樣做了之后,確實可以運行了,但是每當到了這里的時候都會卡一下,而且如果類似情況多了,系統會變得很慢,如果發現出錯能取消此客戶端訂閱就好了。就是這個問題話了我不少時間,總算找到了解決方法,就是不用原先的ReceiveMsg(strText + " " + datTime);,自己遍歷所有的訂閱客戶端,并發送消息,取消OnReceive的OneWay屬性,如果發送報錯則從集合中移除此訂閱,那以后就可高枕無憂了。
有人要問,我的題目是系統廣播和即時聊天,為何只有一個實例?其實,這個實例就是個即時聊天,對于系統廣播,客戶端只接收不發送,服務端只發送不接收即可實現了。呵呵。