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

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的代码如下:

 

#include <Ice/Ice.h>
class MyApplication : virtual public Ice::Application
{
public:
       virtual int run(int, char * []) {

       // 如果需要,设置信号回调模式
              interruptCallback();
              // ignoreInterrupt();

              // Add Server code here...
              // ...

              return 0;
       }
     
      virtual void interruptCallback(int) {
             cout << appName() << “ receive signal ” << endl;
      }
};

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

       MyApplication app;
       return app.main(argc, argv);
}

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

四.实现分析

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源码

时间: 2024-08-24 01:31:25

Ice笔记-利用Ice::Application类简化Ice应用的相关文章

ice c++ 服务器 代理-ICE客户端 如何穿透代理服务器 连接到ICE服务器

问题描述 ICE客户端 如何穿透代理服务器 连接到ICE服务器 大家好,我在使用ICE技术,现在遇到一个问题,但是不知道如何来解决,跪求大家帮我解答下: 我的服务器的IP地址是58.19.110.34 端口:10000,如果我想连接服务器,我可以采用以下的代码:Ice::ObjectPrx base = ic->stringToProxy(""ClientFactory :tcp -h 58.19.110.34 -p 10000""); ClientFacto

如何利用Rational Application Developer将现有的静态Web站点转变为动态Web站

如何利用Rational Application Developer将现有的静态Web站点转变为动态Web站点 本文将帮助技术背景的读者了解如何利用 IBM Rational Application Developer 将现 有的静态 Web 站点转变为动态 Web 站点.用户将添加一个邮件 servlet.Java2 Platform, Enterprise Edition (J2EE) 安全性,并利用 Web 站点设计工具的页面模板来为整个 Web 站点 创建一致的外观. 与静态 Web 站

Sql server存储过程和C#分页类简化你的代码

server|sql|存储过程|分页 Sqlserver存储过程和C#分页类简化你的代码! 在最近的项目中,由于要用到自定义分页的功能,本人就在网上找了个存储过程.结合C#写了个分页类.由于本人第一次写文章.写得不好,大家不要扔鸡蛋.. 下面是存储过程(sqlserver2000下通过) --最通用的分页存储过程 -- 获取指定页的数据   CREATE PROCEDURE Pagination   @tblName   varchar(255),       -- 表名   @strGetFi

Android中用Application类实现全局变量

在Java中如果要使用全局变量,一般定义public static类型的变量.但是这种方法不符合Android的框架 架构,Android中要使用Application context. Application是一个基类,这个基类的作用是获取整个 App的状态,我们需要自己定义一个类来继承这个基类.代码如下: package com.tianjf; import android.app.Application; public class MyApplication extends Applica

利用Rational Application Developer Visual Editor实现Swing可视化…

利用 Rational Application Developer Visual Editor 实现 Swing 可视化组件与数据的绑定 引言 Java可视化编辑器允许用户以图形化的方式编辑 Java 用户界面.在 IBM Rational Application Developer for WebSphere Software V6 中,这一功能得到了进一步的增强.因为 V6 支持可视化组件与不 同来源的数据快速绑定,比如,这些数据可以来源于 Web 服务,也可以来源于 EJB 组件.这篇文章

在Ruby中利用Net::SMTP类发送电子邮件的教程

  这篇文章主要介绍了在Ruby中利用Net::SMTP类发送电子邮件的教程,包括类中所带方法的用法介绍,需要的朋友可以参考下 简单邮件传输协议(SMTP)发送电子邮件及路由的e-mail邮件服务器之间的协议处理. Ruby 提供 Net::SMTP 类的简单邮件传输协议(SMTP)客户端的连接,并提供了两个新的方法:new 和 start. new 带两个参数: server name 默认为 localhost port number 默认为熟知的 25 start 方法带有以下这些参数:

利用Excel.Application object’s RegisterXLL() method加载dll

本文讲的是利用Excel.Application object's RegisterXLL() method加载dll,Ryan Hanson‏@ryHanson最近分享了一个技巧,利用Excel.Application object's RegisterXLL()能够加载dll.我对其分享的POC作了测试,接着做了扩展,添加功能实现远程下载执行,并且分析该方法相关的利用技巧,详细介绍脚本开发中的细节. 0x01 简介 本文将要介绍如下内容: · POC测试 · 添加功能实现远程下载执行 · 扩

Windows Shellcode学习笔记——利用VirtualAlloc绕过DEP

本文讲的是Windows Shellcode学习笔记--利用VirtualAlloc绕过DEP, 0x00 前言 接着介绍DEP绕过的另一种方法--利用VirtualAlloc绕过DEP.通过VirtualAlloc函数可以申请一段具有可执行属性的内存,相比于VirtualProtect,传入VirtualAlloc的四个参数不需要先读取再赋值,可在shellcode中直接指定,结构更简单.当然,利用Immunity Debugger的mona插件可自动构造利用VirtualAlloc绕过DEP

Android Application类的详细介绍_Android

Android Application类详解: Android中Application类的详细解释: 我们在平时的开发中,有时候可能会需要一些全局数据,来让应用中的所有Activity和View都能访问到,大家在遇到这种情况时,可能首先会想到自己定义一个类,然后创建很多静态成员. 但是这种方法不符合Android的框架架构,不过andorid已经为我们提供了这种情况的解决方案:在Android中,有一个名为Application的类,我们可以在Activity中使用getApplication(