服务端开发
本节内容介绍 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)
cli.datagram(url: str, payload: vsoa.Payload | dict = None, quick: bool = False)
server.ondata((cli, url: str, payload: vsoa.Payload, quick: bool)
说明:
VSOA Datagram 服务端的详细接口说明可参考以下手册:
- C 版本可参考 VSOA C 语言编程手册。
- JavaScript 版本可参考 VSOA JavaScript 编程手册 VSOA Server Object 和 VSOA Remote Client Object。
- Java 版本可参考 VSOA Java 编程手册 VSOA Server Class 和 CliHandle Class。
- Golang 版本可参考 VSOA Golang 编程手册。
- Python 版本可参考 VSOA Pyhon 编程手册。
开发示例
在本示例中,服务端执行以下流程:
- 创建 VSOA 服务器
vsoa_datagram_server
。 - 连接客户端时,绑定一个 Datagram 接收事件的回调函数
datagram_callback
。 - 创建一个线程,间隔 1 秒向客户端发送一个 Datagram。
- 在
datagram_callback
回调函数中,打印从客户端接收到的 Datagram。
运行成功时,服务端有如下输出:
- 在创建 VSOA 服务器成功后,打印
Started VSOA datagram server
; - 在接收到客户端回传的 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)
}
}
from vsoa.server import Server, Client
from vsoa.timer import Timer
import vsoa
# Create server
server = Server('vsoa_datagram_server')
# Client event
def onclient(cli: Client, conn: bool):
if not conn:
return
timer = Timer()
def static_vars(**kwargs):
def decorate(func):
for k in kwargs:
setattr(func, k, kwargs[k])
return func
return decorate
@static_vars(roll = 0, pitch = 0, yaw = 0)
def publish_axis_task():
cli.datagram('/axis', {'param': {'roll': publish_axis_task.roll,
'pitch': publish_axis_task.pitch,
'yaw': publish_axis_task.yaw}})
publish_axis_task.roll += 1
publish_axis_task.pitch += 1
publish_axis_task.yaw += 1
timer.start(timeout=0., callback=publish_axis_task, interval=1.)
server.onclient = onclient
# Receive datagram
def ondata(cli: Client, url: str, payload: vsoa.Payload, quick: bool):
print('Datagram echo received from URL {} with payload: {}'.format(url, str(payload.param)))
server.ondata = ondata
# Get server port
server_port = int(auto_port) if (auto_port := os.environ.get('VSOA_AUTO_PORT')) else 3012
# Server start
server.run('127.0.0.1', server_port)
说明:
- 运行示例前,需要在 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 | 提供参数解析功能 |