Posted on 2011-01-19 19:12
點點滴滴 閱讀(755)
評論(0) 編輯 收藏 引用 所屬分類:
10 服務器
有些類似于QT中的event與signal,我將一些動作請求消息定義為事件,而將狀態改變消息定義為信號。比如在QT應用程序中,用戶的一次鼠標點擊會產生一個鼠標點擊事件加入到事件隊列中,當處理此事件時可能會導致某個按鈕控件產生一個clicked()信號。
對應到我們的服務器上的一個例子,玩家登錄時會發給服務器一個請求登錄的數據包,服務器可將其當作一個用戶登錄事件,該事件處理完后可能會產生一個用戶已登錄信號。
這樣,與QT類似,對于事件我們可以重定義其處理方法,甚至過濾掉某些事件使其不被處理,但對于信號我們只是收到了一個通知,有些類似于Observe模式中的觀察者,當收到更新通知時,我們只能更新自己的狀態,對剛剛發生的事件我不已不能做任何影響。
仔細來看,事件與信號其實并無多大差別,從我們對其需求上來說,都只要能注冊事件或信號響應函數,在事件或信號產生時能夠被通知到即可。但有一項區別在于,事件處理函數的返回值是有意義的,我們要根據這個返回值來確定是否還要繼續事件的處理,比如在QT中,事件處理函數如果返回true,則這個事件處理已完成,QApplication會接著處理下一個事件,而如果返回false,那么事件分派函數會繼續向上尋找下一個可以處理該事件的注冊方法。信號處理函數的返回值對信號分派器來說是無意義的。
簡單點說,就是我們可以為事件定義過濾器,使得事件可以被過濾。這一功能需求在游戲服務器上是到處存在的。
關于事件和信號機制的實現,網絡上的開源訓也比較多,比如FastDelegate,sigslot,boost::signal等,其中sigslot還被Google采用,在libjingle的代碼中我們可以看到他是如何被使用的。
在實現事件和信號機制時或許可以考慮用同一套實現,在前面我們就分析過,兩者唯一的區別僅在于返回值的處理上。
另外還有一個需要我們關注的問題是事件和信號處理時的優先級問題。在QT中,事件因為都是與窗口相關的,所以事件回調時都是從當前窗口開始,一級一級向上派發,直到有一個窗口返回true,截斷了事件的處理為止。對于信號的處理則比較簡單,默認是沒有順序的,如果需要明確的順序,可以在信號注冊時顯示地指明槽的位置。
在我們的需求中,因為沒有窗口的概念,事件的處理也與信號類似,對注冊過的處理器要按某個順序依次回調,所以優先級的設置功能是需要的。
最后需要我們考慮的是事件和信號的處理方式。在QT中,事件使用了一個事件隊列來維護,如果事件的處理中又產生了新的事件,那么新的事件會加入到隊列尾,直到當前事件處理完畢后,QApplication再去隊列頭取下一個事件來處理。而信號的處理方式有些不同,信號處理是立即回調的,也就是一個信號產生后,他上面所注冊的所有槽都會立即被回調。這樣就會產生一個遞歸調用的問題,比如某個信號處理器中又產生了一個信號,會使得信號的處理像一棵樹一樣的展開。我們需要注意的一個很重要的問題是會不會引起循環調用。
關于事件機制的考慮其實還很多,但都是一些不成熟的想法。在上面的文字中就同時出現了消息、事件和信號三個相近的概念,而在實際處理中,經常發現三者不知道如何界定的情況,實際的情況比我在這里描述的要混亂的多。
這里也就當是挖下一個坑,希望能夠有所交流。