Ice笔记-利用Ice::Application类简化Ice应用

作者:ydogg,转载请申明。

 

在编写Ice相关应用时,无论是Client还是Server端,都必须进行一些必要的动作,如:Ice通信器初始化、异常捕获,以及应用终止后的销毁。鉴于每个应用都需要,Ice运行时库提供了Ice::Application类来解放用户,避免重复劳动,消除繁琐的初始化和销毁细节。Ice::Application虽然实用,但总体来说是个比较简单的类,主要提供了Ice通信器初始化和信号捕获处理两大功能。下面将从功能和实现两方面进行阐述,并给出常见用法和注意事项。源码版本为Ice-3.2.1。

 

一.Ice::Application概述

Ice::Application本身是一个抽象类,其run()函数为纯虚函数,因此必须被继承后使用。

Ice::Application 是一个单体(singleton)类,会创建单个通信器。 如果你要使用多个通信器,不能使用Ice::Application来定义多个App。而至多定义一个App的实例。

其它通信器需要使用Ice::initialize()手工生成。

 

二.Ice::Application的成员

Ice::Application无真正成员变量,其实际使用变量均在实现文件中以静态形式提供。因此其提供的主要是静态接口 



// Application的入口函数,提供了丰富的初始化方式,一般使用第一个

// 将应用主函数参数直接传入即可

int main(int, char*[]);

int main(int, char*[], const char*);

int main(int, char*[], const Ice::InitializationData&);

int main(int, char*[], const char*, const Ice::LoggerPtr&);

int main(const StringSeq&);

int main(const StringSeq&, const char*);

int main(const StringSeq&, const Ice::InitializationData&);


 // 应用的执行循环,应用需要继承这个函数并用自己的逻辑重写

virtual int run(int, char*[]) = 0;


 // 信号回调函数

// 如果需要自己对信号进行处理,则需要继承和改写这个函数

// 注意,需在run()函数中调用callbackOnInterrupt()来向Ice表示使用用户回调

// 该函数的默认实现是空函数

virtual void interruptCallback(int);

 

// 返回应用名,即argv[0]

static const char* appName();


 // 返回当前使用的Ice通信器实例指针

static CommunicatorPtr communicator();


 // 设置信号处理模式

//

// 销毁模式:信号到来时将通信器实例销毁,也是Application的默认模式

static void destroyOnInterrupt();


// 关闭模式:信号到来时将通信器实例关闭,但不销毁

static void shutdownOnInterrupt();


// 忽略模式:信号到来时将通信器不做任何处理

static void ignoreInterrupt();


// 用户模式:信号到来时将调用interruptCallback()函数

static void callbackOnInterrupt();


 // 信号的阻止和放开,不常用

// 阻塞信号的到来

static void holdInterrupt();


// 放开被阻塞的信号

static void releaseInterrupt();


 // Application当前是否被信号中断

// 可用于判断Application的结束是否由于信号造成

static bool interrupted();


三.使用方法

一般直接初始化通信器的用法如下:



#include <Ice/Ice.h>

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

{

       int status = 0;

       Ice::CommunicatorPtr ic;

       try {

              ic = Ice::initialize(argc, argv);


              // Server code here...


              // ...


        } catch (const Ice::Exception & e) {

              cerr << e << endl;

              status = 1;

       }


        if (ic)

              ic->destroy();

       return status;

}

 

使用Ice::Application的代码如下:

 




Ice笔记-利用Ice::Application类简化Ice应用_异常捕获#include <Ice/Ice.h>

Ice笔记-利用Ice::Application类简化Ice应用_异常捕获class MyApplication : virtual public Ice::Application

Ice笔记-利用Ice::Application类简化Ice应用_sed_03{

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04public:

Ice笔记-利用Ice::Application类简化Ice应用_异常捕获_05       virtual int run(int, char * []) {

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04       // 如果需要,设置信号回调模式

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04              interruptCallback();

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04              // ignoreInterrupt();

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04              // Add Server code here...

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04              // ...

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04              return 0;

Ice笔记-利用Ice::Application类简化Ice应用_静态变量_15       }

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04     

Ice笔记-利用Ice::Application类简化Ice应用_异常捕获_05      virtual void interruptCallback(int) {

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04             cout << appName() << “ receive signal ” << endl;

Ice笔记-利用Ice::Application类简化Ice应用_静态变量_15      }

Ice笔记-利用Ice::Application类简化Ice应用_静态变量_20};

Ice笔记-利用Ice::Application类简化Ice应用_异常捕获

Ice笔记-利用Ice::Application类简化Ice应用_异常捕获int main(int argc, char * argv[])

Ice笔记-利用Ice::Application类简化Ice应用_sed_03{

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04       MyApplication app;

Ice笔记-利用Ice::Application类简化Ice应用_初始化_04       return app.main(argc, argv);

Ice笔记-利用Ice::Application类简化Ice应用_静态变量_20}

可以看出,繁琐的初始化细节已经不用考虑。抽象层次也更清晰一些。

四.实现分析

main的实现较多,但都是对函数

int main(int, char*[], const Ice::InitializationData&)的再包装,其行为

如下:

①        创建一个IceUtil::CtrlCHandler,适当地关闭通信器。

②        保存传入的argv[0]参数。以便通过静态的appName 成员函数,提供应用的名字。

③        初始化(通过调用Ice::initialize)。通过调用静态的communicator()成员,可以访问当前使用的通信器。

④        扫描参数向量,查找与Ice run time 有关的选项,并移除这样的选项。因此,在传给你的run 方法的参数向量中,不再有与Ice 有关的选项,而只有针对你的应用的选项和参数。

实际上,3,4步骤都由同一个函数Ice::initialize来完成。

⑤        调用run()函数

⑥        销毁通信器(如果正常结束,没有收到终止信号)

 

在以上过程中,main()函数还捕获了几乎全部异常,包括IceUtil::Exception,std::exception,甚至还有const char*和const string&。

函数代码如下:



int

Ice::Application::main(int argc, char* argv[], const InitializationData& initData)

{

    // 不允许重复调用

    if(_communicator != 0)

    {

        cerr << argv[0] << ": only one instance of the Application class can be used" << endl;

        return EXIT_FAILURE;

    }

    int status;


    try

    {

        // 设置信号捕捉器

         CtrlCHandler ctrCHandler;

        _ctrlCHandler = &ctrCHandler;


        try

        {   // 内部使用的条件变量初始化,主要用于信号阻塞

            if(_condVar.get() == 0)

            {

                _condVar.reset(new Cond);

            }


            // 初始化Ice通信器及其它变量(均为静态变量)

            _interrupted = false;

            _appName = argv[0];    // 设置应用名

                

            _application = this;

            _communicator = initialize(argc, argv, initData);

            _destroyed = false;


            // 判断应用是否提供了Ice.Nohup参数

            // 如果Ice.Nohup大于0, Application会忽略SIGHUP(UNIX) 和 

           // CTRL_LOGOFF_EVENT (Windows). 因此,如果启动应用的用户注销,

           // 设置了Ice.Nohup 的应用能继续运行(只适用于C++)。

             _nohup = (_communicator->getProperties()->getPropertyAsInt("Ice.Nohup") > 0);

        

            // 收到信号的默认处理方式是销毁通信器

            destroyOnInterrupt();

            status = run(argc, argv);

        }

        catch(const IceUtil::Exception& ex)

        {

            cerr << _appName << ": " << ex << endl;

            status = EXIT_FAILURE;

        }

        catch(const std::exception& ex)

        {

            cerr << _appName << ": std::exception: " << ex.what() << endl;

            status = EXIT_FAILURE;

        }

        catch(const std::string& msg)

        {

            cerr << _appName << ": " << msg << endl;

            status = EXIT_FAILURE;

        }

        catch(const char* msg)

        {

            cerr << _appName << ": " << msg << endl;

            status = EXIT_FAILURE;

        }

        catch(...)

        {

            cerr << _appName << ": unknown exception" << endl;

            status = EXIT_FAILURE;

        }


        // Application清理时,需要忽略所有信号

       ignoreInterrupt();

        {

            StaticMutex::Lock lock(_mutex);

            while(_callbackInProgress)

            {

                _condVar->wait(lock);

            }

            if(_destroyed)

            {

                _communicator = 0;

            }

            else

            {

                _destroyed = true;

                //

                // And _communicator != 0, meaning will be destroyed

                // next, _destroyed = true also ensures that any

                // remaining callback won't do anything

                //

            }

            _application = 0;

        }


       // 清理通信器(如果没有通过信号清理过)

        if(_communicator != 0)

        {  

            try

            {

                _communicator->destroy();

            }

            catch(const IceUtil::Exception& ex)

            {

                cerr << _appName << ": " << ex << endl;

                status = EXIT_FAILURE;

            }

            catch(const std::exception& ex)

            {

                cerr << _appName << ": std::exception: " << ex.what() << endl;

                status = EXIT_FAILURE;

            }

            catch(...)

            {

                cerr << _appName << ": unknown exception" << endl;

                status = EXIT_FAILURE;

            }

            _communicator = 0;

        }


        //

        // Set _ctrlCHandler to 0 only once communicator->destroy() has completed.

        // 

        _ctrlCHandler = 0;

    }

    catch(const CtrlCHandlerException&)

    {

        cerr << argv[0] << ": only one instance of the Application class can be used" << endl;

        status = EXIT_FAILURE;

    }

   

    return status;

}


IceUtil::CtrlCHandler的实现在IceUtil/CtrlCHandler.cpp中,其在windows下使用SetConsoleCtrlHandler()方式实现,可捕获CTRL_C_EVENT、CTRL_BREAK_EVENT、CTRL_CLOSE_EVENT、CTRL_LOGOFF_EVENT以及CTRL_SHUTDOWN_EVENT信号。

在linux下,通过pthread_sigmask()和sigwait()配合实现,注意实现中使用了一个内部的独立线程对信号进行捕获。其选择捕获的信号有SIGHUP、SIGINT、SIGTERM。其它Ice::Application的信号模式设置函数都是利用它来挂接自己的处理函数,来做出不同的动作。在此不再细述,请参见源码。

 

五.参考文献

Ice-1.3.0中文手册(马维达,感谢他的无私贡献)

Ice-3.1.1英文手册

Ice-3.2.1源码