服务端异步应答示例
本章节主要介绍使用中间件开发异步应答功能的方法。
开发示例
本例中 MsgHook
调用 msg_server
的 /warningLampStatus
接口获取警示灯状态然后对当前服务的 /light
RPC 执行应答。具体流程如下。
- 创建 QVSOA 服务器
light_server
。 - 创建 QVSOA Mware,依次绑定
LogHook
和MsgHook
两个监听器。 - 当客户端 RPC 请求
/light
时,LogHook
将记录 URL 到日志文件并向MsgHook
传递数据。 MsgHook
打印相应的数据并使用 RPC 请求msg_server
的/warningLampStatus
接口同时添加resolve
作为 RPC 的回调数据。- 当
msg_server
响应 RPC 时,通过resolve
对/light
的 RPC 执行应答。
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QProcessEnvironment>
#include <QVsoa>
constexpr char SERVER_INFO[] = "{\"name\":\"light_server\"}";
constexpr char SERVER_PASSWORD[] = "123456";
constexpr char SERVER_ADDR[] = "0.0.0.0";
constexpr int SERVER_PORT = 3001;
class LogHook : public IQVsoaMiddlewareListener
{
public:
LogHook()
: m_file(QString("%1/log_file").arg(QDir::tempPath()))
{
if (!m_file.open(QIODevice::Append | QIODevice::Text)) {
exit(-1);
}
}
static void freeLogData(void *data, const QString &)
{
free(data);
}
bool hook(const QVsoaServer *,
QPointer<QVsoaCliHandle>,
const QVsoaHeader &,
const QString &url,
const QVsoaPayload &,
QVsoaMiddlewareResolve &resolve) override
{
if (url.length() > 0) {
auto data = QString("URL: %1\n").arg(url);
m_file.write(data.toUtf8());
m_file.flush();
// Add a new message data to next handler
resolve.addData("log", strdup("new resolved data"), LogHook::freeLogData);
}
// return true to continue processing next hook
return true;
}
private:
QFile m_file;
};
class MsgHook : public QObject,
public IQVsoaMiddlewareListener
{
Q_OBJECT
public:
MsgHook()
: m_client{},
m_invoker{&m_client, "/warningLampStatus", RPCMethod::GET}
{
connect(&m_client, &QVsoaClient::connected, this, &MsgHook::onConnected);
connect(&m_invoker, &QVsoaClientRPCInvoker::serverReply, this, &MsgHook::onServerReply);
m_client.connect2server("vsoa://msg_server", {}, 500);
m_client.autoConnect(1000, 100);
}
bool hook(const QVsoaServer *,
QPointer<QVsoaCliHandle> client,
const QVsoaHeader &header,
const QString &,
const QVsoaPayload &,
QVsoaMiddlewareResolve &resolve) override
{
// get message data from previous callback
char *msg = static_cast<char *>(resolve.getData("log"));
if (msg) {
qDebug() << QString::asprintf("Log resolved data (key: \"log\") : %s\n", msg);
}
m_invoker(QVsoaPayload{}, QVariant::fromValue(resolve));
return false;
}
void onConnected(bool ok, QString info)
{
qDebug() << "On connect, connect:" << ok << "info:" << info;
}
void onServerReply(const QVsoaHeader header, const QVsoaPayload payload, const QVariant arg)
{
auto resolve = arg.value<QVsoaMiddlewareResolve>();
if (header.isInvalid()) {
qDebug() << "VSOA server /warningLampStatus reply timeout!";
return;
}
resolve.reply(StatusCode::SUCCESS, header.seqno(), payload);
}
private:
QVsoaClient m_client;
QVsoaClientRPCInvoker m_invoker;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// Initialize server
QVsoaServer server(SERVER_INFO);
if (server.isInvalid()) {
qDebug() << "Can not create VSOA server!";
return -1;
}
// If need password
server.setPassword(SERVER_PASSWORD);
LogHook logHook;
MsgHook msgHook;
// add mware rpc hook
// 'logHook' will be called first, if 'logHook' returns 'true'
// then 'msgHook' will be called
QVsoaMiddleware mware;
mware.addListener(&logHook);
mware.addListener(&msgHook);
server.addMiddleware("/light", &mware);
int port = SERVER_PORT;
int auto_port = QProcessEnvironment::systemEnvironment().value("VSOA_AUTO_PORT", "-1").toInt();
if (auto_port != -1) {
port = auto_port;
}
// Start server
if (!server.start(QVsoaSocketAddress(AF_INET, SERVER_ADDR, port))) {
qDebug() << "Can not start VSOA server!";
return -1;
}
qDebug() << "Started VSOA server.";
return a.exec();
}
说明:
msg_server
可参考RPC 服务端范例开发。