7回调函数
回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。同步调用是三者当中最简单的,而回调又常常是异步调用的基础。
回调接口的原理很简单,ICE客户和服务器都具有双重角色,即充当服务器也是客户客户。对一些简单的异步处理过程,我们可以通过回调机制来实现。
7.1 定义接口文件
#pragma once
module Demo
{
interface CallbackReceiver
{
void callback();
};
interface CallbackSender
{
void initiateCallback(CallbackReceiver* proxy);
void shutdown();
};
};
7.2 服务端实现代码
#include <Ice/Ice.h>
#include <CallbackSenderI.h>
using namespace std;
using namespace Demo;
class CallbackServer : public Ice::Application
{
public:
virtual int run(int, char*[]);
};
int
main(int argc, char* argv[])
{
CallbackServer app;
return app.main(argc, argv);
}
int
CallbackServer::run(int argc, char*[])
{
if (argc > 1)
{
cerr << appName() << ": too many arguments" << endl;
return EXIT_FAILURE;
}
Ice::ObjectAdapterPtr adapter = communicator()->createObjectAdapter("Callback.Server");
CallbackSenderPtr cbs = new CallbackSenderI;
adapter->add(cbs, communicator()->stringToIdentity("callbackSender"));
adapter->activate();
communicator()->waitForShutdown();
return EXIT_SUCCESS;
}
7.3 客户端实现代码
#include <Ice/Ice.h>
#include <callback.h>
using namespace std;
using namespace Demo;
class CallbackReceiverI : public CallbackReceiver
{
public:
virtual void callback(const Ice::Current&)
{
cout << "received callback" << endl;
}
};
class CallbackClient : public Ice::Application
{
public:
CallbackClient();
virtual int run(int, char*[]);
private:
void menu();
};
int
main(int argc, char* argv[])
{
CallbackClient app;
return app.main(argc, argv);
}
//
// Since this is an interactive demo we don't want any signal
// handling.
//
CallbackClient::CallbackClient() :
Ice::Application(Ice::NoSignalHandling)
{
}
int
CallbackClient::run(int argc, char*[])
{
if (argc > 1)
{
cerr << appName() << ": too many arguments" << endl;
return EXIT_FAILURE;
}
string strProxy = "callbackSender:default -h 192.168.11.100 -p 10000";
Ice::CommunicatorPtr ic = communicator();
Ice::ObjectPrx base = ic->stringToProxy(strProxy);
CallbackSenderPrx sender = CallbackSenderPrx::checkedCast(base);
if (!sender)
{
cerr << appName() << ": invalid proxy" << endl;
return EXIT_FAILURE;
}
string strEndpoints = "default -h 192.168.11.200 –p 10001";
Ice::ObjectAdapterPtr adapter = ic->createObjectAdapterWithEndpoints("Callback.Client", strEndpoints);
CallbackReceiverPtr cr = new CallbackReceiverI;
adapter->add(cr, ic->stringToIdentity("callbackReceiver"));
adapter->activate();
CallbackReceiverPrx receiver = CallbackReceiverPrx::uncheckedCast(
adapter->createProxy(ic->stringToIdentity("callbackReceiver")));
menu();
char c;
do
{
try
{
cout << "==> ";
cin >> c;
if (c == 't')
{
sender->initiateCallback(receiver);
}
else if (c == 's')
{
sender->shutdown();
}
else if (c == 'x')
{
// Nothing to do
}
else if (c == '?')
{
menu();
}
else
{
cout << "unknown command `" << c << "'" << endl;
menu();
}
}
catch (const Ice::Exception& ex)
{
cerr << ex << endl;
}
}
while (cin.good() && c != 'x');
return EXIT_SUCCESS;
}
void
CallbackClient::menu()
{
cout <<
"usage:\n"
"t: send callback\n"
"s: shutdown server\n"
"x: exit\n"
"?: help\n";
}
7.4 ACM(Active Connection Management)问题
192.168.11.200:客户端IP地址,192.168.11.100:服务端IP地址。ICE内部有一个连接监视线程,查看当前端点的溢出时间控制。
上面客户端控制台第一次发送“t”命令时,网络连接状态如下:
TCP 192.168.11.200:10001 192.168.11.100:2761 ESTABLISHED
TCP 192.168.11.200:17531 192.168.11.100:10000 ESTABLISHED
默认60秒之后,连接即将断开:
TCP 192.168.11.200:10001 0.0.0.0:0 LISTENING
ICE的ACM客户端连接服务器,Ice.ACM.Client=num表示客户端到服务器的连接空闲num秒后就会自动关闭,再次通讯时又会连接上。默认为60秒,一分钟。如果是0,表示客户端ACM不启用。服务端ACM Ice.ACM.Server = N,当客户端到服务端N秒没有数据发送时,服务端自动关闭连接。如果是0,表示服务端ACM不启用。
因此,上述代码我们可以设置属性,定义连接时间:
//Ice::CommunicatorPtr ic = communicator();
Ice::PropertiesPtr props = Ice::createProperties(argc, argv);
props->setProperty("Ice.ACM.Server","0"); //客户端与服务端保持连接时间(0禁止)
props->setProperty("Ice.ACM.Client","0"); //服务端与客户端保持连接时间(0禁止)
Ice::InitializationData id;
id.properties = props;
Ice::CommunicatorPtr ic = Ice::initialize(id);
8双向连接(Bidirectional Connections)
ICE的连接通常允许请求只在一个方向流动。如果一个应用程序的设计需要服务器会回调到客户端,服务器通常建立新的连接(如4章节的回调函数)。
图8.1 callback
不幸的是当我们在网络限制条件下,服务器与客户端之间,往往只允许创建一个单独的连接。如图5.2所示,当客户端位于防火墙后面。
图8.2 与防火墙的回调函数。
在两个双向连接流的方向上提供可能要求,以客户端用户现有的连接,使服务器发送到客户端回调请求。
定义一个接口Callback.ice:
#pragma once
#include <Ice/Identity.ice>
module Demo
{
interface CallbackReceiver
{
void callback(int num);
};
interface CallbackSender
{
void addClient(Ice::Identity ident);
};
};