ubuntu18.04 vsomeip的编译及使用

1、概述

vsomeip (Vehicle SOME/IP) 是 GENIVI 联盟(现并入 COVESA - Connected Vehicle Systems Alliance)推出的一个开源项目,它实现了 ​​SOME/IP (Scalable service-Oriented MiddlewarE over IP)​​ 协议。这是一个专为汽车和嵌入式系统设计的通信协议,用于支持服务发现、远程过程调用(RPC)和事件通知等功能。

下面是关于 vsomeip 的核心介绍,我用一个表格来汇总其主要特性:

特性维度 说明
​核心功能​ 实现了 SOME/IP 协议及其服务发现(Service Discovery)机制
​通信模式​ 支持请求/响应(Request/Response)、发布/订阅(Publish/Subscribe)等
​传输协议​ 支持 TCP 和 UDP
​内部通信​ 通过 Unix 域套接字实现同一设备内进程间的高速通信
​路由管理​ 中央路由管理器(Routing Manager)负责消息的路由和分发
​序列化​ vsomeip 自身不处理复杂数据结构的序列化,此部分通常由 CommonAPI 等工具完成
​主要依赖​ Boost 库(特别是 Asio、Thread、System)
​构建系统​ CMake
​配置方式​ 使用 JSON 格式的配置文件
​应用领域​ 车载信息娱乐系统(IVI)、车身控制、自动驾驶辅助系统等车载网络通信

2、 基本工作原理

vsomeip 的工作核心是 ​​服务(Service)​ ​ 和 ​​实例(Instance)​​。每个服务都有一个唯一的 Service ID,一个服务可以有多个实例(Instance ID)。通信双方(服务提供者 Consumer)通过 Service ID 和 Instance ID 来识别和定位服务。

其工作流程通常包含:

1.​服务注册与发现 (Service Discovery)​ ​: 服务提供者启动后,会通过 offer_service 方法宣告其提供的服务。客户端则通过**request_service** 和注册​​可用性回调函数​​来发现并监控服务的状态变化。

2.消息传递​ ​: 建立连接后,双方通过 ​​消息处理程序(Message Handler)​​ 来接收和处理请求或响应。服务端需为特定的 Method ID 注册消息处理器,客户端则发送请求并等待响应。

3、编译

vsomeip依赖Boost,因此我们需要先编译和安装Boost:

编译步骤:点击跳转

接着,编译和安装vsomeip:

下载: github下载csdn下载

复制代码
cmake -DCMAKE_INSTALL_PREFIX:PATH=/home/gui/gui/build -DBOOST_ROOT=/home/gui/gui/build -DBoost_DIR=/home/gui/gui/build -DENABLE_SIGNAL_HANDLING=1 -DDIAGNOSIS_ADDRESS=0x10 ..
make -j$(nproc)
sudo make install

避坑:

复制代码
vsomeip-master/implementation/endpoints/src/asio_socket_factory.cpp:27:2: error: extra ';' [-Werror=pedantic]
 };

解决:

  • 错误位置在asio_socket_factory.cpp代码最后一行的 };,这里多了一个分号,将其删掉便可!

4、demo

复制代码
//server.cpp
#include <vsomeip/vsomeip.hpp>
#include <chrono>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <iomanip> 
#include <signal.h>
#include <cstdio>
#define LOG_INF(...) std::fprintf(stdout, __VA_ARGS__), std::fprintf(stdout, "\n")
#define LOG_ERR(...) std::fprintf(stderr, __VA_ARGS__), std::fprintf(stderr, "\n")

// 全局常量定义
static vsomeip::service_t service_id = 0x1111;
static vsomeip::instance_t service_instance_id = 0x2222;
static vsomeip::method_t service_method_id = 0x3333;

// 全局变量 - 替代类的成员变量
static std::shared_ptr<vsomeip::runtime> rtm_;
static std::shared_ptr<vsomeip::application> app_;
static bool stop_ = false;
static std::mutex mutex_;
static std::condition_variable condition_;
static std::thread stop_thread_;

// 函数声明
bool init();
void start();
void stop();
void terminate();
void on_state_cbk(vsomeip::state_type_e _state);
void on_message_cbk(const std::shared_ptr<vsomeip::message>& _request);

// 初始化函数
bool init() 
{
    // 获取vSomeIP运行时并创建应用
    rtm_ = vsomeip::runtime::get();
    app_ = rtm_->create_application();

    // 初始化应用
    if (!app_->init()) 
	{
        LOG_ERR("Couldn't initialize application");
        return false;
    }

    // 注册消息处理器回调
    app_->register_message_handler(service_id, service_instance_id, service_method_id,
                                   on_message_cbk);

    // 注册状态处理器
    app_->register_state_handler(on_state_cbk);
    return true;
}

// 启动函数
void start() 
{
    // 启动应用
    app_->start();
}

// 停止线程函数
void stop() 
{
    std::unique_lock<std::mutex> its_lock(mutex_);
    while (!stop_) {
        condition_.wait(its_lock);
    }
    std::this_thread::sleep_for(std::chrono::seconds(5));
    
    // 停止提供服务
    app_->stop_offer_service(service_id, service_instance_id);
    // 注销状态处理器
    app_->unregister_state_handler();
    // 注销消息处理器
    app_->unregister_message_handler(service_id, service_instance_id, service_method_id);
    // 关闭应用
    app_->stop();
}

// 终止函数
void terminate() 
{
    std::lock_guard<std::mutex> its_lock(mutex_);
    stop_ = true;
    condition_.notify_one();
}

// 状态回调函数
void on_state_cbk(vsomeip::state_type_e _state) 
{
    if (_state == vsomeip::state_type_e::ST_REGISTERED) 
	{
        // 注册成功后提供服务
        app_->offer_service(service_id, service_instance_id);
    }
}

// 消息回调函数
void on_message_cbk(const std::shared_ptr<vsomeip::message>& _request) 
{
    // 打印接收到的数据
    if (_request && _request->get_payload()) {
        const vsomeip::payload* payload = _request->get_payload().get();
        std::string received_data(reinterpret_cast<const char*>(payload->get_data()), 
                                 payload->get_length());
        
        LOG_INF("Received data from client (length: %zu bytes):", payload->get_length());
        LOG_INF("  String: %s", received_data.c_str());
        
        // 以十六进制格式打印数据
        LOG_INF("  Hex: ");
        for (uint32_t i = 0; i < payload->get_length(); ++i) {
            printf("0x%02X ", payload->get_data()[i]);
        }
        printf("\n");
    }

    // 创建响应
    std::shared_ptr<vsomeip::message> resp = rtm_->create_response(_request);

    // 构造返回字符串
    std::string str("Hello ");
    str.append(reinterpret_cast<const char*>(_request->get_payload()->get_data()), 
              0, _request->get_payload()->get_length());

    // 创建返回负载
    std::shared_ptr<vsomeip::payload> resp_pl = rtm_->create_payload();
    std::vector<vsomeip::byte_t> pl_data(str.begin(), str.end());
    resp_pl->set_data(pl_data);
    resp->set_payload(resp_pl);

    // 打印发送的数据
    LOG_INF("Sending response to client (length: %zu bytes):", str.length());
    LOG_INF("  String: %s", str.c_str());
    
    // 以十六进制格式打印发送的数据
    LOG_INF("  Hex: ");
    for (char c : str) {
        printf("0x%02X ", static_cast<unsigned char>(c));
    }
    printf("\n");

    // 发送响应
    app_->send(resp);
	
	// 完成后终止
    terminate();
}

int main(int argc, char** argv) 
{

    // 初始化停止线程
    stop_thread_ = std::thread(stop);
    
    // 初始化并启动服务
    if (init()) 
	{
        start();
        
        // 等待停止线程结束
        if (stop_thread_.joinable()) 
		{
            stop_thread_.join();
        }
        return 0;
    } 
	else 
	{
        return 1;
    }
}

//client.cpp
#include <vsomeip/vsomeip.hpp>

#include <cstdio>
#define LOG_INF(...) fprintf(stdout, __VA_ARGS__), fprintf(stdout, "\n")
#define LOG_ERR(...) fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n")

// 全局变量代替类的成员变量
static vsomeip::service_t service_id = 0x1111;
static vsomeip::instance_t service_instance_id = 0x2222;
static vsomeip::method_t service_method_id = 0x3333;
static std::shared_ptr<vsomeip::runtime> rtm_;
static std::shared_ptr<vsomeip::application> app_;
static bool is_running = false;

void stop();
// 状态回调函数
void on_state_cbk(vsomeip::state_type_e _state) 
{
    if (_state == vsomeip::state_type_e::ST_REGISTERED) {
        // 注册成功后请求服务
        app_->request_service(service_id, service_instance_id);
    }
}

// 可用性回调函数
void on_availability_cbk(vsomeip::service_t _service, vsomeip::instance_t _instance, bool _is_available) 
{
    // 检查是否是我们需要的服务
    if (service_id == _service && service_instance_id == _instance && _is_available) {
        // 服务可用,发送请求
        std::shared_ptr<vsomeip::message> rq = rtm_->create_request();
        rq->set_service(service_id);
        rq->set_instance(service_instance_id);
        rq->set_method(service_method_id);

        // 创建并设置 payload
        std::shared_ptr<vsomeip::payload> pl = rtm_->create_payload();
        std::string str("World");
        std::vector<vsomeip::byte_t> pl_data(std::begin(str), std::end(str));

        pl->set_data(pl_data);
        rq->set_payload(pl);
        
        LOG_INF("Sending: %s", str.c_str());
        app_->send(rq);
    }
}

// 消息回调函数
void on_message_cbk(const std::shared_ptr<vsomeip::message>& _response) 
{
    if (service_id == _response->get_service() && service_instance_id == _response->get_instance()
        && vsomeip::message_type_e::MT_RESPONSE == _response->get_message_type()
        && vsomeip::return_code_e::E_OK == _response->get_return_code()) {
        
        // 获取并打印响应 payload
        std::shared_ptr<vsomeip::payload> pl = _response->get_payload();
        std::string resp = std::string(reinterpret_cast<const char*>(pl->get_data()), 0, pl->get_length());
        LOG_INF("Received: %s", resp.c_str());
        stop();
    }
}

// 初始化函数
bool init() 
{
    // 获取 runtime 并创建应用
    rtm_ = vsomeip::runtime::get();
    app_ = rtm_->create_application();

    // 初始化应用
    if (!app_->init()) {
        LOG_ERR("Couldn't initialize application");
        return false;
    }

    // 注册状态回调
    app_->register_state_handler(on_state_cbk);

    // 注册消息处理回调
    app_->register_message_handler(vsomeip::ANY_SERVICE, service_instance_id, vsomeip::ANY_METHOD, on_message_cbk);

    // 注册可用性回调
    app_->register_availability_handler(service_id, service_instance_id, on_availability_cbk);
    
    return true;
}

// 启动函数
void start() 
{
    is_running = true;
    app_->start();
    is_running = false;
}

// 停止函数
void stop() 
{
    if (app_) 
	{
        // 注销状态处理器
        app_->unregister_state_handler();
        // 注销消息处理器
        app_->unregister_message_handler(vsomeip::ANY_SERVICE, service_instance_id, vsomeip::ANY_METHOD);
        // 清除所有处理器
        app_->clear_all_handler();
        // 释放服务
        app_->release_service(service_id, service_instance_id);
        // 停止应用
        app_->stop();
    }
    is_running = false;
}

int main(int argc, char** argv) 
{
    if (init()) 
	{
        start();
        return 0;
    } 
	else 
	{
        return 1;
    }
}

//helloworld-local.json
{
    "unicast": "127.0.0.1",
    "logging": {
        "level": "debug",
        "console": "false"
    },
    "applications": [
        {
            "name": "server",
            "id": "0x4444"
        },
        {
            "name": "client",
            "id": "0x5555"
        }
    ],
    "services": [
        {
            "service": "0x1111",
            "instance": "0x2222",
            "unreliable": "30509"
        }
    ],
    "routing": "server",
    "service-discovery": {
        "enable": "false"
    }
}

//1.sh 编译脚本
#!/bin/bash

# 编译选项
CXX=g++
CXXFLAGS="-std=c++17 -Wall -Wextra -I."
LDFLAGS="-lvsomeip3 -lpthread"

# 源文件和目标文件
SERVICE_SRC="./server.cpp"
CLIENT_SRC="./client.cpp"
SERVICE_TARGET="server"
CLIENT_TARGET="client"

# 检查vsomeip是否安装
if ! pkg-config --exists vsomeip3; then
    echo "错误: 未找到vsomeip3库。请先安装vsomeip。"
    exit 1
fi

# 添加pkg-config提供的编译和链接选项
CXXFLAGS="$CXXFLAGS $(pkg-config --cflags vsomeip3)"
LDFLAGS="$LDFLAGS $(pkg-config --libs vsomeip3)"

# 编译服务端
echo "编译服务端..."
$CXX $CXXFLAGS -o $SERVICE_TARGET $SERVICE_SRC $LDFLAGS

# 检查编译是否成功
if [ $? -ne 0 ]; then
    echo "服务端编译失败"
    exit 1
fi

# 编译客户端
echo "编译客户端..."
$CXX $CXXFLAGS -o $CLIENT_TARGET $CLIENT_SRC $LDFLAGS

# 检查编译是否成功
if [ $? -ne 0 ]; then
    echo "客户端编译失败"
    exit 1
fi

echo "编译成功!生成了以下可执行文件:"
echo " - $SERVICE_TARGET"
echo " - $CLIENT_TARGET"

觉得有帮助的话,打赏一下呗。。

需要商务合作(定制程序)的欢迎私信!!

相关推荐
智驾1 年前
【开源分享】vsomeip 安装、编译、运行步骤笔记
中间件·soa·someip·vsomeip