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

觉得有帮助的话,打赏一下呗。。
需要商务合作(定制程序)的欢迎私信!!