libjingle源碼分析之二:Thread和SocketServer

  • 摘要

      本文主要分析了libjingle源碼中的Thread和SocketServer模塊,以及它們是如何協同工作的。首先,介紹了Thread和SocketServer的模型,給出瞭如何使用Thread的示例。然後,分析了Thread中的默認消息循環的處理流程和如何自己處理消息。

  • 概述

      libjingle源碼中,Thread和SocketServer模塊的原理如下圖所示。整個模型實際上是一個消息模型,Thread主要負責處理消息,MessageQue表示的是當前的消息隊列,MessageHandler由用戶用來定義處理消息的動作。而ThreadManager爲單實例,可以獲取當前的Thread,這樣用戶可以往當前的Thread中投遞消息。SocketServer代表的是用來偵聽Socket的服務,它是一個獨立的模塊。


      消息的處理流程主要由Thread負責。上圖中有兩條處理流程,分別用兩根帶箭頭的線表示。左邊箭頭的處理流程爲:當消息隊列中沒有消息時,Thread將控制權轉交給SocketServer,直到有消息時會通知SocketServer返回到Thread。也就是說Thread優先的是處理消息,在空閒時,會讓SocketServer偵聽socket。右邊的箭頭是正常的消息處理流程,獲取消息並處理用戶定義的對應的OnMessage函數。
      SocketServer模塊只是用在libjingle內部,用戶並不需要直接使用它。P2P中使用了PhysicalSocketServer作爲SocketServer,它的原理如下圖所示:


      PhysicalSocketServer主要是偵聽基於本地網卡的socket(libjingle中還有一些僞socket),然後分發socket事件到Dispatcher中。Dispatcher是PhysicalSocketServer的分發體,功能有點類似於MessageHandler。Dispatcher中定義了感興趣的socket事件和對應的處理。

類的關係

      本文提到的一些類的關係如下圖所示。Thread類繼承自MessageQue,可以通過Thread類來操作隊列消息。PhysicalSocketServer除了實現SocketServer接口之外,還可以添加和刪除Dispatcher。熟悉了這些類,基本上就瞭解Thread模塊和SocketServer模塊的工作原理。


  • 使用

      Thread的使用示例參見下面的代碼。獲取當前線程是通過Thread的Current函數,它會轉調ThreadManager對象的CurrentThread函數。由於Thread繼承自MessageQue,可以直接通過Thread對象來投遞消息,Post函數的第一個參數是OnMessage所處的對象,會被保存於Message對象中。處理消息只要重載MessageHandler的OnMessage函數即可。main函數則調用Thread的Run函數進入默認的消息處理循環,默認的消息處理循環在本示例中就是:循環取消息,調用MessageHandler的OnMessage函數。

#include <string>
#include <iostream>
#include "talk/base/thread.h"

class HelpData : public talk_base::MessageData
{
public:
  std::string info_;
};

class Police : public talk_base::MessageHandler
{
public:
  enum {
    MSG_HELP,
  };

  void Help(const std::string& info) {
    HelpData* data = new HelpData;
    data->info_ = info;
    talk_base::Thread::Current()->Post(this, MSG_HELP, data);
  }

  virtual void OnMessage(talk_base::Message* msg) {
    switch (msg->message_id) {
    case MSG_HELP:
      HelpData* data = (HelpData*)msg->pdata;
      std::cout << "MSG_HELP : " << data->info_ << std::endl;
      break;
    }
  }
};

int main(int argc, char** argv)
{
  Police p;
  p.Help("Please help me!");
  talk_base::Thread::Current()->Run();
  return 0;
}

  • 處理消息

        Thread的默認消息處理流程可用下圖表示。默認消息處理函數的入口爲Thread::Run(),另一個內嵌循環是SocketServer::Wait()。箭頭指向數據成員則表示,處理相關數據。


        要執行默認消息處理循環,使用下列語句即可:

  talk_base::Thread::Current()->Run();

        當然,你也可以自己處理消息,可以參見pcp例子中的代碼,這段代碼用在登錄階段,等待登錄操作完成。

  // Wait until login succeeds.
  std::vector<uint32> ids;
  ids.push_back(MSG_LOGIN_COMPLETE);
  ids.push_back(MSG_LOGIN_FAILED);
  if (MSG_LOGIN_FAILED == Loop(ids))
    FatalError("Failed to connect");

        首先設置關係的消息id集合,然後進入自定義的消息循環。

// Runs the current thread until a message with the given ID is seen.
uint32 Loop(const std::vector<uint32>& ids) {
  talk_base::Message msg;
  while (talk_base::Thread::Current()->Get(&msg)) {
    if (msg.phandler == NULL) {
      if (std::find(ids.begin(), ids.end(), msg.message_id) != ids.end())
        return msg.message_id;
      std::cout << "orphaned message: " << msg.message_id;
      continue;
    }
    talk_base::Thread::Current()->Dispatch(&msg);
  }
  return 0;
}

        每次循環中獲取消息,然後判斷消息的id是否後符合要求。符合要求就返回(表示登錄或登錄失敗),否則就派發消息(就是執行OnMessage函數,和Thread的默認消息處理一樣)。

  •         未完待續......

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章