Overview

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

Overview

VSOA is the abbreviation of Vehicle SOA presented by ACOINFO, VSOA provides a reliable, Real-Time SOA (Service Oriented Architecture) framework, this framework has multi-language and multi-environment implementation, developers can use this framework to build a distributed service model.

VSOA includes the following features:

  1. Support resource tagging of unified URL
  2. Support URL matching subscribe and publish model
  3. Support Real-Time Remote Procedure Call
  4. Support parallel multiple command sequences
  5. Support reliable and unreliable data publishing and datagram
  6. Support multi-channel full-duplex high speed parallel data stream
  7. Support network QoS control
  8. Easily implement server fault-tolerant design
  9. Support multiple language bindings
  10. Support middleware model

VSOA is a dual-channel communication protocol, using both TCP and UDP, among which the API marked with quick uses the UDP channel. The quick channel is used for high-frequency data update channels. Due to the high data update frequency, the requirements for communication reliability are not strict. It should be noted that UDP channel cannot pass through NAT network, so please do not use quick channel in NAT network.

The total url and payload length of the VSOA data packet cannot exceed 256KBytes - 20Bytes and 65507Bytes - 20Bytes on quick channel, so if you need to send a large amount of data, you can use the VSOA data stream.

NOTICE: The current version does not support secure encryption.

Support

The following shows vsoa library APIs.

 Header FileLibrary
vsoa_server_createvsoa_server.hlibvsoa-server.so
vsoa_server_closevsoa_server.hlibvsoa-server.so
vsoa_server_passwdvsoa_server.hlibvsoa-server.so
vsoa_server_startvsoa_server.hlibvsoa-server.so
vsoa_server_addressvsoa_server.hlibvsoa-server.so
vsoa_server_bind_ifvsoa_server.hlibvsoa-server.so
vsoa_server_set_customvsoa_server.hlibvsoa-server.so
vsoa_server_customvsoa_server.hlibvsoa-server.so
vsoa_server_fdsvsoa_server.hlibvsoa-server.so
vsoa_server_input_fdsvsoa_server.hlibvsoa-server.so
vsoa_server_on_clivsoa_server.hlibvsoa-server.so
vsoa_server_countvsoa_server.hlibvsoa-server.so
vsoa_server_send_timeoutvsoa_server.hlibvsoa-server.so
vsoa_server_cli_closevsoa_server.hlibvsoa-server.so
vsoa_server_cli_lingervsoa_server.hlibvsoa-server.so
vsoa_server_cli_is_subscribedvsoa_server.hlibvsoa-server.so
vsoa_server_cli_addressvsoa_server.hlibvsoa-server.so
vsoa_server_cli_replyvsoa_server.hlibvsoa-server.so
vsoa_server_cli_priorityvsoa_server.hlibvsoa-server.so
vsoa_server_cli_keepalivevsoa_server.hlibvsoa-server.so
vsoa_server_cli_arrayvsoa_server.hlibvsoa-server.so
vsoa_server_cli_send_timeoutvsoa_server.hlibvsoa-server.so
vsoa_server_cli_set_authedvsoa_server.hlibvsoa-server.so
vsoa_server_cli_authedvsoa_server.hlibvsoa-server.so
vsoa_server_cli_set_customvsoa_server.hlibvsoa-server.so
vsoa_server_cli_customvsoa_server.hlibvsoa-server.so
vsoa_server_cli_datagramvsoa_server.hlibvsoa-server.so
vsoa_server_cli_quick_datagramvsoa_server.hlibvsoa-server.so
vsoa_server_on_datagramvsoa_server.hlibvsoa-server.so
vsoa_server_is_subscribedvsoa_server.hlibvsoa-server.so
vsoa_server_publishvsoa_server.hlibvsoa-server.so
vsoa_server_quick_publishvsoa_server.hlibvsoa-server.so
vsoa_server_add_listenervsoa_server.hlibvsoa-server.so
vsoa_server_remove_listenervsoa_server.hlibvsoa-server.so
vsoa_server_stream_createvsoa_server.hlibvsoa-server.so
vsoa_server_stream_acceptvsoa_server.hlibvsoa-server.so
vsoa_server_stream_closevsoa_server.hlibvsoa-server.so
vsoa_mware_createvsoa_mware.hlibvsoa-server.so
vsoa_mware_deletevsoa_mware.hlibvsoa-server.so
vsoa_mware_handlervsoa_mware.hlibvsoa-server.so
vsoa_mware_add_listenervsoa_mware.hlibvsoa-server.so
vsoa_mware_remove_listenervsoa_mware.hlibvsoa-server.so
vsoa_mware_add_resolve_datavsoa_mware.hlibvsoa-server.so
vsoa_mware_get_resolve_datavsoa_mware.hlibvsoa-server.so
vsoa_mware_add_resolve_data_with_lengthvsoa_mware.hlibvsoa-server.so
vsoa_mware_get_resolve_data_with_lengthvsoa_mware.hlibvsoa-server.so
vsoa_mware_set_customvsoa_mware.hlibvsoa-server.so
vsoa_mware_customvsoa_mware.hlibvsoa-server.so
vsoa_mware_getvsoa_mware.hlibvsoa-server.so
vsoa_mware_ref_resolvevsoa_mware.hlibvsoa-server.so
vsoa_mware_unref_resolvevsoa_mware.hlibvsoa-server.so
vsoa_mware_reply_resolvevsoa_mware.hlibvsoa-server.so
vsoa_mware_add_server_listenervsoa_mware.hlibvsoa-server.so
vsoa_mware_remove_server_listenervsoa_mware.hlibvsoa-server.so
vsoa_client_createvsoa_client.hlibvsoa-client.so
vsoa_client_closevsoa_client.hlibvsoa-client.so
vsoa_client_lingervsoa_client.hlibvsoa-client.so
vsoa_client_connectvsoa_client.hlibvsoa-client.so
vsoa_client_disconnectvsoa_client.hlibvsoa-client.so
vsoa_client_is_connectvsoa_client.hlibvsoa-client.so
vsoa_client_path_tokenvsoa_client.hlibvsoa-client.so
vsoa_client_send_timeoutvsoa_client.hlibvsoa-client.so
vsoa_client_fdsvsoa_client.hlibvsoa-client.so
vsoa_client_input_fdsvsoa_client.hlibvsoa-client.so
vsoa_client_pingvsoa_client.hlibvsoa-client.so
vsoa_client_subscribevsoa_client.hlibvsoa-client.so
vsoa_client_unsubscribevsoa_client.hlibvsoa-client.so
vsoa_client_multi_subscribevsoa_client.hlibvsoa-client.so
vsoa_client_multi_unsubscribevsoa_client.hlibvsoa-client.so
vsoa_client_callvsoa_client.hlibvsoa-client.so
vsoa_client_datagramvsoa_client.hlibvsoa-client.so
vsoa_client_quick_datagramvsoa_client.hlibvsoa-client.so
vsoa_client_on_datagramvsoa_client.hlibvsoa-client.so
vsoa_client_set_customvsoa_client.hlibvsoa-client.so
vsoa_client_customvsoa_client.hlibvsoa-client.so
vsoa_client_stream_createvsoa_client.hlibvsoa-client.so
vsoa_client_stream_closevsoa_client.hlibvsoa-position.so
vsoa_client_sync_createvsoa_client.hlibvsoa-position.so
vsoa_client_sync_deletevsoa_client.hlibvsoa-position.so
vsoa_client_sync_callvsoa_client.hlibvsoa-position.so
vsoa_position_server_createvsoa_position.hlibvsoa-position.so
vsoa_position_server_closevsoa_position.hlibvsoa-position.so
vsoa_position_server_startvsoa_position.hlibvsoa-position.so
vsoa_position_server_fdvsoa_position.hlibvsoa-position.so
vsoa_position_server_inputvsoa_position.hlibvsoa-position.so
vsoa_position_server_responsevsoa_position.hlibvsoa-position.so
vsoa_position_server_set_customvsoa_position.hlibvsoa-position.so
vsoa_position_server_customvsoa_position.hlibvsoa-position.so
vsoa_position_lookupvsoa_position.hlibvsoa-position.so
vsoa_position_lookup_servervsoa_position.hlibvsoa-position.so
vsoa_parser_init_headervsoa_parser.hlibvsoa-parser.so
vsoa_parser_init_recvvsoa_parser.hlibvsoa-parser.so
vsoa_parser_fixp_lengthvsoa_parser.hlibvsoa-parser.so
vsoa_parser_get_lengthvsoa_parser.hlibvsoa-parser.so
vsoa_parser_set_tunidvsoa_parser.hlibvsoa-parser.so
vsoa_parser_set_urlvsoa_parser.hlibvsoa-parser.so
vsoa_parser_set_payloadvsoa_parser.hlibvsoa-parser.so
vsoa_parser_get_urlvsoa_parser.hlibvsoa-parser.so
vsoa_parser_get_payloadvsoa_parser.hlibvsoa-parser.so
vsoa_parser_inputvsoa_parser.hlibvsoa-parser.so
vsoa_parser_print_headervsoa_parser.hlibvsoa-parser.so
vsoa_parser_set_seqnovsoa_parser.h-
vsoa_parser_get_seqnovsoa_parser.h-
vsoa_parser_get_typevsoa_parser.h-
vsoa_parser_get_flagsvsoa_parser.h-
vsoa_parser_get_statusvsoa_parser.h-
vsoa_parser_get_tunidvsoa_parser.h-
vsoa_parser_get_url_lenvsoa_parser.h-
vsoa_parser_get_param_lenvsoa_parser.h-
vsoa_parser_get_data_lenvsoa_parser.h-
VSOA_PARAM_SYNCER_PUBLISHvsoa_syncer.h-
VSOA_PARAM_SYNCER_RPCvsoa_syncer.h-

VSOA Server

All the VSOA server APIs are include in the file vsoa_server.h and libvsoa-server.so. libvsoa-hpserv.so is binary compatible with libvsoa-server.so, libvsoa-hpserv.so provides a parallel processing server, which runs more efficiently on multi-core processors. It is recommended to use a symbolic link to specify which library to use, so that the application can do it without modification.

Use following API to create a VSOA server:

vsoa_server_t *vsoa_server_create(const char *info_json);
  • info_json The information of your server, in json format. This JSON info must contain the name field.
  • Returns: If success, return the server object, return NULL if any errors.

Example

vsoa_server_t *s = vsoa_server_create("{\"name\":\"Test Server\"}");

Use this API to close your server:

void vsoa_server_close(vsoa_server_t *server);
  • server VSOA server object.

Set up a password for your server:

bool vsoa_server_passwd(vsoa_server_t *server, const char *passwd);
  • server VSOA server object.
  • passwd Password string.
  • Returns: true if success, false if any errors.

After that, the client must provide the correct password to connect to this service, otherwise, the client will be refused by server.

NOTICE: The password is just a string without any secure encryption, therefore, it is only a relatively simple security management in current version.

Start the server:

bool vsoa_server_start(vsoa_server_t *server, const struct sockaddr *addr, socklen_t namelen);
  • server VSOA server object.
  • addr The server address, support AF_INET (struct sockaddr_in) and AF_INET6 (struct sockaddr_in6).
  • namelen Address struct size.
  • Returns: true if success, false if any errors.

If the port bound at start is 0, the system will automatically assign a port. At this time, the VSOA server TCP and UDP ports are different, and the client needs to use version 1.0.1 or later to support.

Get server address:

bool vsoa_server_address(vsoa_server_t *server, struct sockaddr *addr, socklen_t *namelen);

Get server TCP address.

Set server binding network interface:

bool vsoa_server_bind_if(vsoa_server_t *server, const char *ifname)

Bind the server to the specified network interface, which can be called before vsoa_server_start or after vsoa_server_start.

After the server starts successfully, you can enter the event loop.

Server Event Loop

When a VSOA server is running, it should always monitor and handle all the input events with a loop using following two APIs:

int vsoa_server_fds(vsoa_server_t *server, fd_set *rfds);
void vsoa_server_input_fds(vsoa_server_t *server, const fd_set *rfds);
  • server VSOA server object.
  • rfds All the events of the VSOA server that need to be monitor.

Example

fd_set fds;
int custom_fd, max_fd, cnt;

while (1) {
    FD_ZERO(&fds);
    max_fd = vsoa_server_fds(my_server, &fds);

    /* add custom event */
    FD_SET(custom_fd, &fds);
    if (max_fd < custom_fd) {
        max_fd = custom_fd;
    }

    /* wait event */
    cnt = pselect(max_fd + 1, &fds, NULL, NULL, &timeout, NULL);
    if (cnt > 0) {
        vsoa_server_input_fds(my_server, &fds);
    }

    /* do other things */
}

Above is a standard event loop template, and you can see that we can add any custom event into this loop, very easy and convenient!

Remote Client Management

In VSOA server side, each connected client will be assigned a unique client id:

typedef uint32_t  vsoa_cli_id_t;

A server uses this API to monitor connect status of client:

void vsoa_server_on_cli(vsoa_server_t *server, vsoa_server_cli_func_t oncli, void *arg);
  • server Server object.
  • oncli The callback will be called when client connect or disconnect from server.
  • arg You can use this callback arg to pass any custom information.

The callback protype :

typedef void (*vsoa_server_cli_func_t)(void *arg, vsoa_server_t *server, vsoa_cli_id_t id, bool connect);
  • id The client id.
  • connect The client status.

You can set the global clients sending timeout of this server, which defaults to 100 ms:

bool vsoa_server_send_timeout(vsoa_server_t *server, bool cur_clis, const struct timespec *timeout);
  • cur_clis Whether to set the currently connected client at the same time.

When using a common VSOA server (Not HP-Server), the data packets sent to the client may be blocked due to network congestion, and this time indicates that a single send is the maximum time for blocking. If this setting is too long, it may cause the common server to block for too long.

You can also get the total clients that connected to the server:

int vsoa_server_count(vsoa_server_t *server);

There is also a way to get all the clients' id:

int vsoa_server_cli_array(vsoa_server_t *server, vsoa_cli_id_t ids[], int max_cnt);
  • server Server object.
  • ids Store the client id.
  • max_cnt Size of the ids.
  • Returns: The actual number of client.

Maybe you need the address of client, use this API:

bool vsoa_server_cli_address(vsoa_server_t *server, vsoa_cli_id_t id, struct sockaddr *addr, socklen_t *namelen);

Set a connection keep alive timer for this remote client:

bool vsoa_server_cli_keepalive(vsoa_server_t *server, vsoa_cli_id_t id, int keepalive);
  • server Server object.
  • keepalive It will be used for TCP_KEEPIDLE and TCP_KEEPINTVL.
  • Returns: true if success, false if any errors.

The server can actively close the client:

bool vsoa_server_cli_close(vsoa_server_t *server, vsoa_cli_id_t id);
  • server Server object.
  • id The client id.
  • Returns: true if success, false if the client not exist.

VSOA client set client socket linger time:

bool vsoa_server_cli_linger(vsoa_server_t *server, vsoa_cli_id_t id, int time);
  • server Server object.
  • id The client id.
  • time Linger time in seconds.
  • Returns: true if success, false if the client not exist.

The time unit is seconds, when the time is 0, it means that the close call will be closed immediately.

The server can get whether the specified client is subscribed to the specified topic:

bool vsoa_server_cli_is_subscribed(vsoa_server_t *server, vsoa_cli_id_t id, const vsoa_url_t *url);
  • server Server object.
  • id The client id.
  • url Specified topic URL.
  • Returns: true for subscribed, false for unsubscribed.

QoS

In VSOA, there is a QoS mechanism for client:

bool vsoa_server_cli_priority(vsoa_server_t *server, vsoa_cli_id_t id, int new_prio);
  • server Server object.
  • new_prio New priority, the range is 0(low) ~ 5(high), default is 0.
  • Returns: true if success, false if any errors.

NOTICE: The priority of the client can be updated dynamically at run time.

Set the client packet sending timeout, default: wait for sending when the network is blocked.

bool vsoa_server_cli_send_timeout(vsoa_server_t *server, vsoa_cli_id_t id, const struct timespec *timeout);
  • server Server object.
  • id client id.
  • timeout Timeout value.
  • Returns: true if success, false if any errors.

If timeout is NULL, it means to use the default server send timeout setting. If three consecutive sending times out, the connection is disconnected.

Set and get client authorization status.

bool vsoa_server_cli_set_authed(vsoa_server_t *server, vsoa_cli_id_t id, bool authed);
bool vsoa_server_cli_authed(vsoa_server_t *server, vsoa_cli_id_t id);

The server can set and obtain the authorization status of the client. When a client connects to the server and has passed the password check, the client’s authed status is true at this time, and he can receive the server’s publish information at the same time. When the server still needs to perform For additional independent authorization checks, this state can be set to false when the client just connects, at this time the client will not be able to get the publish message until the client passes the server's independent authorization check.

Example

vsoa_server_t *server;

...

void onclient(void *arg, vsoa_server_t *server, vsoa_cli_id_t id, bool connect)
{
    if (connect) {
        // We need independent authorization.
        vsoa_server_cli_set_authed(server, id, false);
    }
}

vsoa_server_on_cli(server, onclient, NULL);

/* Independent Authorization */
void onauth(void *arg, vsoa_server_t *server, vsoa_cli_id_t id, vsoa_header_t *vsoa_hdr, vsoa_url_t *url, vsoa_payload_t *payload)
{
    if (/* Independent Authorization Verify */) {
        vsoa_server_cli_set_authed(server, id, true);
        /* The authorization verification is successful, 
         * and the server's publish information can be received */
        vsoa_server_cli_reply(server, id, 0, vsoa_parser_get_seqno(vsoa_hdr), 0, NULL);

    } else {
        vsoa_server_cli_reply(server, id, AUTH ERROR CODE, vsoa_parser_get_seqno(vsoa_hdr), 0, NULL);
    }
}

vsoa_url_t url = { .url: "/auth", .url_len: 5 };

vsoa_server_add_listener(server, &url, onauth, NULL);

This feature is available in VSOA 1.0.7 and above.

Base Data Struct

The base data structs are defined in: vsoa_parser.h

All the data exchange between server and client need the unique identification: URL

URL

typedef struct {
    char *url;
    size_t url_len;
} vsoa_url_t;
  • url the url description, must start from: '/', such as "/res/a/b".
  • url_len string length of the url.

Example

vsoa_url_t url;
...
url.url = "/res/a/b"
url.url_len = strlen(url.url);

Payload

The payload struct is usded for all the data transfer in VSOA except for stream.

typedef struct {
    char *param;
    size_t param_len;
    void *data;
    size_t data_len;
} vsoa_payload_t;
  • param String type, this is optional.
  • param_len String length if the param exist, otherwise please set it to 0.
  • data Raw data type, this is optional.
  • data_len Data size if the data exist, otherwise please set it to 0.

NOTICE: We strongly recommended using JSON format for the param, and you can easily use jstruct tool to parse/stringify the param. And for the data, you can use binary(should consider the endian) or base64 format.

VSOA Packet Header

typedef struct {...} vsoa_header_t;

You shouldn't access the members of the struct directly. so, there are some helper macros:

#define vsoa_parser_get_seqno(vsoa_hdr)         ...
#define vsoa_parser_get_type(vsoa_hdr)          ...
#define vsoa_parser_get_flags(vsoa_hdr)         ...
#define vsoa_parser_get_status(vsoa_hdr)        ...
#define vsoa_parser_get_tunid(vsoa_hdr)         ...
#define vsoa_parser_get_url_len(vsoa_hdr)       ...
#define vsoa_parser_get_param_len(vsoa_hdr)     ...
#define vsoa_parser_get_data_len(vsoa_hdr)      ...
  • seqno For each RPC, this is a very important information to ensure the integrity of RPC transaction, because the RPC can be invoked in parallel asynchronously.
  • type Used by VSOA core, user needn't care about it.
  • flags You may need the RPC method from the flags: VSOA_FLAG_SET.
  • status RPC return code from server. The following values are currently defined by default:
Status CodeValueDescription
vsoa.code.SUCCESS0Call succeeded
vsoa.code.PASSWORD1Wrong password
vsoa.code.ARGUMENTS2Parameter error
vsoa.code.INVALID_URL3Invalid URL
vsoa.code.NO_RESPONDING4Server not responding
vsoa.code.NO_PERMISSIONS5No permission
vsoa.code.NO_MEMORY6Out of memory
vsoa.code.PROXY7Proxy error (payload.param.error indicates the error message)

You can also define your own status code. The user-defined failure value is recommended to be 128 ~ 254.

  • tunid The tunnel id, used for stream stransfer, more details in later.
  • url_len The length of url, used by VSOA core.
  • param_len The length of parameter, used by VSOA core.
  • data_len The length of data, used by VSOA core.

And you can also print the header information:

void vsoa_parser_print_header(const vsoa_header_t *vsoa_hdr, bool data_detail);
  • vsoa_hdr The header pointer.
  • data_detail Print the data datails in HEX format.

Server Publish

bool vsoa_server_publish(vsoa_server_t *server, const vsoa_url_t *url, const vsoa_payload_t *payload);
  • url The URL of current payload.
  • payload Payload data.
  • Returns: true if success, false if any errors.

If a large number of high-frequency publish are required and delivery is not guaranteed, the following publish interfaces can be used:

bool vsoa_server_quick_publish(vsoa_server_t *server, const vsoa_url_t *url, const vsoa_payload_t *payload);

Actually, before publish for a URL, you'd better check if there is any client that need this URL data, if not, the server is not necessary to publish:

bool vsoa_server_is_subscribed(vsoa_server_t *server, const vsoa_url_t *url);
  • url The URL of current payload.
  • Returns: true for subscribed, false for unsubscribed.

Example

vsoa_server_t *server;
vsoa_url_t url;
vsoa_payload_t payload;

if (!vsoa_server_is_subscribed(server, &url)) {
    return;
}

vsoa_server_publish(server, &url, &payload);

NOTICE: URL matching: URL uses '/' as a separator, for example: '/a/b/c', if the client subscribes to '/a/', the server publish '/a', '/a/b' or '/ a/b/c' message, the client will be received.

Server RPC

Server should first add a listener for a URL of RPC:

bool vsoa_server_add_listener(vsoa_server_t *server, const vsoa_url_t *url, vsoa_server_cmd_func_t callback, void *arg);
  • url The url information.
  • callback When the RPC is arrived from a client, this callback will be called.
  • arg The callback argument.
  • Returns: true if success, false if any errors.

The listener callback protype:

typedef void (*vsoa_server_cmd_func_t)(void *arg, vsoa_server_t *server, vsoa_cli_id_t id, vsoa_header_t *vsoa_hdr, vsoa_url_t *url, vsoa_payload_t *payload);
  • arg Callback arg.
  • id The client id.
  • vsoa_hdr VSOA standard data header.
  • url Request URL.
  • payload Payload from client.

You can also remove RPC listener at any time:

void vsoa_server_remove_listener(vsoa_server_t *server, const vsoa_url_t *url);

When the RPC arrived, the server uses followin API to reply the client:

bool vsoa_server_cli_reply(vsoa_server_t *server, vsoa_cli_id_t id, uint8_t status, uint32_t seqno, uint16_t tunid, const vsoa_payload_t *payload);
  • id The client id.
  • status RPC reply status code, 0 means success.
  • seqno Should be same as the RPC request.
  • tunid Used for stream transfer.
  • payload Payload to client, may be NULL.
  • Returns: true if success, false if any errors.

Usually, server replies the client in the RPC listener callback.

Example

vsoa_server_t *server;
vsoa_url_t url;

url.url     = "/read";
url.url_len = strlen(url.url);
vsoa_server_add_listener(my_server.server, &url, command_read, NULL);

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)
{
    /* note: get the seqno from header */
    uint32_t seqno = vsoa_parser_get_seqno(vsoa_hdr); 
    vsoa_payload_t payload_reply;

    /* do somthing */

    /* reply to client */
    vsoa_server_cli_reply(server, cid, 0, seqno, 0, &payload_reply);
}

RPC URL Match Rules

PATHRPC match rules
"/"Default URL listener.
"/a/b/c"Only handle "/a/b/c" path call.
"/a/b/c/"Handle "/a/b/c" and "/a/b/c/..." all path calls.

NOTICE: If both "/a/b/c" and "/a/b/c/" RPC handler are present, When the client makes a "/a/b/c" RPC call, "/a/b/c" handler is matched before "/a/b/c/".

Server Stream

A stream actually is a new tcp connect tunnel:

vsoa_server_stream_t *vsoa_server_stream_create(vsoa_server_t *server);
  • Returns: The stream object.

The stream struct:

typedef struct vsoa_server_stream {
    bool valid;
    int listenfd;
    int clifd;
    uint16_t tunid;
    void *custom;
} vsoa_server_stream_t;
  • valid Identify whether this steam is valid, usbed by VSOA core.
  • listenfd Server use this fd to listen and accept client connection.
  • clifd Client fd when connected.
  • tunid New tcp tunnel, this will be replied by server to client, and client use it to connect to server.

Server use this API to accept the client connect:

int vsoa_server_stream_accept(vsoa_server_stream_t *stream, struct sockaddr *addr, socklen_t *namelen, int keepalive);
  • addr This will store a new connection address of client, could be NULL.
  • namelen This store the length of addr, coule be NULL.
  • keepalive Like the argument of vsoa_server_cli_keepalive.
  • Returns: If success, this return the client fd, server use this fd to do next stream transfer, otherwise, the return is < 0.

When finished the stream transfer, server should close the stream:

void vsoa_server_stream_close(vsoa_server_stream_t *stream);

After the Stream is closed, the stream object is not allowed to be used again.

NOTICE: We strongly recommend that you assign a new independent thread for a stream to avoid blocking general RPC requests.

More details, please refer to the example/c/stream.c.

Server Datagram

The DATAGRAM is another transfer type, usually this type data is used to transmit some data that does not require confirmation, for example, VSOA's DATAGRAM data packets can be used to build a VPN network

Server send datagram to client, this API is very similar to vsoa_server_cli_reply(), but there is no status, seqno, tunid parameter.

bool vsoa_server_cli_datagram(vsoa_server_t *server, vsoa_cli_id_t id, const vsoa_url_t *url, const vsoa_payload_t *payload);
  • server Server objcet.
  • id Client id.
  • url The URL information.
  • payload The payload data that will be sent to client.
  • Returns: true if success, false if any errors.

You can use the following API to send DATAGRAM to client through quick channel:

bool vsoa_server_cli_quick_datagram(vsoa_server_t *server, vsoa_cli_id_t id, const vsoa_url_t *url, const vsoa_payload_t *payload);

Server receive datagram from client:

void vsoa_server_on_datagram(vsoa_server_t *server, vsoa_server_dat_func_t callback, void *arg);
  • callback When any datagram arrived, this callback will be called.
  • arg The callback argument.

The callback protype:

typedef void (*vsoa_server_dat_func_t)(void *arg, vsoa_server_t *server, vsoa_cli_id_t id, vsoa_url_t *url, vsoa_payload_t *payload, bool quick);

Custom Data Bind

bool vsoa_server_cli_set_custom(vsoa_server_t *server, vsoa_cli_id_t id, void *custom);
void *vsoa_server_cli_custom(vsoa_server_t *server, vsoa_cli_id_t id);

You can use above two APIs to bind and expand your own service framework.

VSOA Server Parameter Synchronizer

This function helps VSOA server to synchronize data and reduce data synchronization code. And in the C version, they are designed into macros for easy to adapt to different data types.They are in file: libvsoa/vsoa_syncer.h

The synchronizer return value:

CodeValue
VSOA_SYNCER_PUB_OK0
VSOA_SYNCER_SET_OK1
VSOA_SYNCER_GET_OK2
VSOA_SYNCER_ERROR-1

Publish Syncer

vsoa_syncer_ret VSOA_PARAM_SYNCER_PUBLISH(server, url, module, struc, bin, bin_len);

When ther is any subscribe from client, it will auto publish the data of URL as standard way.

  • server Server object.
  • url URL information.
  • module Data type module prefix (AUTO-GEN by jstruct tool).
  • struc The object of this data type.
  • bin Data of payload.
  • bin_len Data length of payload.
  • return May be VSOA_SYNCER_PUB_OK or VSOA_SYNCER_ERROR.

RPC Syncer

vsoa_syncer_ret VSOA_PARAM_SYNCER_RPC(server, cli_id, module, struc, vsoa_header, payload, checker, arg);

If request method is get, this function will automatically reply to the RPC request. If the method is set, this function will automatically parse and sync the data, and publish the newest data to all the other clients.

  • server Server object.
  • cli_id Client id.
  • module Data type module prefix (AUTO-GEN by jstruct tool).
  • struc The object of this data type.
  • vsoa_header Data header of this RPC.
  • payload Payload from client.
  • checker This a callback for custom valid checking.
  • arg Argument of checker.
  • return May be VSOA_SYNCER_SET_OK or VSOA_SYNCER_GET_OK or VSOA_SYNCER_ERROR.
  • Returns: The RPC return status code. if not VSOA_STATUS_SUCCESS, then this function will reply with this status immediately.

NOTICE: The checker protype should be:

uint8_t (*checker) (void *arg, void *struc, vsoa_payload_t *payload);

VSOA Server Middleware

Middleware allows us to upgrade and expand the data processing of RPC requests on existing URLs without changing the original server or client, such as current limiting, fusing, logging, verification, etc. Through the middleware model, a URL can be a data processing chain, each step can be interrupted or continued to be transmitted to the next step, and each step can generate new data for use in the next step. In this way, we can share some standard and mature middleware in multiple projects, such as log middleware, login handshake middleware,data filtering middleware, etc

All the VSOA Server Middleware APIs are in the file vsoa_mware.h and libvsoa-server.so or libvsoa-hpserv.so.

Middleware Create

vsoa_mware_t * vsoa_mware_create(void);
  • Returns: If success, this return a new middleware object, return NULL if any errors.

When the middleware is no longer in use, you need delete it:

void vsoa_mware_delete(vsoa_mware_t *mware);
  • mware The middleware object.

Middleware Bind to URL

The middleware process an RPC request of URL, there is a middleware common callback, it's a VSOA server standard RPC callback, it is used for binding a RPC URL.

vsoa_server_cmd_func_t vsoa_mware_handler(vsoa_mware_t *mware);
  • mware The middleware object.
  • Returns: Return the common callback, if NULL, it means the mware is invalid.

Example

vsoa_server_t *server;
vsoa_mware_t  *mware;
vsoa_url_t     url;

server = vsoa_server_create("{\"name\":\"C language VSOA server\"}");
mware = vsoa_mware_create();

url.url     = "/bar";
url.url_len = strlen(url.url);
vsoa_server_add_listener(server, &url, vsoa_mware_handler(mware), mware);

In the above code, the middleware is bind to the RPC URL named "/bar". However, the middleware does not have any functions at present. We need to add our data processing steps to the middleware.

NOTICE: When using vsoa_server_plistener_handler() to add a middleware handler, the parallel parameter must be false.

Middleware RPC Hook

For a RPC request data, we can add one or more data process steps, each step provides a data processing hook.

bool vsoa_mware_add_listener(vsoa_mware_t *mware, vsoa_mware_cmd_func_t callback, void *arg);
  • mware The middleware object.
  • callback The hook callback.
  • arg The hook argument.
  • Returns: true if success, return false if any errors.

The hook callback details:

typedef bool (*vsoa_mware_cmd_func_t)(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);
  • arg The argument passed from vsoa_mware_add_listener API.
  • server VSOA server object.
  • id The client id.
  • vsoa_hdr The VSOA packet head.
  • url The url information.
  • payload The VSOA packet payload.
  • resolve The mware resolve data.
  • Returns: The return value is very important, if true, this will pass to next hook, if false, means current request is stop in this hook, and we must reply to the client.

Example

vsoa_mware_t *mware;

bool hook0 (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)
{
    if (...) { /* when data not need passt to next hook */
        /* do something */

        vsoa_server_cli_reply(...); /* must reply to client */
        return  (false);

    } else {
        /* do other thing */
        return  (true); /* pass to next hook */
    }
}

bool hook1 (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)
{
    /* do something */
    return  (false);
}

vsoa_mware_add_listener(mware, hook0, NULL);
vsoa_mware_add_listener(mware, hook1, NULL);

Note that the hook list is a fifo, so the hook0 will be called first.

Middleware Resolve Data

As metioned above, each step can generate new data and pass it to the next step, we use a resolve data type to manage this data, and each type resolve data is distinguished by unique identification.

Use following API to add the resolve data to next hook:

bool vsoa_mware_add_resolve_data(vsoa_mware_resolve_t *resolve, const char *key, void *value, vsoa_mware_resolve_free_func_t free);
bool vsoa_mware_add_resolve_data_with_length(vsoa_mware_resolve_t *resolve, const char *key, void *value, size_t value_len, vsoa_mware_resolve_free_func_t free);
  • resolve The resolve object passed from the hook.
  • key The resolve data identification, this is a constant string.
  • value The resolve data buffer.
  • value_len The resolve data buffer size. Default: 0 means unknown.
  • free If the data is dynamic allocation, we should provide the free method.
  • Returns: Return true if success, false if any errors.

Use following API to get the resolve data from last hook:

void *vsoa_mware_get_resolve_data(vsoa_mware_resolve_t *resolve, const char *key);
void *vsoa_mware_get_resolve_data_with_length(vsoa_mware_resolve_t *resolve, const char *key, size_t *value_len);
  • resolve The resolve object passed from the hook.
  • key The resolve data identification, this is a constant string.
  • value_len Can get the buffer size specified when adding. Default: 0 means unknown.
  • Returns: Return the resolve data buffer, NULL if any errors.

A return value of NULL indicates that the value corresponding to the specified key does not exist.

Example

bool log_hook (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 *fp = (FILE *)arg;
    char *msg;

    if (url->url_len && payload && payload->param_len) {
        fprintf(fp, "URL: %.*s payload: %.*s\n",
                (int)url->url_len, url->url, (int)payload->param_len, payload->param);
        fflush(fp);

        /* Add a new message data to next handler */
        msg = strdup("new resolved data");
        vsoa_mware_add_resolve_data(resolve, "log", msg, (vsoa_mware_resolve_free_func_t)free);
    }

    return  (true); /* pass to next hook */
}

bool msg_hook (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)
{
    char *msg = vsoa_mware_get_resolve_data(resolve, "log");

    if (msg) {
        printf("Log resolved data (key: \"log\") : %s\n", msg);
    }

    vsoa_server_cli_reply(my_server.server, cid, 0, seqno, 0, payload);

    return  (false);  /* Stop calling next handler */
}

FILE *fp = open("log_file", ...);

vsoa_mware_add_listener(mware, log_hook, fp);
vsoa_mware_add_listener(mware, msg_hook, NULL);  

In this example, the log hook will record the RPC data into a file, and then generate a new message with resolve key is 'log', and then add into the resolve data list. The message hook get the message using same key 'log', and reply the client at last. Note that in the log_hook, we pass the free method into vsoa_mware_add_resolve_data when add the new message.

Custom Data Bind

void vsoa_mware_set_custom(vsoa_client_t *client, void *custom);
void *vsoa_mware_custom(vsoa_client_t *client);

You can use above two APIs to bind and expand your own middlware framework.

Asynchronous multi calls

Sometimes when processing a command, we need to cooperate with other asynchronous data. For example, we may need to call another client's RPC and synthesize the results to reply. In this case, we need to resolve data to manually handle the life cycle. The following functions support resolve data references.

int vsoa_mware_ref_resolve(vsoa_mware_resolve_t *resolve);
  • resolve The resolve object passed from the hook.
  • Returns: The number of references after this function call.

This function returns the number of references to resolve. Returning negative number is an error. Normally, resolve data will be automatically released when the command callback chain ends. If an asynchronous callback is used in the user command callback list, resolve data can be retained through the vsoa_mware_ref_resolve() operation until it is released at vsoa_mware_unref_resolve().

NOTICE: That vsoa_mware_ref_resolve() and vsoa_mware_unref_resolve() must appear in pairs, otherwise a memory leak will occur.

int vsoa_mware_unref_resolve(vsoa_mware_resolve_t *resolve);
  • resolve The resolve object passed from the hook.
  • Returns: The number of references after this function call.

This function returns the number of references to resolve. Returning negative number is an error. When this function returns 0, resolve is no longer available.

NOTICE: All referenced resolve data must be unreference before calling vsoa_mware_delete().

bool vsoa_mware_reply_resolve(vsoa_mware_resolve_t *resolve, uint8_t status, uint16_t tunid, const vsoa_payload_t *payload);
  • resolve The resolve object passed from the hook.
  • status RPC reply status code, 0 means success.
  • tunid Used for stream transfer.
  • payload Payload to client, may be NULL.
  • Returns: true if success, false if any errors.

Reply to the caller with the RPC information saved when using vsoa_mware_ref_resolve().

The asynchronous call chain is more complicated, please refer to the example server: composite.

Middleware quickly creates and destroys

Middleware functions can be quickly used using the following APIs.

Create a synchronization service function queue for the specified URL:

vsoa_mware_t *vsoa_mware_add_server_listener(vsoa_server_t *server, const vsoa_url_t *url, vsoa_mware_listener_t cmds[], int cmd_cnt);
  • server VSOA server.
  • url Specified URL.
  • cmds Array of service functions.
  • cmd_cnt Number of functions.
  • Returns: If success, this return a new middleware object, return NULL if any errors.

example:

// Step return false to next step...
static bool step1 (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_mware_add_resolve_data(resolve, "step1", "111", NULL);
    return  (false);
}
// Step return false to next step...
static bool step2 (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_mware_add_resolve_data(resolve, "step2", "222", NULL);
    return  (false);
}
// Step return true to stop call list
static bool step3 (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)
{
    int cnt;
    char reply[256];
    vsoa_payload_t reply_payload;

    cnt = snprintf(reply, sizeof(reply),
             "{\"step1\":\"%s\",\"step2\":\"%s\",\"step3\":\"333\"}",
             vsoa_mware_get_resolve_data(resolve, "step1"),
             vsoa_mware_get_resolve_data(resolve, "step2"));

    reply_payload.param = reply;
    reply_payload.param_len = cnt;
    reply_payload.data = NULL;
    reply_payload.data_len = 0;

    vsoa_mware_reply_resolve(resolve, 0, 0, &reply_payload);
    return  (true);
}
// Main
int main ()
{
    vsoa_url_t url;
    vsoa_server_t *server;
    vsoa_mware_listener_t cmds[] = {
        { step1, NULL },
        { step2, NULL },
        { step3, NULL }
    };
    ...

    url.url     = "/test";
    url.url_len = strlen(url.url);
    vsoa_mware_add_server_listener(server, &url, cmds, 3);
    ...
}

Destroy:

void vsoa_mware_remove_server_listener(vsoa_mware_t *mware);

Same as vsoa_mware_delete().

VSOA Client

All the VSOA client APIs are in the file vsoa_client.h and libvsoa-client.so

Client Create

Use following API to create a VSOA client:

vsoa_client_t *vsoa_client_create(vsoa_client_msg_func_t onmsg, void *arg);
  • onmsg Subscription callback, this is called after the client receives the subscribed message.
  • arg The callback argument.
  • Returns: If success, this return a new client object, return NULL if any errors.

The onmsg protype:

typedef void (*vsoa_client_msg_func_t)(void *arg, vsoa_client_t *client, vsoa_url_t *url, vsoa_payload_t *payload, bool quick);
  • arg The callback argument.
  • client Client object.
  • url The URL of this message.
  • payload Payload of the URL.
  • quick Is it a quick publish message.

NOTICE: The message for all the subscribed URLs will come into the onmsg callback, so you need match and judge the URL to complete different work.

Our suggested standard URL matching rules:

PATHSubscribe match rules
"/"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.

When the client is no longer in use, you need to close it:

void vsoa_client_close(vsoa_client_t *client);

Set VSOA client socket linger time:

bool vsoa_client_linger(vsoa_client_t *client, int time);
  • client The client object.
  • time Linger time in seconds.
  • Returns: true if success, false if any errors.

This function can be called after each successful connect, the time unit is seconds, when the time is 0, it means that the close or disconnect call will be closed immediately.

Client Connect

A client uses following API to connect to the specified server.

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);
  • client The client object.
  • server Destination server address.
  • namelen Address length.
  • timeout Timeout for connecting to the server, if set to NULL, it means to wait forever until the server responds.
  • passwd Password of server, if the server does not have a password, please set it to NULL.
  • info Store the information of server when connect successfully.
  • sz_info The info buffer size.
  • Returns: true if success, false if any errors.

Example

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));

The example uses the position server (refer to the chapter VSOA Position Server) to query the address of server : vsoa.myserver.com, then connect to this server. This function is a synchronous function.

Use the following function to disconnect with server.

bool vsoa_client_disconnect(vsoa_client_t *client);

After disconnect, it is allowed to call vsoa_client_connect again to connect server.

Use the following function to check whether the VSOA client is connected to VSOA server.

bool vsoa_client_is_connect(vsoa_client_t *client);

The following API can be used to decompose URLs to facilitate the classification and processing of subscription information

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

This function method is similar to the C library strtok function.

Example

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);
    }
}

Client send timeout can be set.

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

If timeout is NULL, it means to use the default send timeout (100ms). If three consecutive sending times out, the connection is disconnected.

Client Event Loop

Like VSOA server, when a VSOA client is running, it should always monitor and handle all the input events with a loop using following two APIs.

int vsoa_client_fds(vsoa_client_t *client, fd_set *rfds);
bool vsoa_client_input_fds(vsoa_client_t *client, const fd_set *rfds);
  • rfds: All the events of the VSOA client that need to be monitor.

Example

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 */
}

In this example, you can see that when vsoa_client_input_fds return false, it indicates that the client is disconnected from server, and we can easily do the auto reconnect work in the event loop.

Client Ping

There is a better way that use Ping/Echo mechanism to check whether the connection is normal.

bool vsoa_client_ping(vsoa_client_t *client, vsoa_client_res_func_t callback, void *arg, const struct timespec *timeout);
  • client The client object.
  • callback This callback will be called when success or timeout.
  • arg Argument of the callback.
  • timeout Timeout for ping to the VSOA server, if set to NULL, it means to wait forever until the server responds.

The callback protype:

typedef void (*vsoa_client_res_func_t)(void *arg, vsoa_client_t *client, bool success);
  • success If true, menas connection is OK, if false, means timeout of this ping, connection is lost.

Client Subscribe

You can call vsoa_client_subscribe subscribe URL message or call vsoa_client_unsubscribe unsubscribe URL message.

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);
  • client The client object.
  • url The URL information.
  • callback This will be called after the sub/unsub request is successfully accepted by server(it does not mean that the message data has arrived).
  • arg Argument of the callback.
  • timeout Timeout for subscribe or unsubscrribe, if set to NULL, it means to wait forever until the server responds.
  • Returns: true if success, false if any errors.

Example

The following code snippet shows how to subscribe to the specified URL message.

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);

NOTICE: After the reconnection is successful, we need to perform all subscriptions again.

Client Multi Subscribe

You can call vsoa_client_multi_subscribe subscribe URLs message or call vsoa_client_multi_unsubscribe unsubscribe URLs message.

bool vsoa_client_multi_subscribe(vsoa_client_t *client, 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, char *const urls[], int cnt, vsoa_client_res_func_t callback, void *arg, const struct timespec *timeout);
  • client The client object.
  • urls The URLs information array.
  • cnt URLs array count.
  • callback This will be called after the sub/unsub request is successfully accepted by server(it does not mean that the message data has arrived).
  • arg Argument of the callback.
  • timeout Timeout for subscribe or unsubscrribe, if set to NULL, it means to wait forever until the server responds.
  • Returns: true if success, false if any errors.

Example

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

Client RPC

Use following API can execute once RPC call, VSOA RPC method include VSOA_CLIENT_RPC_METHOD_GET and VSOA_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);
  • client The client object.
  • method RPC method.
  • url The url information.
  • payload RPC payload, this payload will be sended to the VSOA server.
  • callback RPC callback.
  • arg Argument of the callback.
  • timeout Timeout for RPC, if set to NULL, it means to wait forever until the server responds.
  • Returns: true if success, false if any errors.

The RPC callback protype:

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

The memory pointed to by vsoa_hdr and payload will be invalidated when the callback function returns.

  • arg The argument passed from vsoa_client_call().
  • client Client object.
  • vsoa_hdr Packet header.
  • payload Payload data.

If you need a synchronous RPC call, you can use RPC call synchronization extension interface.

Example

The following code shows how to execute a GET RPC to the specified URL.

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);

NOTICE: The RPC is designed for asynchronous mode, so you can execute RPC calls continuously and in parallel.

Client Datagram

Use following APIs receive/send datagram from/to server:

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);

This is same as server datagram.

The datagram callback protype:

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

You can use the following API to send DATAGRAM to server through quick channel:

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

Example

The following code shows that how to transfer datagram for the specified 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

Client use following API to create a VSOA stream, if successful, this function will return new connect fd, otherwise -1 will be returned.

int vsoa_client_stream_create(vsoa_client_t *client, uint16_t tunid, const struct timespec *timeout, int keepalive);
  • client The client object.
  • tunid New tcp tunnel, replied from server, client uses it to connect to server.
  • timeout Timeout for client to connect to server, if set to NULL, it means to wait forever until the server responds.
  • keepalive Set new connect KEEPALIVE value.

NOTICE The client stream must be created after the server stream be created, so we usually use a RPC handshake to create a stream between client and server.

Client use following API to close an VSOA stream.

void vsoa_client_stream_close(int stream);
  • stream The stream fd created by API vsoa_client_stream_create().

Example

The following code shows how to create a VSOA stream.

/* 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();
}

More details, please refer to example/c/stream.c.

Synchronous RPC Calls

VSOA C client provides synchronous RPC call support.

vsoa_client_sync_call_t *vsoa_client_sync_create(bool dynamic);

VSOA client create a RPC call synchronizer, A synchronizer can only process one RPC synchronization call at the same time, and multi-threading is not allowed.

If dynamic is true, it means that the package memory is dynamically applied for each time the call receives a reply from the server. When it is false, it means that it is only applied statically once.

In the MATRIX653 system, the creation of the RPC call synchronizer is not allowed to be created in the operating mode is NORMAL, the developer must create the RPC call synchronizer for each task during initialization.

bool vsoa_client_sync_delete(vsoa_client_sync_call_t *sync);

VSOA client destroy an RPC call synchronizer, The synchronizer can only be destroyed after the use is completed, and is not allowed to be destroyed during the call.

In the MATRIX653 system, the RPC call synchronizer is not allowed to be deleted.

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 client performs a synchronous RPC call, this function will block until the call completes, an error occurs, or it times out, so this function cannot be used in the VSOA main event loop thread.

Return true means the call is successful, vsoa_hdr_reply is valid means the server has replied (otherwise means the command timed out), when the call is successful, you can check vsoa_hdr_reply to get the server reply, server reply, you can use vsoa_parser_get_payload() to get the reply payload.

The packet memory pointed to by vsoa_hdr_reply will be invalid when the next remote call is made or synchronizer is deleted.

It is recommended to create a synchronizer for each thread that will use synchronous RPC calls, and then use it sequentially in the corresponding thread.

Example

The following code shows how to use synchronous RPC calls.

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);

You can use above two APIs to bind and expand your own client framework.

VSOA Position Server

VSOA Position Server provides the function of querying VSOA server address by service name, similar to DNS server. The position module APIs are in the vdos_position.h and libvsoa-position.so

Position Server API

Create position server:

vsoa_position_server_t *vsoa_position_server_create(vsoa_position_query_func_t query, void *arg);
  • query Callback when a client query request arrived.
  • arg Callback argument.
  • Returns: If success, return a new position server object, otherwise return NULL.

The callback protype:

typedef void (*vsoa_position_query_func_t)(void *arg, int domain, const char *query_name, vsoa_position_response_t *response);
  • arg Callback argument.
  • domain Could be AF_INET or AF_INET6.
  • query_name The server's position name.
  • response This used for reply a query.

Start the position server:

bool vsoa_position_server_start(vsoa_position_server_t *server, const struct sockaddr *pos_addr, socklen_t addr_len);
  • pos_addr The address of the pisition server.
  • addr_len Length of the addr.
  • Return: true if success, false if any errors.

Reponse a query to client:

void vsoa_position_server_response(vsoa_position_server_t *server, vsoa_position_response_t *response, const struct sockaddr *vsoa_serv, socklen_t addr_len, bool sec);
  • server Server object
  • response This is from the vsoa_position_query_func_t callback.
  • vsoa_serv The address of server that reponse to client.
  • addr_len Length of the address.
  • sec Indicates that wether the server need secur.

You also can bind your custom data to the position server object:

void vsoa_position_server_set_custom(vsoa_position_server_t *server, void *custom);
void *vsoa_position_server_custom(vsoa_position_server_t *server);

Position Server Event Loop

The event loop of position server is very similar to that of VSOA common server. There are also a pair of APIs:

int vsoa_position_server_fd(vsoa_position_server_t *server);
void vsoa_position_server_input(vsoa_position_server_t *server);

Example

/* the query callback */
static void on_query (void *arg, int domain, const char *query_name, vsoa_position_response_t *response)
{
    struct sockaddr_in res;
    struct sockaddr_in *r;
    socklen_t l;
    vsoa_position_server_t *serv = arg;

    if (strcmp(query_name, "server1") == 0) {
        bzero(&res, sizeof(struct sockaddr_in));
        res.sin_family      = AF_INET;
        res.sin_port        = htons(3001);
        res.sin_addr.s_addr = inet_addr("192.168.1.5");
        res.sin_len = sizeof(struct sockaddr_in);
        r = res;
        l = sizeof(struct sockaddr_in);

    } else if (strcmp(query_name, "server2") == 0) {
        bzero(&res, sizeof(struct sockaddr_in));
        res.sin_family      = AF_INET;
        res.sin_port        = htons(3002);
        res.sin_addr.s_addr = inet_addr("192.168.1.8");
        res.sin_len = sizeof(struct sockaddr_in);
        r = res;
        l = sizeof(struct sockaddr_in);

    } else {
        /* no server position information */
        r = NULL;
        l = 0;
    }

    vsoa_position_server_response(serv, response, r, l, false);
}

/* server entry */
int main (int argc, char **argv)
{
    vsoa_position_server_t *serv;
    struct sockaddr_in pos_addr;

    fd_set fds;
    int cnt, fd;
    bool ret;

    /* create and start pos server */
    serv = vsoa_position_server_create(on_query, serv);
    if (!serv) {
        return  (-1);
    }

    ret = vsoa_position_server_start(serv, (struct sockaddr *)&pos_addr, sizeof(struct sockaddr_in));
    if (!ret) {
        return  (-1);
    }

    /* server event loop */
    while (1) {
        FD_ZERO(&fds);

        fd = vsoa_position_server_fd(serv);
        if (fd >= 0) {
            FD_SET(fd, &fds);
        } else {
            return  (-1);
        }

        cnt = pselect(fd + 1, &fds, NULL, NULL, NULL, NULL);
        if (cnt > 0) {
            vsoa_position_server_input(pos_serv);
        }
    }

    return  (0);
}

In this example, on_query is very simple to handle the position information of each server. Actually, you can use a JSON or a DataBase file to store/index the information.

Position Client API

For the VSOA client, just need lookup the information from position server. First the client should setup the address of position server:

bool vsoa_position_lookup_server(const struct sockaddr *pos_addr, socklen_t addr_len);
  • pos_addr The address of the position server.
  • addr_len Length of the address.
  • Returns: true if success, false if any errors.

If pos_addr is NULL or addr_len is 0, clear the previously set position server address.

After that, you can use this API to lookup:

bool vsoa_position_lookup(int domain, const char *serv_name, struct sockaddr *serv_addr, socklen_t *addr_len, bool *sec, const struct timespec *timeout);
  • domain Could be AF_INET or AF_INET6.
  • serv_name Server domain name.
  • serv_addr If success, this store the server's address.
  • addr_len The length of address.
  • sec Need security by this server.
  • timeout If NULL, will wait until the server responds.
  • Returns: true if success, false if any errors.

If position server is specified, only the specified server will be queried, If the position server address has not been specified, this function will first use the environment variable VSOA_POS_SERVER to query, if not found, it will use the server query configured in /etc/vsoa.pos (C:\Windows\System32\drivers\etc\vsoa.pos in Windows).

VSOA Tools

VSOA provides some tools that can be used to quickly test and diagnose your VSOA server or client program, or monitor data information.

All these tools is actually VSOA client, so the address of server must be offered. The address format of these tools is:

vsoa://192.168.1.1:3000
#or
vsoa://192.168.1.1:3000/a/b/c

If you don't use the details address, you can build one of your own VSOA position server according to your servers. At the same time, you also need a config file /etc/vsoa.pos, so that these tools can find the position server.

Assume the position server's ip is: 192.168.1.1, and port is: 3000:

cat etc/vsoa.pos

192.168.1.1 3000

Actually, there may be many position servers in a VSOA system, so the vsoa.pos file could contain many lines for each position server.

vcl

vcl is a VSOA command listener, it is a VSOA client and subscribe for monitoring all the publish data from specificed server.

General use:

vcl vsoa://vsoa.myserver.com 
  • vsoa:// the URL must start from this prefix, like http://
  • vsoa.myserver.com the server's domain name, this means we will monitor all the publish data of the server

If you just need a part of topics of the server:

vcl vsoa://vsoa.myserver.com/topic0
vcl vsoa://vsoa.myserver.com/topic0/data1
...

If the server need a password:

vcl -p 123456 vsoa://vsoa.myserver.com/

If you need show the details of the data (in HEX):

vcl -p 123456 -d vsoa://vsoa.myserver.com/

More details, you can use "vcl -h" to get help information.

vcx

vcx is a VSOA command executor that using VSOA RPC. Support RPC set/get, file transfer(support stream) and general data show.

RPC Get

vcx -g'{"value": 0}' vsoa://vsoa.myserver.com/data0
  • -g use GET method.
  • {"value": 0} the parameter in JSON format.

This will get the resource of data0 from server, and the parameter is a JSON string.

The parameter can be a file:

vcx -g'./data0.param' vsoa://vsoa.myserver.com/data0

If this resource need payload data information:

vcx -g'./data0.param' -d 123456 vsoa://vsoa.myserver.com/data0

The payload data can be from a file:

vcx -g'./data0.param' -d ./data0.data vsoa://vsoa.myserver.com/data0

If the payload data file is hex format:

vcx -g'./data0.param' -d ./data0.data -x vsoa://vsoa.myserver.com/data0

RPC Set

Like RPC Get:

vcx -s'./data0.param' -d ./data0.data vsoa://vsoa.myserver.com/data0
  • -s use SET method.

Other uses are exactly the same as RPC Get.

Use Stream

Receive file:

vcx -r'./video.dat' vsoa://vsoa.myserver.com/video0
  • -r means use stream to receive file.
  • ./video.dat the stream data will stored in this file.

Send file:

vcx -w'./video.dat' vsoa://vsoa.myserver.com/video0
  • -w means use stream to send file.
  • ./video.dat the data of this file will be sent to server.

More details, you can use "vcx -h" to get help information.


文档内容是否对您有所帮助?
有帮助
没帮助