什么叫服务通信
定义:以请求响应的方式实现不同节点间的数据交互通信模式,用于偶然触发,高实时性要求的场景。
俗解:不同于前面的不间断收发,假设存在客户端与服务端,他们之间是通过话题通信链接起来,此时客户端产生了新的需求并发送给服务端,服务端进行内部响应,响应完成后在传送回客户端。
实列分析:自动驾驶过程中,客户端通过手机发出修改目的地的请求,服务端接收请求,并进行路径的规划,规划完毕后再返还给服务端,服务端在发送结果给客户端。
案例分析以及解决方案
案例需求
需求:客户端发送数字,服务端接收数据并进行数据的求和处理,将结果响应并返还至客户端。
流程思路:该流程适用于CPP与PY的程序编写
•编写消息载体protobuf文件并进行配置
•编写服务端代码
•编写客户端代码
•编译并执行
Protobuf文件的编写
新建一个demo_pro的文件夹,这个文件夹是用来存放protobuf的相关文件,且在该文件夹下面创建昊BUILD文件和protobuf文件。
创建proto文件
// 申明版本号
syntax = "proto2";
//申明包
package apollo.cyber.demo_SC.demo_SC_pro;
//创建请求消息
message AddInts_Request {
//两个整形数据
required int64 num1 =1;
required int64 num2 =2;
}
//创建响应消息
message AddInts_Response {
//一个整形数据
required int64 sum =1;
}
他的主要功能是创建一个发布方与响应方的变量名
Build的内容如下:
load ("//tools:python_rules.bzl", "py_proto_library")
package (default_visibility = ["//visibility:public"])
proto_library(
name = "addints_proto",
srcs = ["SC_addinits.proto"]
)
cc_proto_library(
name = "addints_cc",
deps = [":addints_proto"]
)
py_proto_library(
name = "addints_py",
deps = [":addints_proto"]
)
Protobuf编写完成,编译执行(bazel build 目录/…)后,会生成 addints.pb.h、addints.pb.cc、addints.pb2.py。我们主要使用到其中的SC_addinits_pb.cc和SC_addinits.pb.h
客户端代码分析
客户端主要由BUILD文件demo_cc_SC.cpp组成
其中核心部分是demo_cc_SC.cpp其详细代码如下
/**
* @file demo_cc_SC.cpp
* @author bobo
* @brief 客户端发送请求,提交两个整数,服务端接收请求,提取数字相加后将结果响应回客户端
* @version 0.1
* @date 2022-07-05
*
* 实现流程
* 头文件包含
* 初始化
* 创建节点
* 创建服务方
* 数据处理
* 等待关闭
*/
#include "cyber/cyber.h"
#include "cyber/demo_SC/demo_SC_pro/SC_addinits.pb.h"
//调用了proto中的两个message
using apollo::cyber::demo_SC::demo_SC_pro::AddInts_Request;
using apollo::cyber::demo_SC::demo_SC_pro::AddInts_Response;
void cb(const std::shared_ptr<AddInts_Request>&request , const std::shared_ptr<AddInts_Response>&response){
int64_t num1 =request ->num1();
int64_t num2 =request ->num2();
AINFO << "客户请求num1 = "<< num1 << "num2 = "<<num2;
//设置求和并响应
int16_t sum = num1 +num2;
response ->set_sum(sum);
}
int main(int argc, char*argv[]){
//初始化
apollo::cyber::Init(argv[0]);
AINFO << "初始化完成";
//创建节点
auto server_node = apollo::cyber::CreateNode("server_node");
//设置服务方 提交的数据类型 响应的数据类型 字符串(话题)通过话题服务方和客户端关联在一起
auto server = server_node ->CreateService<AddInts_Request, AddInts_Response>("addints",cb);
//数据处理
//等待关闭
apollo::cyber::WaitForShutdown();
return 0;
}
服务端代码分析
服务端主要实现接收数据并进行计算的功能,其主要流程本质上是与客户端代码一致的
/**
* @file demo_cc_CS.cc
* @author bobo
* @brief
* @version 0.1
* @date 2022-07-05
* 包含头文件
* 初始化cyber
* 创建客户端节点
* 创建发布方
* 发送数据
* 等待关闭
*/
#include "cyber/cyber.h"
#include "cyber/demo_SC/demo_SC_pro/SC_addinits.pb.h"
using apollo::cyber::demo_SC::demo_SC_pro::AddInts_Request;
using apollo::cyber::demo_SC::demo_SC_pro::AddInts_Response;
int main(int argc, char* argv[]){
apollo::cyber::Init(argv[0]);
//处理输入的参数
if(argc != 3){
AINFO<<"执行程序时请输入两个数字作为参数";
return 1;
}
AINFO<<"客户端。。。。。。。。。。。。。。。。。。";
//创建客户端节点
auto client_node = apollo::cyber::CreateNode("client_node");
//创建发布方
auto client =client_node->CreateClient<AddInts_Request,AddInts_Response>("addints");
//发送数据
AINFO <<"等待服务启动";
auto request = std::make_shared<AddInts_Request>();
request ->set_num1(atoll(argv[1]));
request ->set_num2(atoll(argv[2]));
AINFO<<"发送num1"<<request ->num1() <<"num2 = " << request ->num2() ;
auto response = client ->SendRequest(request);
//处理响应
AINFO << "响应结果: sum = "<<response ->sum() ;
//等待关闭
apollo::cyber::WaitForShutdown();
return 0;
}
BUILD的内容如下所示:
# https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary
#***********************服务通信************************ #
cc_binary(
name = "demo_SC",
srcs = ["demo_cc_SC.cc"],
deps = ["//cyber",
"//cyber/demo_SC/demo_SC_pro:addints_cc",
],
)
cc_binary(
name = "demo_CS",
srcs = ["demo_cc_CS.cc"],
deps = ["//cyber",
"//cyber/demo_SC/demo_SC_pro:addints_cc",
],
)
编译上述程序得到如下文件
执行可执行文件 demo_SC与demo_CS,这里需要注意,要先执行服务端,再执行客户端,要不然会产生错误,这种错误是因为代码本身的执行逻辑就不支持这两者调换顺序,此外执行demo_CS时需要给定 两个 int64的参数,这样demo_SC才能有显示。