服务端开发

更新时间:
2024-11-21
下载文档

服务端开发

本节内容介绍 VSOA 服务端使用 Mware 的方法。

开发须知

  • 使用 Mware,可以将多个 RPC 回调函数绑定至一个 URL 中,在 URL 被 RPC 请求后,回调函数按照绑定的先后顺序依次执行。
  • 在按序执行回调函数的过程中,函数可以使用 Mware 提供的接口,将数据以键值对的形式传递给下一个回调函数。若回调函数返回“true” ,则执行继续;若回调函数返回“false”,则执行中止。

常用接口

VSOA Mware 接口详细说明可参考 VSOA C 语言编程手册,源码可见 VSOA Mware 头文件

开发示例

在本示例中,服务端执行以下流程:

  1. 打开“/tmp/log_file”文件用于存储日志信息。
  2. 创建 VSOA Mware,绑定两个回调函数 log_callbackmsg_callback 到 URL /light 上。
  3. 创建 VSOA 服务器 light_server。当创建成功时,服务端会打印 Started VSOA stream server
  4. 监听 RPC URL /light,当 URL 被请求时,调用 log_callback 回调函数。
  5. log_callback 中,记录 URL 到日志文件,再向下一个回调函数传递数据。数据键为 log,值为 new resolved data。最后,返回“true” 继续执行下一个回调。
  6. 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提供参数解析功能
文档内容是否对您有所帮助?
有帮助
没帮助