- 摘要
本文主要分析了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的默認消息處理一樣)。
- 未完待續......