摘要gRPC 区别于传统 HTTP 接口最大的亮点就是支持四种通信模型也是企业开发、面试的核心考点。本文基于 C 从零实战 gRPC 全部主流业务场景简单单向调用、服务端流式推送、客户端流式上传、双向流式交互覆盖日志上报、实时推送、文件传输、实时交互等真实业务所有代码可直接编译运行。关键词C gRPC、gRPC流式通信、服务端流、客户端流、双向流、gRPC实战场景通信模型对应场景业务用途简单RPCUnary一问一答接口查询、数据请求、普通业务接口服务端流式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, ServerWriterStreamData* 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, ServerReaderStreamData* reader, CommonRsp* rsp) override { std::cout [客户端流] 接收客户端批量上传数据... std::endl; StreamData data; std::vectorstd::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, ServerReaderWriterStreamData, 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_ptrServer 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_ptrChannel 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_ptrClientReaderStreamData 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_ptrClientWriterStreamData 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_ptrClientReaderWriterStreamData, 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_ptrSceneService::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} --pluginprotoc-gen-grpcwhich 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 全部四大通信模型所有案例贴合真实企业业务场景可直接复用至微服务、物联网、实时通信、日志系统等项目。