【VSOA】VSOA Client

VSOA Client

VSOA客户端相关的所有 API 都在文件 vsoa_client.hlibvsoa-client.so 中。

Notice

下列函数为线程安全:

  • vsoa_client_ping(),
  • vsoa_client_subscribe(),
  • vsoa_client_unsubscribe(),
  • vsoa_client_multi_subscribe(),
  • vsoa_client_multi_unsubscribe(),
  • vsoa_client_call(),
  • vsoa_client_sync_create(),
  • vsoa_client_sync_delete(),
  • vsoa_client_sync_call(),
  • vsoa_client_datagram(),
  • vsoa_client_quick_datagram(),
  • vsoa_client_is_connect(),
  • vsoa_client_send_timeout(),
  • vsoa_client_set_custom(),
  • vsoa_client_custom(),
  • vsoa_client_stream_create(),
  • vsoa_client_stream_close().

所有回调函数都在 vsoa_client_input_fds() 中调用,因此需要注意多线程安全。

当连接断开时,只需重新调用 vsoa_client_connect() 即可重新连接。重新连接后,您需要重新订阅以前的主题。
vsoa_client_connect() 重新连接只能在 vsoa_cclient_input_fds() 所在的事件循环中调用,而不是在其他的回调中。

在调用 vsoa_client_close() 之后,客户端对象不可再次被使用,且 vsoa_client_close() 只能在 vsoa_cclient_input_fds() 所在的事件循环中调用,而不是在其他的回调中。

快速通道用于高频数据更新通道,由于数据更新频率高,对通信可靠性的要求并不严格。

Client Create

使用以下 API 来创建一个 VSOA 客户端:

vsoa_client_t *vsoa_client_create(vsoa_client_msg_func_t onmsg, void *arg);
  • 函数运行成功返回一个新的客户端对象,失败返回 NULL
  • 参数 onmsg:订阅回调,在客户端收到订阅消息后调用;
  • 参数 arg:回调函数的参数;

onmsg 函数原型:

typedef void (*vsoa_client_msg_func_t)(void *arg, vsoa_client_t *client, vsoa_url_t *url, vsoa_payload_t *payload, bool quick);
  • 参数 arg:回调函数的参数;
  • 参数 client:客户端对象;
  • 参数 url :URL 信息;
  • 参数 payload:payload 数据;
  • 参数 quick:是否是一个快速发布的消息;

注意:所有订阅的URL的消息将会进入 onmsg 回调中,所以需要匹配并判断该 URL来完成不同的工作。

建议使用的标准 URL 匹配规则:

订阅路径匹配规则
"/"Catch all publish message.
"/a/b/c"Only catch "/a/b/c" publish message.
"/a/b/c/"Catch "/a/b/c" and "/a/b/c/..." all publish message.

当客户端不再使用时,使用下列函数将其关闭:

void vsoa_client_close(vsoa_client_t *client);

Client Connect

客户端使用以下 API 来连接到指定的服务器:

bool vsoa_client_connect(vsoa_client_t *client, const struct sockaddr *server, socklen_t namelen, const struct timespec *timeout, const char *passwd, char *info, size_t sz_info);
  • 函数成功返回 true,失败返回 false
  • 参数 client:客户端对象;
  • 参数 server:目标服务器;
  • 参数 namelen:目标服务器长度;
  • 参数 timeout:连接到服务器的超时时间,如果设置为 NULL 代表永远等待,直到服务器响应;
  • 参数 passwd:服务器的密码,如果服务器没有密码,请设置为 NULL
  • 参数 info:当连接成功时,存储服务器的信息;
  • 参数 sz_infoinfo 缓冲区的大小;

使用示例

char info[256];
socklen_t serv_len;
vsoa_client_t *client;
struct sockaddr_in pos_addr, serv_addr;
struct timespec timeout = { 1, 0 };

/* assume the position server is in local machine */
bzero(&pos_addr, sizeof(struct sockaddr_in));
pos_addr.sin_family  = AF_INET;
pos_addr.sin_port    = htons(5000);
pos_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
pos_addr.sin_len = sizeof(struct sockaddr_in);

vsoa_position_lookup_server((struct sockaddr *)&pos_addr, sizeof(struct sockaddr_in));

vsoa_position_lookup(AF_INET, "vsoa.myserver.com", (struct sockaddr *)&serv_addr, &serv_len, NULL, &timeout);

/* create client and connect to server */
client = vsoa_client_create(NULL, NULL);
vsoa_client_connect(client, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr_in), &timeout, "123456", info, sizeof(info));

示例使用 position 服务器(参见 VSOA Position Server 章节)来查询服务器的地址: vsoa.myserver.com,然后连接到此服务器。此函数是一个同步函数。

使用以下 API 来检查 VSOA 客户端是否已连接到 VSOA 服务器。

bool vsoa_client_is_connect(vsoa_client_t *client);

以下 API 可用于拆分 URL,进行订阅信息的分类和处理:

char *vsoa_client_path_token(vsoa_client_t *client, vsoa_url_t *url, size_t *len)

这个函数方法类似于 C 库的 strtok 函数。

使用示例

void onmessage (void *arg, struct vsoa_client *client, vsoa_url_t *url, vsoa_payload_t *payload)
{
    int i = 0;
    char *seg;
    size_t seg_len;

    seg = vsoa_client_path_token(client, url, &seg_len);
    while (seg) {
        i++;
        printf("[%d] %.*s\n", i, (int)seg_len, seg);
        seg = vsoa_client_path_token(client, NULL, &seg_len);
    }
}

以下 API 可以设置客户端发送超时时间:

bool vsoa_client_send_timeout(vsoa_client_t *client, const struct timespec *timeout);

如果 timeoutNULL,则表示使用默认的发送超时时间 (100 ms)。如果连续三次发送超时,则连接将被断开。

Client Event Loop

与 VSOA 服务器一样,当 VSOA 客户端正在运行时,它应该始终使用以下两个 API进行一个循环来监视和处理所有的输入事件。

int vsoa_client_fds(vsoa_client_t *client, fd_set *rfds);
bool vsoa_client_input_fds(vsoa_client_t *client, const fd_set *rfds);
  • 函数成功返回 true,失败返回 false
  • 参数 client :客户端对象;
  • 参数 rfds:需要监视的 VSOA 客户端的所有事件;

使用示例

fd_set fds;
vsoa_client_t *client;
int custom_fd, max_fd, cnt;
struct timespec timeout = { 1, 0 };
bool ret;

while (1) {
    FD_ZERO(&fds);
    max_fd = vsoa_client_fds(client, &fds);

    /* wait event */
    cnt = pselect(max_fd + 1, &fds, NULL, NULL, &timeout, NULL);
    if (cnt > 0) {
        /* handle event */ 
        ret = vsoa_client_input_fds(client, &fds);
        if (!ret) {
            /* disconnect, do auto reconnect in the event loop */
            while (1) {
                ret = vsoa_client_connect(...);
                if (ret) {
                    break;
                }
                sleep(1);
            }
        }
    }

    /* do other things */
}

在本例中,可以看到当 vsoa_client_input_fds 返回 false 时,表示客户端与服务器断开连接,可以很容易地在事件循环中完成自动重新连接工作。
In this example, you can see that when vsoa_client_input_fds return false, it indicates

Client Ping

使用 Ping/Echo 机制来检查连接是否正常,使用以下 API 实现。

bool vsoa_client_ping(vsoa_client_t *client, vsoa_client_res_func_t callback, void *arg, const struct timespec *timeout);
  • 函数成功返回 true,失败返回 false;
  • 参数 client:客户端对象;
  • 参数 callback:当检查成功或超时时,该回调将被调用;
  • 参数 arg:回调函数参数;
  • 参数 timeout:ping 到 VSOA 服务器的超时时间,如果设置为 NULL 表示永远等待,直到服务器响应;

回调函数原型:

typedef void (*vsoa_client_res_func_t)(void *arg, vsoa_client_t *client, bool success);
  • 参数 arg:函数参数;
  • 参数 client:客户端对象;
  • 参数 success:若值为 true,则表示连接正常,若为 false,则意味着此 ping 超时,则连接丢失。

Client Subscribe

可以调用 vsoa_client_subscribe 订阅 URL 消息或调用 vsoa_client_unsubscribe 取消订阅 URL 消息。

bool vsoa_client_subscribe(vsoa_client_t *client, const vsoa_url_t *url, vsoa_client_res_func_t callback, void *arg, const struct timespec *timeout);

bool vsoa_client_unsubscribe(vsoa_client_t *client, const vsoa_url_t *url, vsoa_client_res_func_t callback, void *arg, const struct timespec *timeout);
  • 函数成功返回 true,失败返回 false
  • 参数 client:客户端对象;
  • 参数 url:URL 信息;
  • 参数 callback:这将在服务器成功接受 sub/unsub 请求后调用(这并不意味着消息数据已到达);
  • 参数 arg:回调函数参数;
  • 参数 timeout:订阅或非订阅的超时时间,如果设置为 NULL,表示永远等待,直到服务器响应;

使用示例

以下代码片段显示了如何订阅指定的 URL 消息。

vsoa_url_t url;
vsoa_client_t *client;

url.url     = "/foo";
url.url_len = strlen(url.url);
vsoa_client_subscribe(client, &url, NULL, NULL, NULL);

注意:在重新连接成功后,我们需要再次执行所有的订阅。

Client Multi Subscribe

您可以调用 vsoa_client_multi_subscribe 订阅 URL 消息或调用 vsoa_client_multi_unsubscribe 取消订阅 URL 消息。

bool vsoa_client_multi_subscribe(vsoa_client_t *client, const char *const *urls, int cnt, vsoa_client_res_func_t callback, void *arg, const struct timespec *timeout);

bool vsoa_client_multi_unsubscribe(vsoa_client_t *client, const char *const *urls, int cnt, vsoa_client_res_func_t callback, void *arg, const struct timespec *timeout);
  • 函数成功返回 true,失败返回 false
  • 参数 client:客户端对象;
  • 参数 urls:URL 信息数组;
  • 参数 cnt:URL 数组计数;
  • 参数 callback:这将在服务器成功接受 sub/unsub 请求后调用(这并不意味着消息数据已到达);
  • 参数 arg:回调函数参数;
  • 参数 timeout:订阅或非订阅的超时时间,如果设置为 NULL,表示永远等待,直到服务器响应;

使用示例

char *topics[] = {
    "/a/b/c", "/d/e/f", "/x/y/z"
};
vsoa_client_subscribe(client, topics, sizeof(topics) / sizeof(char *), NULL, NULL, NULL);

Sub-Pub URL匹配规则:

PATHRPC URL 匹配规则
"/"获取所有发布消息。
"/a/b/c"仅获取 “/a/b/c” 路径的发布消息。
"/a/b/c/"获取 “/a/b/c”“/a/b/c/...” 下所有路径的发布消息。
注意:URL匹配:URL使用 '/'作为分隔符,例如:'/a/b/c'。如果客户端订阅'/a/',服务器发布'/a''/a/b''/a/b/c'消息,则客户端都将收到。

Client RPC

使用以下 API 可以执行一次RPC调用,VSOA RPC 方法包括 VSOA_CLIENT_RPC_METHOD_GETVSOA_CLIENT_RPC_METHOD_SET

  • VSOA_CLIENT_RPC_METHOD_GET this method is to get data from server.
  • VSOA_CLIENT_RPC_METHOD_SET this method is to set data to server.
bool vsoa_client_call(vsoa_client_t *client, int method, const vsoa_url_t *url, const vsoa_payload_t *payload, vsoa_client_rpc_func_t callback, void *arg, const struct timespec *timeout);
  • 函数成功返回 true,失败返回 false
  • 参数 client:客户端对象;
  • 参数 method:RPC 方法;
  • 参数 url:URL 信息;
  • 参数 payload:RPC payload,此 payload 将被发送到VSOA服务器;
  • 参数 callback:RPC 回调函数;
  • 参数 arg:回调函数参数;
  • 参数 timeout:RPC 的超时时间,如果设置为 NULL,这意味着永远等待,直到服务器响应;

RPC 回调原型:

typedef void (*vsoa_client_rpc_func_t)(void *arg, vsoa_client_t *client, vsoa_header_t *vsoa_hdr, vsoa_payload_t *payload);

当回调函数返回时,vsoa_hdrpayload 所指向的内存将失效。

  • 参数 client:客户端对象;
  • 参数 vsoa_hdr:数据包头部;
  • 参数 payload:payload 数据;
    如果需要同步 RPC 调用,则可以使用 RPC 调用同步扩展接口。

使用示例

下面的代码显示了如何对指定的 URL 执行 RPC GET 操作。

extern void get_foo(void *arg, struct vsoa_client *client, vsoa_header_t *vsoa_hdr, vsoa_payload_t *payload);

vsoa_url_t url;
vsoa_client_t *client;  

url.url     = "/foo";
url.url_len = strlen(url.url);
vsoa_client_call(client, VSOA_CLIENT_RPC_METHOD_GET, &url, NULL, get_foo, NULL, &timeout);

注意:RPC是为异步模式而设计的,因此可以连续并行地执行RPC调用。

Client Datagram

使用以下 API 同服务器接收/发送数据报:

void vsoa_client_on_datagram(vsoa_client_t *client, vsoa_client_dat_func_t callback, void *arg);
bool vsoa_client_datagram(vsoa_client_t *client, const vsoa_url_t *url, const vsoa_payload_t *payload);

这与服务器数据报相同。

数据报回调原型:

typedef void (*vsoa_client_dat_func_t)(void *arg, vsoa_client_t *client, vsoa_url_t *url, vsoa_payload_t *payload, bool quick);

可以使用以下 API 通过快速通道向服务器发送DATAGRAM(数据报)

bool vsoa_client_quick_datagram(vsoa_client_t *client, const vsoa_url_t *url, const vsoa_payload_t *payload);

使用示例

下面的代码显示了如何同指定的 URL 传输数据报。

vsoa_client_t *client;

/* VSOA client set on datagram callback */
vsoa_client_on_datagram(client, on_datagram, NULL);

static void on_datagram (void *arg, vsoa_client_t *client, vsoa_url_t *url, vsoa_payload_t *payload, bool quick)
{
    /* send datagram to server (echo) */
    vsoa_client_datagram(client, url, payload);
}

Client Stream

客户端使用以下 API 创建一个 VSOA 流,如果成功,此函数将返回新的连接 fd,否则将返回 -1

int vsoa_client_stream_create(vsoa_client_t *client, uint16_t tunid, const struct timespec *timeout, int keepalive);
  • 参数 client:客户端对象;
  • 参数 tunid:新的 TCP 通道,客户端使用该通道连接到服务器;
  • 参数 timeout:客户端连接到服务器的超时时间,如果设置为 NULL,表示永远等待,直到服务器响应;
  • 参数 keepalive:设置新的连接 KEEPALIVE 值;

注意:客户端流必须在服务器流创建后创建,所以我们通常使用 RPC 握手在客户端和服务器之间创建流。

客户端使用以下 API 来关闭一个 VSOA 流。

void vsoa_client_stream_close(int stream);
  • 参数 stream:由 vsoa_client_stream_create() 创建的流 fd;

使用示例

下面的代码展示了如何创建一个 VSOA 流:

/* server side code RPC callback */
void command_read (void *arg, vsoa_server_t *server, vsoa_cli_id_t cid, vsoa_header_t *vsoa_hdr, vsoa_url_t *url, vsoa_payload_t *payload)
{
    uint32_t seqno = vsoa_parser_get_seqno(vsoa_hdr);
    vsoa_server_stream_t *stream;

    /* do other things */

    vsoa_server_stream_create(server, stream);

    /* reply tunid to client */
    vsoa_server_cli_reply(server, cid, 0, seqno, stream->tunid, NULL);
}

/* server side accept the connect of client and do transfer in a new thread */
{
    vsoa_server_stream_accept(...);
    send();
    recv();
}

/* client side code RPC callback */
void on_rpc_read (void *arg, struct vsoa_client *client, vsoa_header_t *vsoa_hdr, vsoa_url_t *url, vsoa_payload_t *payload)
{
    int stream;
    struct timespec timeout = { 1, 0 };
    tunid = vsoa_parser_get_tunid(vsoa_hdr);

    /* do other things */

    /* create a new thread with tunid for stream*/
}

/* client create a new stream and do transfer in a new thread */
{
    /* tunid from server stream */
    stream = vsoa_client_stream_create(client, tunid, &timeout, 5)
    send();
    recv();
}

更多详情,请参考 example/c/stream.c

Synchronous RPC Calls

VSOA C 客户端提供同步 RPC 调用支持。

vsoa_client_sync_call_t *vsoa_client_sync_create(bool dynamic);

VSOA 客户端创建一个 RPC 调用同步器,同步器只能同时处理一个 RPC 同步调用,并且不允许使用多线程。
如果参数 dynamic 值为 true,这意味着每次调用收到来自服务器的回复时都动态地应用包内存。当值为 false 时,表示只静态应用一次。
在 MATRIX653 系统中,不允许在 NORMAL 操作模式下创建 RPC 调用同步器,开发人员必须在初始化期间为每个任务创建 RPC 调用同步器。

bool vsoa_client_sync_delete(vsoa_client_sync_call_t *sync);

VSOA 客户端可以销毁一个 RPC 调用同步器,但该同步器只能在使用完成后销毁,不允许在调用期间销毁。
在 MATRIX653 系统中,不允许删除 RPC 调用同步器。

bool vsoa_client_sync_call(vsoa_client_t *client, int method, const vsoa_url_t *url, const vsoa_payload_t *payload,
                           vsoa_client_sync_call_t *sync, vsoa_header_t **vsoa_hdr_reply, const struct timespec *timeout);

VSOA 客户端执行同步 RPC 调用,此函数将阻塞,直到调用完成、发生错误或超时,因此该函数不能在 VSOA 主事件循环线程中使用。
函数返回 true 表示调用成功,vsoa_hdr_reply 有效则表示服务器已回复(否则表示命令超时),当调用成功时,您可以检查 vsoa_hdr_reply 获得服务器回复,您可以使用 vsoa_parser_get_payload() 获取回复的 payload。
当进行下一次远程调用或删除同步器时,vsoa_hdr_reply 指向的数据包内存将无效。
建议为将使用同步 RPC 调用的每个线程创建一个同步器,然后在相应的线程中按顺序使用它。

使用示例

下面的代码展示了如何使用同步 RPC 调用。

void *sync_call_thread (void *arg)
{
    bool ret;
    int cnt = 0;
    char param[32];
    vsoa_url_t url;
    vsoa_header_t *vsoa_hdr;
    vsoa_payload_t reply, send;
    vsoa_client_sync_call_t *sync;

    sync = vsoa_client_sync_create(true);

    url.url     = "/echo";
    url.url_len = strlen(url.url);

    while (true) {
        ret = vsoa_client_sync_call(client, VSOA_CLIENT_RPC_METHOD_GET,
                                    &url, &send, sync, &vsoa_hdr, NULL);
        if (ret) {
            if (vsoa_hdr) {
                vsoa_parser_get_payload(vsoa_hdr, &reply);
                printf("Server /echo reply: %.*s\n", (int)reply.param_len, reply.param);
            } else {
                fprintf(stderr, "Server not reply!\n");
            }
        } else {
            fprintf(stderr, "Synchronous RPC call error!\n");
            break;
        }

        sleep(1);
    }

    vsoa_client_sync_delete(sync);

    return  (NULL);
}

Custom Data Bind

void vsoa_client_set_custom(vsoa_client_t *client, void *custom);
void *vsoa_client_custom(vsoa_client_t *client);

您可以使用上面的两个api来绑定和扩展自定义的客户端框架。

  • 27
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ScilogyHunter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值