4. SOME/IP服务端实现

任务目标

在DoIP(Diagnostic over IP)之上实现一个SOME/IP服务端,对外提供加法计算服务。客户端发送两个32位整数,服务端计算其和并返回结果。本案例聚焦SOME/IP服务端的核心实现:服务发现(OfferService)与请求/响应处理。

环境与依赖

  • 操作系统:Linux(Ubuntu 20.04+)或Windows(WSL2)
  • 编译器:g++ 9.4+(需支持C++17)
  • 依赖库:vsomeip(版本≥3.1.20),安装命令:sudo apt install libvsomeip-dev
  • DoIP作为底层传输由vsomeip的local配置自动处理,本案例不涉及DoIP协议细节

服务接口定义

使用SOME/IP的事件组(Eventgroup)与方法(Method)机制:

  • 服务ID:0x1234
  • 实例ID:0x5678
  • 方法ID:0x0421(加法计算)
  • 请求载荷:8字节(两个int32,小端序)
  • 响应载荷:4字节(int32结果)

完整实现代码

// calculator_service.cpp
#include <vsomeip/vsomeip.hpp>
#include <iostream>
#include <thread>
#include <cstring>

class CalculatorService {
public:
    CalculatorService() : app_(vsomeip::runtime::get()->create_application("calc_service")) {}

    bool init() {
        if (!app_->init()) {
            std::cerr << "Failed to init vsomeip" << std::endl;
            return false;
        }
        app_->register_message_handler(
            vsomeip::service_id_t(0x1234), vsomeip::instance_id_t(0x5678),
            vsomeip::method_id_t(0x0421),
            [this](const std::shared_ptr<vsomeip::message>& req) { on_request(req); });
        app_->register_availability_handler(
            vsomeip::service_id_t(0x1234), vsomeip::instance_id_t(0x5678),
            [this](vsomeip::service_t s, vsomeip::instance_t i, bool avail) {
                if (avail) std::cout << "Service available" << std::endl;
            });
        return true;
    }

    void start() {
        app_->offer_service(vsomeip::service_id_t(0x1234), vsomeip::instance_id_t(0x5678));
        app_->start();
    }

private:
    void on_request(const std::shared_ptr<vsomeip::message>& req) {
        // 解析请求载荷:两个int32
        const auto& payload = req->get_payload();
        if (payload->get_length() < 8) {
            std::cerr << "Invalid payload length" << std::endl;
            return;
        }
        const uint8_t* data = payload->get_data();
        int32_t a, b;
        std::memcpy(&a, data, 4);
        std::memcpy(&b, data + 4, 4);
        int32_t result = a + b;

        // 构造响应
        auto resp = vsomeip::runtime::get()->create_response(req);
        auto resp_payload = vsomeip::runtime::get()->create_payload();
        resp_payload->set_data(reinterpret_cast<uint8_t*>(&result), 4);
        resp->set_payload(resp_payload);
        app_->send(resp);
        std::cout << "Calc: " << a << " + " << b << " = " << result << std::endl;
    }

    std::shared_ptr<vsomeip::application> app_;
};

int main() {
    CalculatorService svc;
    if (!svc.init()) return -1;
    svc.start();
    // vsomeip::application::start() 会阻塞,此处不会返回
    return 0;
}

编译与运行

# 编译
g++ -std=c++17 -o calc_service calculator_service.cpp -lvsomeip

# 运行(需先启动vsomeip守护进程)
vsomeipd &
./calc_service

验证方法

使用vsomeip自带的someip_test_client工具或编写简单客户端发送测试请求:

# 使用vsomeip示例客户端(需单独编译)
# 发送请求:a=5, b=3(小端序:05 00 00 00 03 00 00 00)
# 预期响应:08 00 00 00(即8)

通过条件:

  1. 服务端启动后控制台输出Service available,表明服务已注册到vsomeip路由;
  2. 客户端发送合法请求后,服务端控制台打印Calc: 5 + 3 = 8;
  3. 客户端收到响应载荷为0x08 0x00 0x00 0x00(小端序int32值8)。

关键说明

  • 本案例使用vsomeip的本地IPC作为传输层,DoIP网关配置由vsomeip的vsomeipd守护进程统一管理,服务端代码无需感知DoIP细节;
  • 若需跨设备通信,需在vsomeip配置文件中指定DoIP端点(本案例不展开);
  • 服务端启动后阻塞在app_->start(),如需优雅退出可注册信号处理。