首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 开源软件 >

OpenRTMFP/Cumulus Primer(五)CumulusServer启动流程分析(续)

2012-07-27 
OpenRTMFP/Cumulus Primer(5)CumulusServer启动流程分析(续)OpenRTMFP/Cumulus Primer(5)CumulusServer启

OpenRTMFP/Cumulus Primer(5)CumulusServer启动流程分析(续)

OpenRTMFP/Cumulus Primer(5)CumulusServer启动流程分析(续)
  • 作者:柳大·Poechant(钟超)
  • 博客:Blog.CSDN.net/Poechant
  • 邮箱:zhongchao.ustc#gmail.com (# -> @)
  • 日期:April 14th, 2012

    1 CumulusServer 是 ServerApplication 的子类

    ServerApplication 对其子类有如下要求:

    • Subsystems must be registered in the constructor.
    • All non-trivial initializations must be made in the initialize() method.
    • At the end of the main() method, waitForTerminationRequest() should be called.

      2 ServerApplication 是 Application 的子类

      Application 对其子类的要求是,如下这些成员函数必须被覆盖:

      • initialize() (the one-argument, protected variant):上一篇已介绍过。
      • uninitialize():下面会介绍,Application 的 run() 函数会在调用 main() 函数后调用 uninitialize() 函数。
      • reinitialize()
      • defineOptions():定义命令行启动选项。
      • handleOption():响应相应的命令行选项。
      • main():

        3 反初始化

        CumulusServer是继承ServerApplication的,ServerApplication是继承Application的。Applicationrun()函数会先调用initialize(),然后调用main(),最后调用uninitialize。最后这个反初始化过程,在CumulusServer就是直接调用父类的uninitialize函数。

        void uninitialize() {    ServerApplication::uninitialize();}

        4 命令行选项设定

        CumulusServer的命令行选项有:log(l)、dump(d)、cirrus(c)、middle(m)、help(h)。

        void defineOptions(OptionSet& options) {    ServerApplication::defineOptions(options);

        设定日志级别(0 - 8,默认是 6,表示 info 级别)。

            options.addOption(        Option("log", "l", "Log level argument, must be beetween 0 and 8 : \            nothing, fatal, critic, error, warn, note, info, debug, trace. \            Default value is 6 (info), all logs until info level are displayed.")                .required(false)                .argument("level")                .repeatable(false));

        其他一些选项:

            options.addOption(        Option("dump", "d", "Enables packet traces in logs. Optional arguments \            are 'middle' or 'all' respectively to displays just middle packet \            process or all packet process. If no argument is given, just outside \            packet process will be dumped.",false,"middle|all",false)                .repeatable(false));    options.addOption(        Option("cirrus", "c", "Cirrus address to activate a 'man-in-the-middle' \            developer mode in bypassing flash packets to the official cirrus \            server of your choice, it's a instable mode to help Cumulus developers, \            "p2p.rtmfp.net:10000" for example. By adding the 'dump' argument, \            you will able to display Cirrus/Flash packet exchange in your logs \            (see 'dump' argument).",false,"address",true)                .repeatable(false));    options.addOption(        Option("middle", "m","Enables a 'man-in-the-middle' developer mode \            between two peers. It's a instable mode to help Cumulus developers. \            By adding the 'dump' argument, you will able to display Flash/Flash \            packet exchange in your logs (see 'dump' argument).")                .repeatable(false));

        显示帮助信息的选项:

            options.addOption(        Option("help", "h", "Displays help information about command-line usage.")            .required(false)            .repeatable(false));}

        OptionSetPoco::Util::OptionSet,调用addOption可以向其中增加选项Option。其中requiredrepeatable表示:

        • Sets whether the option is required (flag == true) or optional (flag == false).
        • Returns true if the option can be specified more than once, or false if at most once.

          当需要显示帮助信息时,调用如下函数:

          void displayHelp() {    HelpFormatter helpFormatter(options());    helpFormatter.setCommand(commandName());    helpFormatter.setUsage("OPTIONS");    helpFormatter.setHeader("CumulusServer, open source RTMFP server");    helpFormatter.format(cout);}
          • setCommand(): Sets the command name.
          • setUsage(): Sets the usage string.
          • setHeader(): Sets the header string.
          • format(): Writes the formatted help text to the given stream.

            5 处理命令行选项

            参数是选项名和选项值。

            void handleOption(const std::string& name, const std::string& value) {    ServerApplication::handleOption(name, value);

            如果选项是帮助:

                if (name == "help")        _helpRequested = true;

            如果是cirrus,即该服务的 IP 和端口号,Poco::URI 中有协议名(Scheme)、IP 地址(Host)、端口号(Port)、查询串(Query)等等。

                else if (name == "cirrus") {        try {            URI uri("rtmfp://"+value);            _pCirrus = new SocketAddress(uri.getHost(),uri.getPort());            NOTE("Mode 'man in the middle' : the exchange will bypass to '%s'",value.c_str());        } catch(Exception& ex) {            ERROR("Mode 'man in the middle' error : %s",ex.message().c_str());        }

            如果选项是dump日志:

                } else if (name == "dump") {        if(value == "all")            Logs::SetDump(Logs::ALL);        else if(value == "middle")            Logs::SetDump(Logs::MIDDLE);        else            Logs::SetDump(Logs::EXTERNAL);

            如果选项是middle

                } else if (name == "middle")        _middle = true;

            如果选项是log,表示设定日志级别:

                else if (name == "log")        Logs::SetLevel(atoi(value.c_str()));}

            6 Dump logs

            先加一个作用域锁,然后再向日志流写数据。

            void dumpHandler(const UInt8* data,UInt32 size) {    ScopedLock<FastMutex> lock(_logMutex);    cout.write((const char*)data, size);    _logStream.write((const char*)data,size);    manageLogFile();}

            调用 manageLogFile,主要做一些日志大小超出限制的处理。

            void manageLogFile() {

            先判断是否超过日志文件的大小上线,LOG_SIZE1000000字节(即约 1 MB)。

                if(_pLogFile->getSize() > LOG_SIZE) {        _logStream.close();        int num = 10;

            打开新日志文件:

                    File file(_logPath + "10");

            如果该文件已经存在,则先删除:

                    if (file.exists())            file.remove();        while (--num >= 0) {            file = _logPath + NumberFormatter::format(num);            if (file.exists())                file.renameTo(_logPath + NumberFormatter::format(num + 1));        }        _logStream.open(_pLogFile->path(), ios::in | ios::ate);    }   }

            7 停止运行

            CumulusServer继承了ApplicationKiller,该类中有纯虚函数kill()需要被实现,于是有:

            void kill() {    terminate();}

            ApplicationKiller的定义在ApplicationKiller.h中,如下:

            class ApplicationKiller {public:    ApplicationKiller(){}    virtual ~ApplicationKiller(){}    virtual void kill()=0;};

            8 载入配置

            initialize()函数中调用,上一篇已提到过。

            void loadConfiguration(const string& path) {    try {        ServerApplication::loadConfiguration(path);    } catch(...) {    }}

            9 处理日志
            void logHandler(Thread::TID threadId,                const std::string& threadName,                Priority priority,                const char *filePath,                long line,                 const char *text) {

            作用域锁:

                ScopedLock<FastMutex> lock(_logMutex);    Path path(filePath);    string file;    if (path.getExtension() == "lua")        file += path.directory(path.depth()-1) + "/";

            如果是命令行交互模式(即不是 daemon 模式):

                if (_isInteractive)        printf("%s  %s[%ld] %s\n",            g_logPriorities[priority - 1],            (file + path.getBaseName()).c_str(),            line,            text);

            向日志流输出一句日志:

                _logStream << DateTimeFormatter::format(LocalDateTime(),"%d/%m %H:%M:%S.%c  ")            << g_logPriorities[priority-1]             << '\t' << threadName             << '(' << threadId << ")\t"            << (file + path.getFileName())             << '[' << line << "]  "             << text << std::endl;    _logStream.flush();

            日志文件的善后处理(主要处理文件大小限制可能产生的问题):

                manageLogFile();}

            -

            转载请注明来自柳大的CSDN博客:Blog.CSDN.net/Poechant

            -

热点排行