如何使用c++和gRPC实现流式(streaming) RPC? (双向通信)

发布时间 - 2026-01-22 00:00:00    点击率:
gRPC双向流(Bidi Streaming)支持客户端与服务器在单个RPC中同时异步收发多条消息,需在.proto中声明双stream,用async C++ API配合CompletionQueue手动管理读写协程及回调,漏调Read()或Write()将导致流停滞。

什么是 gRPC 双向流(Bidi Streaming)

gRPC 双向流 RPC 允许客户端和服务器在同一个 RPC 调用中**同时发送和接收多个消息**,连接保持打开,直到任意一方关闭。它对应 Protocol Buffer 中的 stream 关键字出现在请求和响应两侧:rpc Chat(stream ChatMessage) returns (stream ChatMessage);。这不是“先发后收”或“发一次收多次”,而是真正的并发读写——你得自己管理读/写协程或回调,否则容易卡死。

如何定义并生成双向流接口

关键在 .proto 文件里声明双 stream,且必须用 async C++ API(同步 API 不支持双向流)。生成代码时需启用 C++ 插件,并确保 grpc_cpp_plugin 版本与 libgrpc 匹配,否则 AsyncReaderWriter 类型可能缺失或签名不一致。

  • 定义示例:
    service ChatService {
      rpc Chat(stream ChatMessage) returns (stream ChatMessage);
    }
    message ChatMessage {
      string content = 1;
      int64 timestamp = 2;
    }
  • 生成命令要包含 --grpc_out--plugin=protoc-gen-grpc=.../grpc_cpp_plugin
  • 生成后你会得到 ChatService::AsyncService 和客户端 stub 的 AsyncChat 方法,返回类型是 std::unique_ptr<:clientasyncreaderwriter chatmessage>>

客户端如何发起并驱动双向流

客户端必须显式启动读写循环:先调用 Write() 发送首条消息(或空消息触发流建立),再立即调用 Read() 启动异步接收;后续靠 OnReadDone()OnWriteDone() 回调交替推进。漏掉任一 Read() 或重复 Write() 而没等完成,都会导致流挂起或崩溃。

  • 必须用 CompletionQueue 驱动异步事件,不能混用多个队列处理同一 stream
  • Write() 第二个参数是 void* 标签,用于区分不同操作;建议用 this 或枚举值,避免裸指针误释放
  • 调用 WritesDone() 表示客户端不再发消息,但服务端仍可继续发;之后收到 status.ok() == true 且无更多 Read() 时才算真正结束
  • 常见错误:Read() 后没再调用下一次 Read() → 流停滞;Write() 前未检查 write_ok_ → 写入失败被忽略

服务端如何响应并维持双向流

服务端用 AsyncServiceRequestChat() 接收新流,拿到 ServerAsyncReaderWriter 对象后,必须立刻调用其 Read() 启动接收,再根据业务逻辑决定何时 Write()。注意:所有 Read()/Write() 都是异步

且需配对回调,不能在回调里直接阻塞等待另一端。

  • 不要在 OnReadDone() 回调里直接调用 Write() 并期望立刻发出——必须再次 Write() + Next() 才生效
  • 若需广播消息(如聊天室),要把 ServerAsyncReaderWriter 指针安全存入容器,但注意生命周期:客户端断连时该对象会被销毁,需监听 Finish() 状态
  • 性能陷阱:频繁小包 Write() 会放大 TCP 小包问题,可考虑缓冲+定时 flush,但需权衡延迟
  • 调试提示:抓包看 HTTP/2 DATA 帧是否双向持续流动;若只有 client→server 有数据,大概率是 server 忘了 Read()Write() 没触发完成队列

双向流的本质不是“自动管道”,而是由你手动拼接的一组异步读写原语——漏掉一次 Read(),整个流就静默了。最易错的永远是“以为发完就完了”,其实只是刚开了个门。


# c++  # stream  # void  # 循环  # 指针  # 接口  # 并发  # 对象  # 事件  # this  # 异步  # http  # rpc  # 客户端  # 回调  # 服务端  # 多个  # 都是  # 你会  # 出现在  # 能在  # 要把  # 第二个 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 高端云建站费用究竟需要多少预算?  湖南网站制作公司,湖南上善若水科技有限公司做什么的?  如何安全更换建站之星模板并保留数据?  Win11怎么更改系统语言为中文_Windows11安装语言包并设为显示语言  Win11关机界面怎么改_Win11自定义关机画面设置【工具】  简单实现Android验证码  香港服务器网站搭建教程-电商部署、配置优化与安全稳定指南  Win11怎么查看显卡温度 Win11任务管理器查看GPU温度【技巧】  Laravel怎么使用Intervention Image库处理图片上传和缩放  如何将凡科建站内容保存为本地文件?  zabbix利用python脚本发送报警邮件的方法  实例解析angularjs的filter过滤器  Laravel怎么导出Excel文件_Laravel Excel插件使用教程  详解免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)  如何解决hover在ie6中的兼容性问题  北京网站制作公司哪家好一点,北京租房网站有哪些?  如何彻底删除建站之星生成的Banner?  CSS3怎么给轮播图加过渡动画_transition加transform实现【技巧】  如何在搬瓦工VPS快速搭建网站?  如何快速查询域名建站关键信息?  SQL查询语句优化的实用方法总结  Gemini手机端怎么发图片_Gemini手机端发图方法【步骤】  如何用好域名打造高点击率的自主建站?  如何自定义safari浏览器工具栏?个性化设置safari浏览器界面教程【技巧】  如何用5美元大硬盘VPS安全高效搭建个人网站?  品牌网站制作公司有哪些,买正品品牌一般去哪个网站买?  Laravel怎么实现前端Toast弹窗提示_Laravel Session闪存数据Flash传递给前端【方法】  C++时间戳转换成日期时间的步骤和示例代码  如何在阿里云购买域名并搭建网站?  android nfc常用标签读取总结  Laravel事件和监听器如何实现_Laravel Events & Listeners解耦应用的实战教程  活动邀请函制作网站有哪些,活动邀请函文案?  Linux系统运维自动化项目教程_Ansible批量管理实战  电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?  python中快速进行多个字符替换的方法小结  详解MySQL数据库的安装与密码配置  微信公众帐号开发教程之图文消息全攻略  微信小程序 wx.uploadFile无法上传解决办法  ,南京靠谱的征婚网站?  jQuery validate插件功能与用法详解  如何破解联通资金短缺导致的基站建设难题?  如何用AI帮你把自己的生活经历写成一个有趣的故事?  公司网站制作需要多少钱,找人做公司网站需要多少钱?  标题:Vue + Vuex + JWT 身份认证的正确实践与常见误区解析  Laravel如何实现一对一模型关联?(Eloquent示例)  如何在IIS中新建站点并解决端口绑定冲突?  如何续费美橙建站之星域名及服务?  Windows10电脑怎么查看硬盘通电时间_Win10使用工具检测磁盘健康  打造顶配客厅影院,这份100寸电视推荐名单请查收  太平洋网站制作公司,网络用语太平洋是什么意思?