4. 基于SOME/IP的服务通信

任务说明

本案例在AUTOSAR AP框架下,基于SOME/IP协议实现服务端与客户端之间的远程方法调用。服务端提供Add方法(输入两个32位整数,返回其和),客户端通过SOME/IP报文完成远程调用并获取结果。案例聚焦于协议栈配置、服务接口定义与调用链路的完整闭环。

服务接口定义(ARXML片段)

// 服务接口:CalculatorService
// 方法:Add
//   请求参数:int32 a, int32 b
//   响应参数:int32 result
// 服务ID:0x1234
// 实例ID:0x5678
// 方法ID:0x0001
// 事件组ID:0x0001(本例未使用事件通知)

SOME/IP协议栈配置(JSON格式)

{
  "network": {
    "interface": "eth0",
    "ip_address": "192.168.1.100",
    "port": 30501
  },
  "service_discovery": {
    "enabled": true,
    "offer_interval_ms": 1000,
    "ttl_ms": 3000
  },
  "services": [
    {
      "service_id": 0x1234,
      "instance_id": 0x5678,
      "major_version": 1,
      "minor_version": 0,
      "methods": [
        {
          "method_id": 0x0001,
          "name": "Add",
          "request_type": "int32,int32",
          "response_type": "int32"
        }
      ]
    }
  ]
}

服务端实现(C++)

#include <ara/com/someip/someip.h>
#include <ara/com/someip/service_base.h>
#include <functional>
#include <iostream>

using namespace ara::com;
using namespace ara::com::someip;

// 服务实现类
class CalculatorService : public ServiceBase {
public:
    CalculatorService() : ServiceBase(0x1234, 0x5678, 1, 0) {
        // 注册方法处理器
        RegisterMethodHandler(0x0001, 
            [this](const SomeIpMessage& request, SomeIpMessage& response) {
                HandleAdd(request, response);
            });
    }

private:
    void HandleAdd(const SomeIpMessage& request, SomeIpMessage& response) {
        // 解析请求负载:两个int32(网络字节序)
        if (request.payload.size() < 8) {
            response.return_code = ReturnCode::E_MALFORMED_MESSAGE;
            return;
        }
        int32_t a = ntohl(*reinterpret_cast<const int32_t*>(request.payload.data()));
        int32_t b = ntohl(*reinterpret_cast<const int32_t*>(request.payload.data() + 4));
        
        // 计算并填充响应
        int32_t result = a + b;
        response.payload.resize(4);
        *reinterpret_cast<int32_t*>(response.payload.data()) = htonl(result);
        response.return_code = ReturnCode::E_OK;
        std::cout << "[Server] Add(" << a << ", " << b << ") = " << result << std::endl;
    }
};

int main() {
    // 初始化协议栈(读取配置文件)
    SomeIpConfig config("someip_config.json");
    SomeIpStack stack(config);
    
    // 注册并启动服务
    CalculatorService service;
    stack.RegisterService(service);
    stack.Start();
    
    std::cout << "[Server] SOME/IP service started. Press Ctrl+C to stop." << std::endl;
    while (true) {
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    return 0;
}

客户端实现(C++)

#include <ara/com/someip/someip.h>
#include <ara/com/someip/proxy_base.h>
#include <iostream>
#include <chrono>

using namespace ara::com;
using namespace ara::com::someip;

// 服务代理类
class CalculatorProxy : public ProxyBase {
public:
    CalculatorProxy() : ProxyBase(0x1234, 0x5678, 1, 0) {}

    // 同步远程调用
    bool Add(int32_t a, int32_t b, int32_t& result, int timeout_ms = 1000) {
        SomeIpMessage request;
        request.service_id = 0x1234;
        request.instance_id = 0x5678;
        request.method_id = 0x0001;
        request.message_type = MessageType::REQUEST;
        request.payload.resize(8);
        *reinterpret_cast<int32_t*>(request.payload.data()) = htonl(a);
        *reinterpret_cast<int32_t*>(request.payload.data() + 4) = htonl(b);

        SomeIpMessage response;
        if (!InvokeMethod(request, response, timeout_ms)) {
            std::cerr << "[Client] RPC timeout or network error" << std::endl;
            return false;
        }
        if (response.return_code != ReturnCode::E_OK) {
            std::cerr << "[Client] Server returned error: " 
                      << static_cast<int>(response.return_code) << std::endl;
            return false;
        }
        if (response.payload.size() < 4) {
            std::cerr << "[Client] Malformed response" << std::endl;
            return false;
        }
        result = ntohl(*reinterpret_cast<const int32_t*>(response.payload.data()));
        return true;
    }
};

int main() {
    SomeIpConfig config("someip_config.json");
    SomeIpStack stack(config);
    
    CalculatorProxy proxy;
    stack.RegisterProxy(proxy);
    stack.Start();
    
    // 等待服务发现(最多3秒)
    if (!proxy.WaitForService(3000)) {
        std::cerr << "[Client] Service not available" << std::endl;
        return -1;
    }
    
    // 执行远程调用
    int32_t result = 0;
    if (proxy.Add(15, 27, result)) {
        std::cout << "[Client] Remote Add(15, 27) = " << result << std::endl;
    } else {
        std::cout << "[Client] RPC failed" << std::endl;
        return -1;
    }
    
    stack.Stop();
    return 0;
}

编译与运行

# 依赖:AUTOSAR AP SOME/IP协议栈库(ara::com::someip)
# 编译服务端
g++ -std=c++17 server.cpp -o someip_server -lara-com-someip -lpthread
# 编译客户端
g++ -std=c++17 client.cpp -o someip_client -lara-com-someip -lpthread

# 终端1:启动服务端(需先创建someip_config.json)
./someip_server

# 终端2:启动客户端
./someip_client

验证方法

判定标准:客户端控制台输出[Client] Remote Add(15, 27) = 42,服务端控制台同步输出[Server] Add(15, 27) = 42,且两端无错误日志,即判定远程调用链路验证通过。若客户端输出超时或错误码,则检查IP地址配置、服务发现周期及防火墙设置。

SOME/IP 远程调用时序与报文结构 服务端 Service ID: 0x1234 客户端 Proxy ID: 0x1234 阶段1:服务发现(UDP广播) SD Offer Service (0x1234, 0x5678) 阶段2:TCP连接建立 TCP SYN → ACK(端口30501) 阶段3:远程方法调用(TCP) REQUEST Method: Add(15,27) RESPONSE Result: 42 SOME/IP 报文头(16字节) Message ID (4B): 0x12345678 Length (4B): 负载长度+8 Request ID (4B): 客户端生成 Protocol (1B): 0x01 | Type (1B)