OpenRTMFP/Cumulus Primer(4)CumulusServer啟動流程分析

鍾超發表於2012-04-14

OpenRTMFP/Cumulus Primer(4)CumulusServer啟動流程分析

  • 作者:柳大·Poechant(鍾超)
  • 部落格:Blog.CSDN.net/Poechant
  • 郵箱:zhongchao.ustc#gmail.com (# -> @)
  • 日期:April 14th, 2012

首先要知道的是,OpenRTMFP/Cumulus中使用到的庫有 Poco、OpenSSL 和 Lua。

1 main.cpp 中的 main() 函式

入口在 main.cpp 中:

int main(int argc, char* argv[]) {

先檢查記憶體洩露,不過目前這個開發中的專案還沒有實現這個功能,只是個空函式:

    DetectMemoryLeak();

然後會建立一個匿名的 CumulusServer 物件,並呼叫其 run() 函式,該函式由 CumulusServer 從 ServerApplication 中繼承而來,而 ServerApplication 由是從 Application 繼承而來的。CumulusServer 物件呼叫 run() 函式,實際是 ServerApplication 的 run() 函式,ServerApplication 的 run() 函式則是呼叫 Application 的函式,而該 run() 函式則是先呼叫 initialize() 函式,然後呼叫 main() 函式,然後呼叫 uninitialize() 函式。如果 initialize() 函式被呼叫時丟擲異常,則不會執行 main() 函式,但仍然會執行 uninitialize() 函式:

    // Runs the application by performing additional initializations
    // and calling the main() method.
    return CumulusServer().run(argc, argv);
}

2 main.cpp 中的 CumulusServer() 建構函式

CumulusServer 的建構函式定義為:

CumulusServer(): _helpRequested(false), // 顯示幫助資訊
                 _pCirrus(NULL),
                 _middle(false),
                 _isInteractive(true),
                 _pLogFile(NULL) {
}

3 main.cpp 中的 CumulusServer 的 initialize() 成員函式

在執行 main() 函式之前,CumulusServer 會啟動 initialize() 函式,傳入的引數就是 CumulusServer 自己,可以猜到 Poco::Util::Application 的 run 方法中,呼叫該函式時的引數是this

void initialize(Application& self) {

呼叫父函式 ServerApplication 的初始化函式:

    ServerApplication::initialize(self);

再繼續下面的原始碼分析之前,先要知道,根據Poco::Util::Application,Application 類有一些內建的配置屬性,如下:

  • application.path: 可執行檔案的絕對路徑;
  • application.name: 可執行檔案的檔名(含副檔名);
  • application.baseName: 可執行檔案的檔名(不含副檔名)
  • application.dir: 可執行檔案的所在目錄;
  • application.configDir: 配置檔案所在目錄;

所以下面就讀取了可執行檔案的所在目錄,其中第二個參數列示預設值(即當前目錄):

    string dir = config().getString("application.dir", "./");

然後讀取配置檔案,目錄為上一句所得到的dir,檔名(不含副檔名)為application.basename內建配置屬性值,其預設值為CumulusServer,然後加上點和副檔名.ini

    loadConfiguration(dir
        + config().getString("application.baseName", "CumulusServer")
        + ".ini");

這樣就載入完配置了。然後檢視當前的程式是從命令列執行的(命令列是互動的,所以是 interactive),還是以 daemon 方式執行的,這個函式是ServerApplication的一個成員函式:

    _isInteractive = isInteractive();

然後獲取表示日誌檔案所在目錄的字串,其中logs.directory是外接配置屬性(配置檔案中),其預設值為上面獲取到的可執行檔案路徑(一般為當前路徑)與logs的組合,即一般為當前目錄下的logs目錄:

    string logDir(config().getString("logs.directory", dir + "logs"));

建立日誌檔案目錄:

    File(logDir).createDirectory();

日誌檔案絕對路徑,logs為預設的日誌檔名(不含副檔名的部分),副檔名用0

    _logPath = logDir + "/" + config().getString("logs.name", "log") + ".";
    _pLogFile = new File(_logPath + "0");

用日誌流開啟日誌檔案(方式為追加寫入):

    _logStream.open(_pLogFile->path(), ios::in | ios::ate);

Logs 是一個方法類(其中的 public 函式都是靜態的),SetLogger的作用就是將Logs中的似有靜態成員設定為某個 Cumulus::Logger 物件(由於 CumulusServer 繼承了 Cumulus::Logger)。

    Logs::SetLogger(*this);
}

4 main.cpp 中的 CumulusServer 的 main() 成員函式

OpenRTMFP/Cumulus是一個基於Poco::Util::Application的服務端應用(準確的說是基於Poco::Util::ServerApplication的服務端應用)。如果沒有特殊的啟動要求,可以呼叫Poco/Application.h中定義的巨集POCO_APP_MAIN來完成初始化、日誌和啟動(該巨集會根據不同的平臺啟用不同的main()函式)。

run() 函式在呼叫完 initialize() 函式後,會呼叫 CumulusServer 中的 main() 函式,該 main() 函式的定義在 main.cpp 中:

int main(const std::vector<std::string>& args) {

首先看是否是要求幫助資訊,displayHelp是藉助Poco::Util::HelpFormatter實現的,CumulusServer的建構函式會在呼叫時將_helpRequested設定為false

    if (_helpRequested) {
        displayHelp();
    }

如果不是,則進入啟動狀態,首先建立一個RTMFPServerParams物件params,用來儲存OpenRTMFP/CumulusServer的基本配置資訊。

    else {
        try {
            RTMFPServerParams params;

params儲存CumulusServer的埠號和CumulusEdge的埠號:

            params.port = config().getInt("port", params.port);
            UInt16 edgesPort = config().getInt("edges.port",
                RTMFP_DEFAULT_PORT+1);
            if(config().getBool("edges.activated",false)) {
                if(edgesPort==0)
                    WARN("edges.port must have a positive value if \
                          edges.activated is true. Server edges is \
                          disactivated.");
                params.edgesPort=edgesPort;
            }

_pCirrusSocketAddress的成員,是封裝IP地址和埠號的物件。

            params.pCirrus = _pCirrus;

            params.middle = _middle;

UDB 所使用的緩衝區大小:

            params.udpBufferSize = config().getInt("udpBufferSize",
                params.udpBufferSize);

            params.keepAliveServer = config().getInt(
                "keepAliveServer",params.keepAliveServer);

            params.keepAlivePeer = config().getInt("keepAlivePeer",
                params.keepAlivePeer);

失敗前CumulusEdge的嘗試次數:

            params.edgesAttemptsBeforeFallback = config().getInt(
                "edges.attemptsBeforeFallback",
                params.edgesAttemptsBeforeFallback);

            Server server(config().getString("application.dir","./"),
                *this,config());

            server.start(params);

waitForTerminationRequest()函式是main()函式中必須呼叫的,意為等待終止執行的操作請求。

            // wait for CTRL-C or kill
            waitForTerminationRequest();

一旦接收到終止操作的請求,就會執行下面這句,用以退出OpenRTMFP/Cumulus的執行:

            // Stop the server
            server.stop();

catch 一些可能產生的異常:

        } catch(Exception& ex) {
            FATAL("Configuration problem : %s",ex.displayText().c_str());
        } catch (exception& ex) {
            FATAL("CumulusServer : %s",ex.what());
        } catch (...) {
            FATAL("CumulusServer unknown error");
        }
    }

OpenRTMFP/CumulusServer 停止執行:

    return Application::EXIT_OK;
}

-

轉載請註明來自柳大的CSDN部落格:Blog.CSDN.net/Poechant

-

相關文章