作者: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源码

Ice笔记-利用Ice::Application类简化Ice应用相关推荐

  1. Nhibernate 3.0 Cookbook学习笔记 利用XML映射类

    本文基于Nhibernate 3.0 Cookbook,下载地址:http://home.cnblogs.com/group/topic/42850.html 准备工作,下载Nhibernate:ht ...

  2. Ice笔记---异步程序设计demo

    最近学习了异步通信这一章:异步包括服务端的异步分派(AMD)以及客户端的异步调用(AMI). 下面介绍一下如何实现简单的异步通信(主要实现了客户端的异步调用,AMD暂未体现): 1.首先编写Ice文件 ...

  3. 详细教你如何部署ICE服务(二)---IceBox加载启动Ice服务

    在上一篇文章中,我使用了自己实现的一个简单容器简单粗暴的启动一个ICE服务,正如上篇文章所总结的那样,这种部署服务的方式存在诸多缺点. 这里我们给出使用IceBox这个框架来开发和部署我们的Ice服务 ...

  4. Spring框架学习笔记03:初探Spring——利用注解配置类取代Spring配置文件

    文章目录 一.课程引入 二.利用注解配置类取代Spring配置文件 (一)打开项目[SpringDemo2021] (二)创建net.hw.spring.lesson03包 (三)移植上一讲的接口和类 ...

  5. Android学习笔记(三)Application类简介

    每次运行APP时,Application类都保持实例化状态.与Activity不同,配置改变不会导致应用程序重启.通过继承Application类,可以完成一下3项工作: · 对Android运行时广 ...

  6. JDBC学习笔记02【ResultSet类详解、JDBC登录案例练习、PreparedStatement类详解】

    黑马程序员-JDBC文档(腾讯微云)JDBC笔记.pdf:https://share.weiyun.com/Kxy7LmRm JDBC学习笔记01[JDBC快速入门.JDBC各个类详解.JDBC之CR ...

  7. Effective Java(第三版) 学习笔记 - 第四章 类和接口 Rule20~Rule25

    Effective Java(第三版) 学习笔记 - 第四章 类和接口 Rule20~Rule25 目录 Rule20 接口优于抽象类 Rule21 为后代设计接口 Rule22 接口只用于定义类型 ...

  8. Unity API详解——Application类

    Unity对API的合理利用不仅可以减轻编码负担,而且往往可以提高程序的运行效率,尽管官方给出了较为丰富的API文档,然而这并不能满足实际开发的需要,因为官方给出的API解释往往只描述相应的API的主 ...

  9. 我的Unity学习笔记----------API常用方法和类详解(四)

    二十三. Camera组件(类) 通过获取camera组件把场景中鼠标的位置(点)转化为射线 mainCamera=GameObject.Find("MainCamera").Ge ...

最新文章

  1. 开源:数据可视化分析平台 DataGear 1.11.1 发布
  2. POJ 计算几何专项训练(1) 【2318】【2398】【3304】【2653】【1556】【1066】...
  3. PYPL 二月榜单发布:最受欢迎的编程语言、IDE 和数据库都是哪些
  4. Google图片搜索的原理
  5. NETSH WINSOCK RESET这条命令的含义和作用?
  6. python dictionary的遍历
  7. 转载:Android Studio 3.1.2 新项目报错 AAPT2 error: check logs for details (Gradle 3.1.2)
  8. nofollow标签_SEO技术动态:谷歌升级Nofollow标签的作用
  9. MuPlayer『百度音乐播放内核』
  10. Window操作系统注册表学习
  11. SQLServer用COMPUTE 和 COMPUTE BY 汇总数据
  12. devexpress控件使用笔记
  13. 正义网评咪蒙公号注销:自媒体创作须守住道德底线
  14. 手把手教你禁止访问某个网站
  15. 彻底清除 mplay.com与mplay.exe病毒
  16. 二元一次方程有唯一解的条件_线性方程组在什么时候有唯一解/无穷个解/无解?...
  17. 【文摘】2008年度_Atom处理器
  18. 备受欢迎的随心金融P2P
  19. 使用微软DNS服务器解决Hotmail、微软账户登录页面无法访问的问题
  20. 布袋除尘器过滤风速多少_布袋除尘器过滤风速一般多大

热门文章

  1. php中怎么定义page,css @page的使用与定义详解
  2. python企业微信回调_python 微信企业号-回调模式接收微信端客户端发送消息并被动返回消息...
  3. 局部特征(local feature)
  4. Vue.js 过滤器
  5. InnoDB 存储引擎体系架构
  6. 教你认清MVC,MVP和MVVM
  7. 跟vczh看实例学编译原理——一:Tinymoe的设计哲学
  8. python closure闭包 lambda表达式
  9. winform DataGrid排序、去掉第一的空白列
  10. 专访阿里云萧少聪、曹龙:一家云厂商对入局数据库做了哪些思考?