服务端开发

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

服务端开发

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

开发须知

客户端和服务端建立连接后,双方均可发送和接收 VSOA Datagram。发送使用直接调用函数方式;而接收使用事件机制,需要绑定一个接收回调函数。

常用接口

void vsoa_server_on_datagram(vsoa_server_t *server, vsoa_server_dat_func_t callback,
                             void *arg);

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

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);
cli.datagram(url, payload[, quick]);

server.ondata(cli, url, payload, quick);
void Server.onData(CliHandle client, String url, Payload payload, boolean quick);

boolean CliHandle.datagram(String url, Payload payload, boolean quick);
func (s *Server) OnDatagram(servicePath string, handler func(*protocol.Message, *protocol.Message)) (err error)

说明:

VSOA Datagram 服务端的详细接口说明可参考以下手册:

开发示例

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

  1. 创建 VSOA 服务器 vsoa_datagram_server
  2. 连接客户端时,绑定一个 Datagram 接收事件的回调函数 datagram_callback
  3. 创建一个线程,间隔 1 秒向客户端发送一个 Datagram。
  4. datagram_callback 回调函数中,打印从客户端接收到的 Datagram。

运行成功时,服务端有如下输出:

  1. 在创建 VSOA 服务器成功后,打印 Started VSOA datagram server
  2. 在接收到客户端回传的 Datagram 后,打印 Datagram echo received from URL /axis with payload:,后面带有收到的数据。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "vsoa_parser.h"
#include "vsoa_server.h"

static vsoa_server_t    *server;

static void *datagram_send_thread (void *arg)
{
    vsoa_url_t      url;
    vsoa_payload_t  payload;
    vsoa_cli_id_t   id;
    char            send_string[50];
    int             roll    = 1;
    int             pitch   = 1;
    int             yaw     = 1;

    id = (vsoa_cli_id_t)(unsigned long)arg;

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

    payload.data        = NULL;
    payload.data_len    = 0;
    payload.param       = send_string;

    while (true) {
        payload.param_len = sprintf(send_string,
                                    "{\"roll\": %d, \"pitch\": %d, \"yaw\": %d}",
                                    roll++, pitch++, yaw++);

        vsoa_server_cli_datagram(server, id, &url, &payload);

        sleep(1);
    }

    return NULL;
}

void datagram_callback(void *arg, vsoa_server_t *server, vsoa_cli_id_t id,
                      vsoa_url_t *url, vsoa_payload_t *payload, bool quick)
{
    printf("Datagram echo received from URL %.*s with payload: %.*s\n",
           (int)url->url_len, url->url, (int)payload->param_len, payload->param);
}

void client_callback(void *arg, vsoa_server_t *server, vsoa_cli_id_t id, bool connect)
{
    pthread_t   thread;

    if (!connect) {
        fprintf(stderr, "Client connect failed.\n");
        return;
    }

    // register on datagram callback
    vsoa_server_on_datagram(server, datagram_callback, NULL);

    // create send datagram thread
    pthread_create(&thread, NULL, datagram_send_thread, (void *) (unsigned long) id);
}

int main(int argc, char *argv[])
{
    uint16_t            server_port = 3012;
    struct sockaddr_in  addr;

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

    // 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\":\"vsoa_datagram_server\"}");
    if (!server) {
        fprintf(stderr, "Can not create VSOA server!\n");
        return -1;
    }

    // start vsoa server
    if (!vsoa_server_start(server, (struct sockaddr *)&addr, sizeof(struct sockaddr_in))) {
        vsoa_server_close(server);
        fprintf(stderr, "Can not start VSOA server!\n");
        return -1;
    }

    printf("Started VSOA datagram server.\n");

    // register on client callback
    vsoa_server_on_cli(server, client_callback, NULL);

    // 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;
}
var vsoa    = require('vsoa');
var socket  = require('socket');
var process = require('process');

var SERVER_NAME = "vsoa_datagram_server"

/*
 * Create server
 */
var server = new vsoa.Server({
    info: {
        name: SERVER_NAME
    }
});

/*
 * Client event
 */
server.onclient = function (client, link) {
    if (!link) {
        return;
    }

    let roll = 0
    let pitch = 0
    let yaw = 0
    setInterval(() => {
        client.datagram('/axis', {
            param: {
                roll: roll++, pitch: pitch++, yaw: yaw++
            }
        });
    }, 1000)
};

/* Receive datagram */
server.ondata = function (cli, url, payload) {
    console.log('Datagram echo received from URL ' + url +
                ' with payload: ' + JSON.stringify(payload.param))
}

var server_port = 3012;
const auto_port = process.getenv('VSOA_AUTO_PORT');
if (auto_port) {
    server_port = Number(auto_port);
}

/*
 * Server start
 */
server.start({
    domain: socket.AF_INET, addr: '127.0.0.1', port: server_port
});

/*
 * Event loop
 */
require('iosched').forever();
import java.net.InetSocketAddress;
import java.util.Timer;
import java.util.TimerTask;

import com.acoinfo.vsoa.*;

public class datagram_server {
    private static String SERVER_NAME = "vsoa_datagram_server";
    private static String SERVER_INFO = "\"java language VSOA server\"";
    private static String SERVER_ADDR = "0.0.0.0";
    private static int SERVER_PORT = 3012;

    static Server server;

    public static void main(String[] args) {

        /*
         * Initialize server
         */
        try {
            ServerOption opt = new ServerOption(SERVER_INFO, null, false);
            InetSocketAddress address = new InetSocketAddress(SERVER_ADDR, SERVER_PORT);
            server = new Server(opt) {
                @Override
                public void onClient(CliHandle client, boolean link) {
                    if (!link) {
                        return;
                    }
                    TimerTask taskInterval = new TimerTask() {
                        @Override
                        public void run() {
                            String param = "{ \"roll\": " + roll++ +
                                    "; \"pitch\": " + pitch++ + "; \"yaw\": " + yaw++ + ";}";
                            client.datagram("/axis", new Payload(param, null));
                        }

                        private int roll = 1;
                        private int pitch = 1;
                        private int yaw = 1;
                    };

                    Timer interval = new Timer();
                    interval.schedule(taskInterval, 0 ,1000);
                }

                @Override
                public void onData(CliHandle client, String url, Payload payload, boolean quick) {
                    System.out.println("Datagram echo received from URL " +
                                       url + " with payload: " + payload.param);
                }
            };

            /*
             * Start server
             */
            if (!server.start(address, null)) {
                System.err.println("Can not start VSOA server!");
                return;
            }

            System.out.println("Started VSOA datagram server.");

        } catch (Exception e1) {
            e1.printStackTrace();
            return;
        }

        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}
package main

import (
    "fmt"
    "time"

    "gitee.com/sylixos/go-vsoa/protocol"
    "gitee.com/sylixos/go-vsoa/server"
)

// startServer initializes the Go VSOA server.
//
// It creates a new server with the given server options. The server password is set to "123456".
// If no password is needed, the password field can be left empty.
//
// The server registers a one-way handler for the "/datagram" URL. The handler receives a request message.
// It prints the URL and the parameter value.
//
// The server starts serving on the address "localhost:3001" in a separate goroutine.
func startServer() {
    // Initialize the Go VSOA server.
    // Set the server options.
    serverOption := server.Option{
        Password: "123456",
    }

    // Create a new server with the given options.
    s := server.NewServer("golang VSOA datagarm server", serverOption)

    // Define the handler function for the "/axis" URL.
    h := func(req, res *protocol.Message) {
        // Print the URL and the parameter value.
        fmt.Println("Datagram echo received from URL :", string(req.URL), " with payload: ", string(req.Param))
    }

    // Register the "/axis" URL with the handler function.
    s.OnDatagram("/axis", h)

    // Start serving on the address "localhost:3001" in a separate goroutine.
    go func() {
        _ = s.Serve("localhost:3001")
    }()
}

func main() {
    startServer()

    for {
        time.Sleep(1 * time.Second)
    }
}

说明:

  • 运行示例前,需要在 VSOA Position 服务中添加 vsoa_datagram_server 的解析。
  • 在 Node.js 环境中,需要替换为 process.env.VSOA_AUTO_PORT 来获取 VSOA_AUTO_PORT 环境变量。
  • 在 Node.js 环境中,不需要最后的事件循环操作,需要去除 require('iosched').forever()
  • 在 Node.js 环境中,socket.AF_INET 应为 vsoa.AF_INET,详情可见 https://www.npmjs.com/package/vsoa 。
  • 在 Node.js 环境中,需要去除对 socket 的引用,即删除 var socket = require('socket')

注意事项

C/C++ 服务端编译时需链接如下表所示的 VSOA 动态库,在 RealEvo-IDE 中配置时请参考 C/C++ 开发示例,Linux 下开发请参考 搭建 Linux 运行环境 提供的 C 语言范例进行配置。

库名称功能
libvsoa-json.so提供 JSON 功能
libvsoa-server.so提供服务端功能
libvsoa-parser.so提供参数解析功能
文档内容是否对您有所帮助?
有帮助
没帮助