C++ gRPC 四大核心场景实战|普通调用/服务端流/客户端流/双向流
摘要:gRPC 区别于传统 HTTP 接口最大的亮点就是支持四种通信模型,也是企业开发、面试的核心考点。本文基于 C++ 从零实战 gRPC 全部主流业务场景:简单单向调用、服务端流式推送、客户端流式上传、双向流式交互,覆盖日志上报、实时推送、文件传输、实时交互等真实业务,所有代码可直接编译运行。
关键词:C++ gRPC、gRPC流式通信、服务端流、客户端流、双向流、gRPC实战场景
|
通信模型 |
对应场景 |
业务用途 |
|---|---|---|
|
简单RPC(Unary) |
一问一答 |
接口查询、数据请求、普通业务接口 |
|
服务端流式(Server Stream) |
一次请求,多次响应 |
实时消息推送、日志批量下发、大数据分页返回、设备状态订阅 |
|
客户端流式(Client Stream) |
多次请求,一次响应 |
批量数据上报、日志采集、文件分片上传、设备心跳上报 |
|
双向流式(Bidirectional Stream) |
多次请求、多次响应、互不阻塞 |
实时聊天、音视频传输、实时游戏交互、实时设备双向通信 |
下面我们通过一套完整 Proto 协议 + C++ 服务端/客户端代码,一次性实现四种场景,覆盖 99% gRPC 业务开发需求。
二、统一 Proto 协议定义(四种场景全覆盖)
新建 scene.proto,同时定义四种通信模型接口,为后续 C++ 代码生成提供基础:
syntax = "proto3";
package scene;
option cc_generic_services = true;
// 通用请求
message CommonReq {
string msg = 1;
}
// 通用响应
message CommonRsp {
int32 code = 1;
string data = 2;
}
// 流式数据单元
message StreamData {
string content = 1;
int32 seq = 2;
}
// gRPC 四大场景服务定义
service SceneService {
// 1. 基础单向RPC:一问一答
rpc NormalCall(CommonReq) returns (CommonRsp);
// 2. 服务端流式RPC:客户端一次请求,服务端持续返回数据流
rpc ServerStreamCall(CommonReq) returns (stream StreamData);
// 3. 客户端流式RPC:客户端持续上传数据,服务端最后统一返回结果
rpc ClientStreamCall(stream StreamData) returns (CommonRsp);
// 4. 双向流式RPC:双方持续实时交互
rpc BidirectionalStreamCall(stream StreamData) returns (stream StreamData);
}
三、C++ 服务端完整实现(四种场景统一实现)
基于 gRPC 原生异步/流式接口,实现全部业务逻辑,代码规范可直接上线。
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <grpcpp/grpcpp.h>
#include "scene.grpc.pb.h"
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerReader;
using grpc::ServerReaderWriter;
using grpc::ServerWriter;
using scene::SceneService;
using scene::CommonReq;
using scene::CommonRsp;
using scene::StreamData;
class SceneServiceImpl final : public SceneService::Service
{
// 1. 普通单向RPC调用
grpc::Status NormalCall(ServerContext* context, const CommonReq* req, CommonRsp* rsp) override
{
std::cout << "[普通RPC] 收到客户端请求:" << req->msg() << std::endl;
rsp->set_code(200);
rsp->set_data("普通接口响应成功:" + req->msg());
return grpc::Status::OK;
}
// 2. 服务端流式:一次请求,服务端多次返回数据
grpc::Status ServerStreamCall(ServerContext* context, const CommonReq* req, ServerWriter<StreamData>* writer) override
{
std::cout << "[服务端流] 开始推送数据,客户端请求:" << req->msg() << std::endl;
// 模拟分5次流式推送数据
for (int i = 1; i <= 5; i++)
{
StreamData data;
data.set_seq(i);
data.set_content("服务端流式推送数据_" + std::to_string(i));
writer->Write(data);
}
return grpc::Status::OK;
}
// 3. 客户端流式:客户端多次上传,服务端最终统一返回
grpc::Status ClientStreamCall(ServerContext* context, ServerReader<StreamData>* reader, CommonRsp* rsp) override
{
std::cout << "[客户端流] 接收客户端批量上传数据..." << std::endl;
StreamData data;
std::vector<std::string> recv_list;
// 循环读取客户端流式数据
while (reader->Read(&data))
{
std::cout << "收到分片数据:seq=" << data.seq() << " content=" << data.content() << std::endl;
recv_list.push_back(data.content());
}
// 全部接收完成后统一响应
rsp->set_code(200);
rsp->set_data("客户端上传完成,总分片数:" + std::to_string(recv_list.size()));
return grpc::Status::OK;
}
// 4. 双向流式:双方实时互发数据
grpc::Status BidirectionalStreamCall(ServerContext* context, ServerReaderWriter<StreamData, StreamData>* stream) override
{
std::cout << "[双向流] 双向通信通道建立成功" << std::endl;
StreamData recv_data;
// 循环读取客户端实时消息
while (stream->Read(&recv_data))
{
std::cout << "双向流-接收客户端:" << recv_data.content() << std::endl;
// 实时回包响应
StreamData send_data;
send_data.set_seq(recv_data.seq());
send_data.set_content("服务端已收到:" + recv_data.content());
stream->Write(send_data);
}
return grpc::Status::OK;
}
};
void RunServer()
{
std::string addr = "0.0.0.0:9000";
SceneServiceImpl service;
ServerBuilder builder;
builder.AddListeningPort(addr, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "gRPC场景服务启动成功:" << addr << std::endl;
server->Wait();
}
int main()
{
RunServer();
return 0;
}
四、C++ 客户端完整实现(四种场景逐个调用)
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "scene.grpc.pb.h"
using grpc::Channel;
using grpc::ClientContext;
using grpc::ClientReader;
using grpc::ClientReaderWriter;
using grpc::ClientWriter;
using grpc::Status;
using scene::SceneService;
using scene::CommonReq;
using scene::CommonRsp;
using scene::StreamData;
class SceneClient
{
public:
SceneClient(std::shared_ptr<Channel> channel)
: stub_(SceneService::NewStub(channel)) {}
// 1. 普通单向调用
void TestNormal()
{
CommonReq req;
CommonRsp rsp;
ClientContext ctx;
req.set_msg("Hello Normal RPC");
Status status = stub_->NormalCall(&ctx, req, &rsp);
if (status.ok())
std::cout << "[普通调用结果] " << rsp.data() << std::endl;
}
// 2. 测试服务端流
void TestServerStream()
{
CommonReq req;
req.set_msg("请求服务端流式数据");
ClientContext ctx;
std::unique_ptr<ClientReader<StreamData>> reader = stub_->ServerStreamCall(&ctx, req);
StreamData data;
std::cout << "[服务端流接收数据]:" << std::endl;
while (reader->Read(&data))
{
std::cout << "seq:" << data.seq() << " content:" << data.content() << std::endl;
}
Status status = reader->Finish();
}
// 3. 测试客户端流
void TestClientStream()
{
CommonRsp rsp;
ClientContext ctx;
std::unique_ptr<ClientWriter<StreamData>> writer = stub_->ClientStreamCall(&ctx, &rsp);
// 模拟上传5个分片数据
for (int i = 1; i <= 5; i++)
{
StreamData data;
data.set_seq(i);
data.set_content("客户端分片数据_" + std::to_string(i));
writer->Write(data);
}
writer->WritesDone();
Status status = writer->Finish();
if (status.ok())
std::cout << "[客户端流结果] " << rsp.data() << std::endl;
}
// 4. 测试双向流
void TestBidirectionalStream()
{
ClientContext ctx;
std::unique_ptr<ClientReaderWriter<StreamData, StreamData>> stream = stub_->BidirectionalStreamCall(&ctx);
// 客户端连续发送3条消息
for (int i = 1; i <= 3; i++)
{
StreamData data;
data.set_seq(i);
data.set_content("客户端双向消息_" + std::to_string(i));
stream->Write(data);
// 同步读取服务端返回
StreamData recv;
if (stream->Read(&recv))
{
std::cout << "[双向流接收] " << recv.content() << std::endl;
}
}
stream->WritesDone();
Status status = stream->Finish();
}
private:
std::unique_ptr<SceneService::Stub> stub_;
};
int main()
{
SceneClient client(grpc::CreateChannel("127.0.0.1:9000", grpc::InsecureChannelCredentials()));
std::cout << "===== 1.测试普通单向RPC =====" << std::endl;
client.TestNormal();
std::cout << "\n===== 2.测试服务端流式RPC =====" << std::endl;
client.TestServerStream();
std::cout << "\n===== 3.测试客户端流式RPC =====" << std::endl;
client.TestClientStream();
std::cout << "\n===== 4.测试双向流式RPC =====" << std::endl;
client.TestBidirectionalStream();
return 0;
}
五、配套 CMakeLists.txt 编译文件
cmake_minimum_required(VERSION 3.13)
project(grpc_scene_demo)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Protobuf REQUIRED)
find_package(GRPC REQUIRED)
include_directories(${PROTOBUF_INCLUDE_DIRS})
set(PROTO_FILE scene.proto)
set(GENERATED_PROTO
${CMAKE_CURRENT_BINARY_DIR}/scene.pb.cc
${CMAKE_CURRENT_BINARY_DIR}/scene.pb.h
${CMAKE_CURRENT_BINARY_DIR}/scene.grpc.pb.cc
${CMAKE_CURRENT_BINARY_DIR}/scene.grpc.pb.h
)
add_custom_command(
OUTPUT ${GENERATED_PROTO}
COMMAND protoc
ARGS --grpc_out=${CMAKE_CURRENT_BINARY_DIR}
--plugin=protoc-gen-grpc=`which grpc_cpp_plugin`
--cpp_out=${CMAKE_CURRENT_BINARY_DIR}
${PROTO_FILE}
DEPENDS ${PROTO_FILE}
)
add_executable(server server.cpp ${GENERATED_PROTO})
target_link_libraries(server grpc grpc++ ${PROTOBUF_LIBRARIES} pthread)
add_executable(client client.cpp ${GENERATED_PROTO})
target_link_libraries(client grpc grpc++ ${PROTOBUF_LIBRARIES} pthread)
六、编译运行步骤
mkdir build && cd build
cmake ..
make -j4
# 终端1:启动服务端
./server
# 终端2:启动客户端,自动执行全部4种场景
./client
七、场景落地总结
1. 普通Unary RPC
适用于所有短平快的单次查询接口,如用户信息查询、配置获取、接口校验,是业务最常用模式。
2. 服务端流式 ServerStream
客户端只需一次请求,服务端持续推送,适合 消息订阅、实时日志推送、设备状态监控、大批量数据分页拉取。
3. 客户端流式 ClientStream
客户端持续上传分片数据,服务端汇总结果返回,适合日志批量上报、文件分片上传、传感器高频数据采集,极大减少网络握手开销。
4. 双向流式 BidirectionalStream
全双工实时通信,双方互不阻塞,是 IM聊天、实时游戏、音视频传输、远程设备控制 的核心方案。
八、文末总结
gRPC 相比传统 HTTP 接口最大的优势就是多形态流式通信能力,也是 C++ 后端高性能服务开发、面试的核心重难点。本文一套代码覆盖 gRPC 全部四大通信模型,所有案例贴合真实企业业务场景,可直接复用至微服务、物联网、实时通信、日志系统等项目。
更多推荐




所有评论(0)