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地址配置、服务发现周期及防火墙设置。