1. 程式人生 > >cocos2dx上的通用socket通訊(一)

cocos2dx上的通用socket通訊(一)

前言:之前一直在搗鼓socket通訊模組,當然,到現在為止我還沒有徹底弄好,不過資料至少是有去有回了,只是在Android上跑容易崩掉罷了,汗。在網上找了好多關於cocos2dx上跨平臺的socket通訊庫的文章,都只有皮毛,不見有真正好用的,好吧,我確實比較懶,當然,官方沒有去實現這個定有它的原因,我也懶得深究,因為最近折騰Android上的坑都快把我搞崩潰了。希望這個能搞個系列吧,我有好多文章標題上有(一)之類的,後面再也沒出現過(二),不是懶得寫就是試驗失敗,不能繼續了。這裡是我和某人(暫時不便透露)之間的一封郵件內容(其實是我寫的),我做了摘取,算是個思路的整理過程吧,後續我會把對應的程式碼也放上來。

目的

    在cocos2dx上實現一個支援非同步通訊的socket通訊例子。

 大致思路

  1. 專門實現一個業務處理模組,比如叫TransactionModule,其中封裝了BSD的socket進行資料的收發。提供單例實現,即提供靜態的方法訪問一個全域性的唯一物件。
  2. 所有的資料傳送都是通過業務模組處理,比如提供一個SendData之類的函式。
  3. 業務處理模組獨立啟動一個執行緒(使用pthread)用來接收伺服器發來的資料,放到TransactionModule中的資料接收快取。當有資料寫入快取時(或者當有資料收到時),通知介面執行緒取資料後更新介面。

 問題

    這裡犯了錯誤,直到昨天我才對這個錯誤有比較深刻的認識:

  1. 工作執行緒不能操作介面執行緒的資源,比如更新介面資料,不要在工作執行緒中回撥涉及介面更新的函式
  2. CCNotificationCenter並不是執行緒安全的(這樣講可能不是很準確),如果在工作執行緒中post,那必然回撥函式還在工作執行緒中響應
  3. 執行緒間的通訊,還是需要用一個執行緒安全的佇列或者執行緒安全的資料交換區來實現,比如可以使用pthread_mutex_lock和pthread_mutex_unlock之類的方法來實現執行緒間訪問資料的互斥

 實現方法更新

    對應的業務模組TransactionModule依然存在,啟動工作執行緒的方式也不改變,本來在工作執行緒中收到資料後使用CCNotificationCenter去實現對應業務處理的回撥函式不在工作執行緒中使用,可以考慮放到主執行緒(介面執行緒)中使用,這樣可以使得處理邏輯更加清晰。

  • 步驟一:在TransactionModule中有業務請求時,檢查是否有連上伺服器,沒有連結上則執行步驟二,如果連結狀態正常,則執行步驟三
  • 步驟二:建立Socket,連結伺服器,如果連結成功,則執行步驟三,如果連結失敗則報錯,請求失敗,結束
  • 步驟三:發請求資料到伺服器,判斷send返回狀態,如果小於0,執行步驟四,如果已傳送長度等於傳送長度,則執行步驟五,否則繼續執行步驟三再次傳送剩餘資料(這裡沒有考慮等於0的狀態)
  • 步驟四:返回了SOCKET_ERROR錯誤,關閉socket,重置連結狀態和建立Socket狀態,返回錯誤,請求失敗,結束
  • 步驟五:檢查接收執行緒是否啟動,如果執行緒正在執行,則執行步驟七,否則執行步驟六
  • 步驟六:建立執行緒,判斷停止標誌,迴圈執行接收資料函式(使用libevent的話,僅僅是執行dispatch),然後轉到步驟七,等待資料返回。
  • 步驟七:等待伺服器資料返回,阻塞狀態,如果有資料返回,則執行步驟八
  • 步驟八:當有資料到達時,判斷接收狀態,如果大於等於0,則執行步驟四,並設定執行緒終止標誌。否則把資料新增到公共緩衝區或者公共佇列(執行緒安全)中。

    以上便是業務處理模組在收發資料時要做的事情,並且從步驟五開始,都是工作執行緒呼叫的函式(僅由工作執行緒呼叫)。步驟四有主執行緒呼叫或者工作執行緒呼叫處理。呃,如果有個流程圖會更直觀得解釋下處理步驟,暫時我手邊沒有繪製流程圖的工具,抱歉。

    在介面執行緒(主執行緒)中,會啟動一個定時器,用於判斷公共緩衝區或者公共佇列中是否有未處理的資料到達。如果有則呼叫業務處理模組的資料預處理函式處理資料(比如處理粘包之類),處理完成後呼叫介面的函式來更新資料,做場景跳轉等。

    這裡的關鍵應該就是工作執行緒只處理資料的返回,並且把返回資料放到公共緩衝區中,其餘的就什麼都不幹。介面執行緒單獨用定時器去處理緩衝區中的資料。其實就是執行緒間的資料通訊。

    另:這裡我使用的通訊事件處理是libevent,socket的處理是用的是ODSocket,這個是針對WIN32和其餘*unix平臺做了一個簡單封裝。這樣就可以在win32、android、iOS上實現統一的socket通訊處理了。當然libevent是要各個平臺分別編譯的。

    到了主執行緒中,當資料處理的時候,倒是可以使用CCNotificationCenter來實現回撥函式來處理。之前一直以為CCNotificationCenter是相對獨立的執行緒中的實現,並且可以通知到各個其他執行緒實現正確的函式回撥,這明顯是天真了,因為函式是不會繫結執行緒執行的,除非另外有一個機制去保證某些函式的回撥只實現在繫結的執行緒中(扯遠了)。