轉帖|其它|編輯:郝浩|2010-11-17 17:24:58.000|閱讀 1047 次
概述:本文主要介紹WCF如何回調已離線客戶端的異常處理,希望對大家有幫助。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
熟悉WCF的朋友應該都了解WCF的雙工回調,這里簡單說一下,WCF的服務開放出去后,一旦有客戶端調用,服務端便會保留各個客戶端的一個句柄,然后服務端會在合適的時候做遠程調用來給客戶端傳遞一些數據,這個類似遠程事件綁定的機制非常有用,很多時候可以避免timer的主動請求服務器,而是由服務器主動推送數據給客戶端:
然而,這種機制,有一個問題,就是當client注冊到服務器后,client可能會意外掉線但來不及通知服務端,當服務端再試圖回調此client時,由于回調句柄無法找到客戶端實現而出現異常:
The communication object, System.ServiceModel.Security.SecuritySessionServerSettings+
SecurityReplySessionChannel, cannot be used for communication because it has been Aborted.
大概意思就是連接已經終止,無法使用連接。
為了解決這個問題,我今天晚上特意寫了一個Demo來測試,這個例子是客戶端一旦注冊到服務器后,服務器每隔3秒鐘回調一次客戶端并傳給客戶端參數:
服務契約
[ServiceContract(CallbackContract=typeof(IAddServiceCallBack))]
public interface IAddService
{
[OperationContract]
void Login(string name);
}
[ServiceContract]
public interface IAddServiceCallBack
{
[OperationContract(IsOneWay=true)]
void ReturnValue(string returnName);
}
服務實現
public class AddService:IAddService
{
public class Client
{
public string username { get; set; }
public IAddServiceCallBack callbackHandler { set; get; }
}
static List<Client> list = new List<Client>();
public void Login(string name)
{
if (list.Where(m => m.username == name).Count() == 0)
{
list.Add(new Client() { username=name, callbackHandler=
OperationContext.Current.GetCallbackChannel
<IAddServiceCallBack>() });
}
}
static System.Timers.Timer timer;
public static void Start()
{
timer = new System.Timers.Timer();
timer.Interval = 1000;
timer.Elapsed += new System.Timers.
ElapsedEventHandler(timer_Elapsed);
timer.Start();
}
static void timer_Elapsed(object sender,
System.Timers.ElapsedEventArgs e)
{
try
{
if (DateTime.Now.Second%3 == 0)
{
list.ForEach(m =
> m.callbackHandler.ReturnValue("服務端回調:" + m.username));
}
}
catch(Exception ex)
{
string s = ex.Message;
}
}
}
static void RemoveCallBack(string name)
{
if (DIC.ContainsKey(name))
{
DIC.Remove(name);
}
}
為了找出原因,我做了一個有趣的測試,將解決方案編譯后,切到windows資源管理器,找到了生成的服務端和客戶端的控制臺程序
我開啟了一個服務實例和三個client實例
我試著將第2個client直接關掉,這樣服務端是不知道2已經掉線的,因此在回調2的時候會出現異常,這時會出現什么問題呢?
如上圖,關閉2后,最先注冊到服務器的客戶端1仍然繼續被服務器回調,但是在2后注冊到服務器的3停止被回調,于是猜想出異常的那個客戶端以后的其他客戶端都會停止掉,而之前的不受影響。為了驗證想法,重新做了測試,關閉1后,果然2和3都停止了。
問題確認后,就得有解決辦法,否則留個異常跟吃個蒼蠅沒有什么區別了。
辦法一,在客戶端的Close或停止的事件中告訴服務器移除回調句柄,這個方法我首先給排除了,關閉窗口怎么辦?斷電怎么辦…客戶端的路走不通了
辦法二,服務端監控客戶端是否離線,心跳包出場,客戶端每5秒鐘想服務器回發一次,若服務器監控到某個客戶端的最后更新時間比現在大5秒則做離線處理,移除客戶端。
在僅有的辦法里,我選了第二個辦法,于是在服務器端加上了
[OperationContract]
void Update(string name);
static Dictionary<string, DateTime> dicOfOnLine =
new Dictionary<string, DateTime>();
static System.Timers.Timer timer1;
public static void StartListenClients()
{
timer1 = new System.Timers.Timer();
timer1.Interval = 500;
timer1.Elapsed +=
new System.Timers.ElapsedEventHandler(timer1_Elapsed);
timer1.Start();
}
static void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
foreach(var item in dicOfOnLine)
{
if (item.Value.AddSeconds(5) < DateTime.Now)
{
DIC.ToList().RemoveAll(m => m.Key == item.Key);
}
}
}
具體實現是,當客戶端注冊到服務器時,服務器將客戶端添加到一個字典中,這個字典中保存有客戶端名稱和添加時間,以后由客戶端定時心跳來更新服務器上的這個字典集合,在服務中會有一個定時器,500毫秒一次去檢測這個集合,如果發現有大于5秒鐘還未更新的客戶端,則從回調句柄集合中移除,由此避免句柄的調用異常問題。
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自:網絡轉載