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