服务端开发
本节内容介绍 VSOA 服务端使用 Mware 的方法。
开发须知
- 使用 Mware,可以将多个 RPC 回调函数绑定至一个 URL 中,在 URL 被 RPC 请求后,回调函数按照绑定的先后顺序依次执行。
- 在按序执行回调函数的过程中,函数可以使用 Mware 提供的接口,将数据以键值对的形式传递给下一个回调函数。若回调函数返回“true” ,则执行继续;若回调函数返回“false”,则执行中止。
常用接口
VSOA Mware 接口详细说明可参考 VSOA C 语言编程手册,源码可见 VSOA Mware 头文件。
开发示例
在本示例中,服务端执行以下流程:
- 打开“/tmp/log_file”文件用于存储日志信息。
- 创建 VSOA Mware,绑定两个回调函数
log_callback
和msg_callback
到 URL/light
上。 - 创建 VSOA 服务器
light_server
。当创建成功时,服务端会打印Started VSOA stream server
。 - 监听 RPC URL
/light
,当 URL 被请求时,调用log_callback
回调函数。 - 在
log_callback
中,记录 URL 到日志文件,再向下一个回调函数传递数据。数据键为log
,值为new resolved data
。最后,返回“true” 继续执行下一个回调。 - 在
msg_callback
中,获取并打印上一个回调传递的数据。最后响应客户端,并返回“false”结束执行。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "vsoa_server.h"
#include "vsoa_mware.h"
static vsoa_server_t *server;
bool log_callback(void *arg, vsoa_server_t *server, vsoa_cli_id_t id, vsoa_header_t *vsoa_hdr,
vsoa_url_t *url, vsoa_payload_t *payload, vsoa_mware_resolve_t *resolve)
{
FILE *log_file = (FILE *)arg;
char *msg;
if (url->url_len) {
// log url into file
fprintf(log_file, "URL: %.*s\n", (int)url->url_len, url->url);
fflush(log_file);
// pass message data 'new resolved data' to next handler
// and specify a data destructor 'free()'
msg = strdup("new resolved data");
vsoa_mware_add_resolve_data(resolve, "log", msg, (vsoa_mware_resolve_free_func_t)free);
}
// return true to continue processing next callback
return true;
}
bool msg_callback(void *arg, vsoa_server_t *server, vsoa_cli_id_t id, vsoa_header_t *vsoa_hdr,
vsoa_url_t *url, vsoa_payload_t *payload, vsoa_mware_resolve_t *resolve)
{
vsoa_payload_t send;
uint32_t seqno = vsoa_parser_get_seqno(vsoa_hdr);
char *msg;
send.data = NULL;
send.data_len = 0;
send.param = "{\"light\": 1}";
send.param_len = strlen(send.param);
// get message data from previous callback
msg = vsoa_mware_get_resolve_data(resolve, "log");
if (msg) {
printf("Log resolved data (key: \"log\") : %s\n", msg);
}
// reply vsoa client
vsoa_server_cli_reply(server, id, 0, seqno, 0, &send);
// return false to stop processing next callback
return false;
}
int main(int argc, char *argv[])
{
vsoa_mware_t *mware;
vsoa_url_t url;
uint16_t server_port = 3001;
struct sockaddr_in addr;
int ret;
fd_set fds;
int max_fd;
int count;
struct timespec timeout;
extern char **environ;
int exits;
char **env = environ;
char *auto_key = "VSOA_AUTO_PORT=";
char *vsoa_auto_port ="\0";
FILE *log_file;
log_file = fopen("/tmp/log_file", "a");
if (!log_file) {
fprintf(stderr, "Can not open log_file!\n");
return (-1);
}
// check if 'VSOA_AUTO_PORT' environment variable exists
while (*env) {
exits = memcmp(*env, auto_key, (size_t)strlen(auto_key));
if (exits == 0) {
vsoa_auto_port = *env;
break;
}
env++;
}
// get server port from 'VSOA_AUTO_PORT' environment variable
if (strlen(vsoa_auto_port) > 1) {
const char s[2] = "=";
char *port_str = "\0";
char *token;
token = strtok(vsoa_auto_port, s);
while (token != NULL) {
port_str = token;
token = strtok(NULL, s);
}
server_port = strtol(port_str, NULL, 10);
}
// set server listen address and port
bzero(&addr, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(server_port);
addr.sin_addr.s_addr = INADDR_ANY;
// create vsoa server
server = vsoa_server_create("{\"name\":\"light_server\"}");
if (!server) {
fprintf(stderr, "Can not create VSOA server!\n");
return -1;
}
// create vsoa mware
mware = vsoa_mware_create();
// add /light listener
url.url = "/light";
url.url_len = strlen(url.url);
vsoa_server_add_listener(server, &url, vsoa_mware_handler(mware), mware);
// add mware rpc hook
// 'log_callback' will be called first, if 'log_callback' returns 'true'
// then 'msg_callback' will be called
vsoa_mware_add_listener(mware, log_callback, log_file);
vsoa_mware_add_listener(mware, msg_callback, NULL);
// start vsoa server
ret = vsoa_server_start(server, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if (!ret) {
printf("Cannot start VSOA stream server.\n");
vsoa_server_close(server);
return -1;
}
printf("Started VSOA stream server.\n");
// set timeout to 1s
timeout.tv_sec = 1;
timeout.tv_nsec = 0;
// server event loop
while (1) {
FD_ZERO(&fds);
max_fd = vsoa_server_fds(server, &fds);
count = pselect(max_fd + 1, &fds, NULL, NULL, &timeout, NULL);
if (count > 0) {
vsoa_server_input_fds(server, &fds);
}
}
return 0;
}
说明: Mware 功能仅涉及到服务器部分,客户端部分可使用 RPC 客户端范例。
自定义数据绑定
在 VSOA 的 C 语言环境下可在 Mware 功能中绑定用户自定义的数据,绑定数据时使用 vsoa_mware_set_custom
接口,获取绑定数据时使用 vsoa_mware_custom
接口。
以如上开发示例为基础,添加 Mware 自定义数据绑定使用 vsoa_mware_get()
从 resolve
中获取数据。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "vsoa_server.h"
#include "vsoa_mware.h"
static vsoa_server_t *server;
bool log_callback(void *arg, vsoa_server_t *server, vsoa_cli_id_t id, vsoa_header_t *vsoa_hdr,
vsoa_url_t *url, vsoa_payload_t *payload, vsoa_mware_resolve_t *resolve)
{
FILE *log_file = (FILE *)arg;
char *msg;
if (url->url_len) {
fprintf(log_file, "URL: %.*s\n", (int)url->url_len, url->url);
fflush(log_file);
msg = strdup("new resolved data");
vsoa_mware_add_resolve_data(resolve, "log", msg, (vsoa_mware_resolve_free_func_t)free);
}
// get mware by resolve
vsoa_mware_t *mware = vsoa_mware_get(resolve);
if (mware) {
// get custom data by mware
const char *custom = (const char *)vsoa_mware_custom(mware);
printf("[custom] %s\r\n", custom);
}
return true;
}
bool msg_callback(void *arg, vsoa_server_t *server, vsoa_cli_id_t id, vsoa_header_t *vsoa_hdr,
vsoa_url_t *url, vsoa_payload_t *payload, vsoa_mware_resolve_t *resolve)
{
vsoa_payload_t send;
uint32_t seqno = vsoa_parser_get_seqno(vsoa_hdr);
char *msg;
send.data = NULL;
send.data_len = 0;
send.param = "{\"light\": 1}";
send.param_len = strlen(send.param);
msg = vsoa_mware_get_resolve_data(resolve, "log");
if (msg) {
printf("Log resolved data (key: \"log\") : %s\n", msg);
}
vsoa_server_cli_reply(server, id, 0, seqno, 0, &send);
return false;
}
int main(int argc, char *argv[])
{
vsoa_mware_t *mware;
vsoa_url_t url;
uint16_t server_port = 3001;
struct sockaddr_in addr;
int ret;
fd_set fds;
int max_fd;
int count;
struct timespec timeout;
extern char **environ;
int exits;
char **env = environ;
char *auto_key = "VSOA_AUTO_PORT=";
char *vsoa_auto_port ="\0";
FILE *log_file;
log_file = fopen("/tmp/log_file", "a");
if (!log_file) {
fprintf(stderr, "Can not open log_file!\n");
return (-1);
}
while (*env) {
exits = memcmp(*env, auto_key, (size_t)strlen(auto_key));
if (exits == 0) {
vsoa_auto_port = *env;
break;
}
env++;
}
if (strlen(vsoa_auto_port) > 1) {
const char s[2] = "=";
char *port_str = "\0";
char *token;
token = strtok(vsoa_auto_port, s);
while (token != NULL) {
port_str = token;
token = strtok(NULL, s);
}
server_port = strtol(port_str, NULL, 10);
}
bzero(&addr, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(server_port);
addr.sin_addr.s_addr = INADDR_ANY;
server = vsoa_server_create("{\"name\":\"light_server\"}");
if (!server) {
fprintf(stderr, "Can not create VSOA server!\n");
return -1;
}
mware = vsoa_mware_create();
url.url = "/light";
url.url_len = strlen(url.url);
vsoa_server_add_listener(server, &url, vsoa_mware_handler(mware), mware);
// custom data
const char *custom = "hello mware!";
// bind custom data to mware
vsoa_mware_set_custom(mware, (void *)custom);
vsoa_mware_add_listener(mware, log_callback, log_file);
vsoa_mware_add_listener(mware, msg_callback, NULL);
ret = vsoa_server_start(server, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if (!ret) {
printf("Cannot start VSOA stream server.\n");
vsoa_server_close(server);
return -1;
}
printf("Started VSOA stream server.\n");
timeout.tv_sec = 1;
timeout.tv_nsec = 0;
while (1) {
FD_ZERO(&fds);
max_fd = vsoa_server_fds(server, &fds);
count = pselect(max_fd + 1, &fds, NULL, NULL, &timeout, NULL);
if (count > 0) {
vsoa_server_input_fds(server, &fds);
}
}
return 0;
}
注意事项
C/C++ 服务端编译时需链接如下表所示的 VSOA 动态库,在 RealEvo-IDE 中配置时请参考 C/C++ 环境验证,Linux 下开发请参考 搭建 Linux 运行环境 提供的 C 语言范例进行配置。
库名称 | 功能 |
---|---|
libvsoa-json.so | 提供 JSON 功能 |
libvsoa-server.so | 提供服务端功能 |
libvsoa-parser.so | 提供参数解析功能 |