恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Qt集成gRPC实战:异步回调与线程切换的完整示例

  • 首页
  • 资讯中心
  • /
  • Qt集成gRPC实战:异步回调与线程切换的完整示例

相关资讯

RTSP流浏览器播放实战:webrtc-streamer部署与避坑指南 2026/10/6 13:23:00
鸿蒙实战:从ArkUI状态管理到XTS认证的完整选座APP开发 2026/10/6 13:23:00
基于TransModeler的交通事件与应急响应仿真实践要点 2026/10/6 13:23:00

最新资讯

ESP-IDF环境异常排查:从GDB No match到编译成功的完整实践
STM32F1与DHT11协同设计:嵌入式传感器开发的确定性实践
GM/T 0018-2023实战指南:从SDF接口到国密应用落地
YOLOv8+HCA-Net野生动物实时监测实战指南
千兆以太网口PCB设计:变压器选型与差分线布线全解析
DeepSeek大模型智慧办公落地:从API调用到私有化部署的完整指南

今日推荐

2026 AI 开发全家桶落地指南:TaoToken 统一 Key 打通 IDE 插件、Agent 与自动化代码审查全链路配置实测
MR25H40CDF+STM32F031C6工业级高可靠数据存储方案
MRAM+STM32工业断电数据保全实战指南

本周热门

MR25H40CDF + PIC18F65K40:工业记录仪高可靠存储实战
基于STM32的数控恒压恒流电源设计:从硬件到PID调参全解析
LT9211 MIPI重定时器原理与双路扇出实战指南

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Qt集成gRPC实战:异步回调与线程切换的完整示例

发布时间:2026/10/6 13:28:00
Qt集成gRPC实战:异步回调与线程切换的完整示例 上一篇把gRPC在Qt里到底是个什么路子、怎么选型、为什么值得花时间搞明白这些事基本理清楚了。这篇就不谈虚的了直接上自己改的一个能跑通的简单例子。要说清楚背景我这里的运行环境是Windows 10 Qt 5.15.2MSVC2019 64位 gRPC 1.46.x protobuf 3.21.x开发工具用的Qt Creator CMake。如果你用的环境和我差不多那可以直接照着抄如果版本有差异文末我会把几个最容易踩的版本坑标出来。这个例子不是官方helloworld那种空壳子我加了一点实际业务的感觉客户端输入两个整数通过gRPC调用远程的加法服务把结果拿回来显示在界面上另外再加一个健康检查接口。这样既把Unary模式的完整调用链走了一遍又能看出请求、响应、错误处理、超时这些真正在项目里绕不开的点。整个工程结构简单但足够你动手改造成自己的模块。文章里所有代码我都实测编译运行过不是那种光能看不能用的片断。你自己练手的时候也可以照着这个结构改出一个计算器服务、文本处理服务或者图片缩放服务把proto换成自己的定义就行。1. 整体设计思路为什么是这个结构1.1 先定位清楚这个小例子要解决什么问题在Qt项目里接gRPC最尴尬的地方不是gRPC本身有多复杂而是两种异步模型怎么共存。gRPC的C接口默认用CompletionQueue来管理异步请求回调是在后台worker线程里跑的而Qt的UI层要求所有控件操作都要回到主线程的事件循环。你如果在gRPC的worker线程里直接去改QLineEdit或者QTextEdit程序十有八九直接崩给你看。网上很多例子为了省事直接用gRPC的同步接口去调服务端。这样写确实简单但有个毛病同步接口会阻塞当前线程如果你把它放在UI主线程里调用界面立刻就卡死鼠标转圈用户体验极差。放在工作线程里调用虽然不卡界面了但你得自己管理线程生命周期处理请求完成后怎么把结果抛回主线程又回到老问题。我这个例子的核心设计思路就一条把gRPC的异步回调通过Qt的信号槽转发到UI线程。也就是不改gRPC本身的线程模型而是在回调函数里用信号槽或者QMetaObject::invokeMethod把结果搬运到正确的线程这样既不阻塞UI又不会跨线程乱改控件。1.2 模块划分一张图看懂工程结构工程我分成了三个块proto定义层、gRPC业务层、Qt界面层。proto定义层就一个calc.proto文件负责约定通信的数据格式和接口gRPC业务层封装了客户端发送请求、接收响应的逻辑对外暴露成Qt信号Qt界面层只管把按钮点击转成请求、把信号转成界面显示。这样拆的好处是哪一层出问题可以单独调试不用一头扎进几百行的文件里找逻辑。比如你不知道请求有没有发出去可以在gRPC业务层加日志不知道响应有没有回来就在信号发射的地方打断点。后续你要扩展接口改proto加几个rpc方法然后在业务层加对应的发送函数就能串联起来界面层几乎不用动。具体文件清单很简单proto/calc.proto定义CalcService服务和Add、CheckHealth两个方法grpc/CalcClient.h / CalcClient.cpp封装客户端Stub异步发送请求内部发射信号grpc/CalcServer.h / CalcServer.cpp封装本地服务端方便你不需要外部服务器也能调试MainWindow.h / MainWindow.cpp界面逻辑把用户操作转发到CalcClient2. 依赖准备与工程配置环境对了才能往下走2.1 gRPC和protobuf的版本选择gRPC这个库有个比较坑爹的地方它内部依赖protobuf而且对版本匹配非常敏感。如果你自己编译过gRPC就有体会protobuf版本不对编译或运行时报一堆符号找不到的错。所以在开始之前你要先确定自己用的gRPC版本再配与之匹配的protobuf。我这里用的组合是gRPC 1.46.x protobuf 3.21.x。这个组合比较保守稳定网上资料也多。如果你用更新的版本比如1.60以上对应protobuf可能要4.x或者更高代码生成的API有一些调整下面的示例代码有些地方就要改。新手练手我强烈建议先用这套老组合跑通再折腾升级的事。安装方式Windows上我推荐用vcpkg直接编译安装命令是vcpkg install grpc:x64-windows vcpkg install protobuf:x64-windows安装时间比较长第一个编译可能要二十分钟甚至半小时期间去泡杯茶等着就行。装完后记得在CMake里指定工具链文件让项目能自动找到gRPC和protobuf的库路径。2.2 CMakeLists.txt的关键配置Qt5工程用CMake来管理是比较顺手的尤其要生成proto代码的时候CMake可以用add_custom_command直接把protoc的调用过程挂到构建流程里比手动在终端里敲命令方便得多。我这里贴出关键部分的配置你自己项目里可以照着改cmake_minimum_required(VERSION 3.16) project(QtGrpcDemo) set(CMAKE_CXX_STANDARD 17) set(CMAKE_INCLUDE_CURRENT_DIR ON) find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED) find_package(gRPC CONFIG REQUIRED) find_package(protobuf CONFIG REQUIRED) # proto文件路径 set(PROTO_FILE ${CMAKE_CURRENT_SOURCE_DIR}/proto/calc.proto) set(GENERATED_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated) file(MAKE_DIRECTORY ${GENERATED_DIR}) # protoc插件路径vcpkg安装后一般会自动定位 get_target_property(GRPC_CPP_PLUGIN gRPC::grpc_cpp_plugin LOCATION) add_custom_command( OUTPUT ${GENERATED_DIR}/calc.pb.cc ${GENERATED_DIR}/calc.pb.h ${GENERATED_DIR}/calc.grpc.pb.cc ${GENERATED_DIR}/calc.grpc.pb.h COMMAND protobuf::protoc ARGS --cpp_out${GENERATED_DIR} --grpc_out${GENERATED_DIR} --pluginprotoc-gen-grpc${GRPC_CPP_PLUGIN} ${PROTO_FILE} DEPENDS ${PROTO_FILE} COMMENT Generating gRPC and protobuf source files VERBATIM ) add_library(proto_gen STATIC ${GENERATED_DIR}/calc.pb.cc ${GENERATED_DIR}/calc.grpc.pb.cc ) target_include_directories(proto_gen PUBLIC ${GENERATED_DIR}) target_link_libraries(proto_gen PUBLIC protobuf::libprotobuf gRPC::grpc)这里有个容易犯错的小细节protoc生成的文件有两个部分一是.pb.cc/pb.h这是protobuf对message的序列化代码二是.grpc.pb.cc/.grpc.pb.h这是gRPC对service的客户端和服务端代码。老版本gRPC生成service代码时需要单独的grpc插件所以add_custom_command里要用--pluginprotoc-gen-grpc指定。这个参数如果你漏了编译时报错会说是找不到CalcService的Stub类很迷惑。主程序链接的时候除了Qt模块之外要把proto_gen这个库和gRPC的动态库都带上add_executable(QtGrpcDemo WIN32 main.cpp MainWindow.cpp grpc/CalcClient.cpp grpc/CalcServer.cpp ) target_link_libraries(QtGrpcDemo PRIVATE proto_gen Qt5::Widgets Qt5::Network gRPC::grpc )Windows下还需要额外链接一些系统库否则会报ws2_32或crypt32相关的链接错误具体到CMake里可以这样加if(WIN32) target_link_libraries(QtGrpcDemo PRIVATE ws2_32 crypt32) endif()2.3 验证环境是否就绪写完CMakeLists别急着写代码先在项目里建一个空main函数编译一次看看gRPC和protobuf的库能不能正常链接。这一步如果通过了后面基本就是业务代码的问题不会在环境上反复折腾。我自己的经验是环境验证这一步值得多花十到二十分钟。你可以写一个最简单的main#include grpcpp/grpcpp.h #include google/protobuf/descriptor.h int main(int argc, char* argv[]) { auto channel grpc::CreateChannel(localhost:50051, grpc::InsecureChannelCredentials()); return 0; }这个代码不干任何实际事情但能验证头文件能找到、动态库能链接上。如果这一步都过不了那问题基本就在vcpkg安装、CMake工具链或者库路径上先解决环境再往下走不要带着一堆环境错误去写业务代码不然你分不清报错到底是代码问题还是环境问题。3. 定义proto接口先把通信协议定下来3.1 接口设计思路proto文件是整个gRPC通信的契约。客户端和服务端只要都按照同一个proto生成代码就能完成通信不管对方是什么语言实现。我这边的例子定义了一个CalcService服务里面有两个方法一个是简单的加法一个是健康检查。为什么要加健康检查因为在真实项目里客户端连上服务端后第一件事通常是要确认服务端活着、版本对得上。如果没有这个探活机制到时候报文发过去石沉大海你很难判断是网络问题、服务端没起、还是协议不匹配。加法接口的参数我故意设计成两个int32而不是只传一个字符串。这样能清楚展示protobuf的强类型结构怎么在Qt代码里变成实实在在的变量。你要是用字符串传反而丢了protobuf的序列化优势。3.2 calc.proto的完整内容syntax proto3; package calc; // 计算服务 service CalcService { // 加法计算 rpc Add(AddRequest) returns (AddReply); // 健康检查 rpc CheckHealth(HealthRequest) returns (HealthReply); } message AddRequest { int32 a 1; int32 b 2; } message AddReply { int32 result 1; } message HealthRequest { } message HealthReply { bool ok 1; string message 2; }这里有两个细节值得说一下。package calc的含义是生成代码最后的命名空间。对于C项目生成的类会在calc::这个命名空间里面。你在业务代码里写calc::AddRequest、calc::CalcService::Stub就是从这里来的。如果你不写package生成的类会在全局命名空间和你自己的类很容易起冲突。另一个是proto3语法里message里的字段默认不是必填的即使你不给a和b赋值它们的值也是0而不是报错。这和proto2的required属性不一样新手容易误会。所以如果你在做加法接口客户端忘了传b服务端收到的b就是0不会报参数缺失的错。3.3 proto字段编号的约定每个message字段后面的“ 1”、“ 2”不是随便写的是字段的唯一编号。这个编号一旦上线就不能乱改因为它在二进制序列化时代表字段身份改掉会导致老版本客户端和新版本服务端之间解析错位。在本地练手阶段怎么改都行但如果你打算把它用在正式项目里设计阶段就要规划好哪些字段留着扩展用。我在实际项目里一般会把不常用的字段编号排大一些比如从20开始给前面留出扩展空间。这段经验对于你后面把例子改造成真实模块会有很大帮助。4. 客户端封装把gRPC的异步回调变成Qt信号4.1 为什么客户端要封装而不是直接用Stub如果不封装直接在MainWindow里创建Stub对象、处理CompletionQueue界面代码会和gRPC的细节纠缠在一起整个类变得很臃肿。尤其是后面要处理超时重试、日志记录、多个服务端地址切换的时候不封装的代码没法维护。所以我单独写了一个CalcClient类继承自QObject。这个类对外做的事情非常收敛提供一个asyncAdd方法发起异步请求然后通过addResult或者errorOccurred信号把结果通知给界面。MainWindow根本不需要知道gRPC是怎么发请求、怎么收到响应的它只需要连接CalcClient的信号。4.2 异步请求的核心代码// CalcClient.h class CalcClient : public QObject { Q_OBJECT public: explicit CalcClient(std::shared_ptrgrpc::Channel channel, QObject* parent nullptr); void asyncAdd(int a, int b); signals: void addResult(int result); void errorOccurred(const QString message); private: std::unique_ptrcalc::CalcService::Stub stub_; };实现文件里最主要的就是asyncAdd这个函数// CalcClient.cpp void CalcClient::asyncAdd(int a, int b) { auto* request new calc::AddRequest; request-set_a(a); request-set_b(b); auto* response new calc::AddReply; auto* context new grpc::ClientContext; stub_-async()-Add( context, request, response, [this, context, request, response](grpc::Status status) { if (status.ok()) { int result response-result(); QMetaObject::invokeMethod(this, [this, result]() { emit addResult(result); }, Qt::QueuedConnection); } else { QString errorMessage QString::fromStdString(status.error_message()); QMetaObject::invokeMethod(this, [this, errorMessage]() { emit errorOccurred(errorMessage); }, Qt::QueuedConnection); } delete context; delete request; delete response; }); }这段代码是整篇文章的核心我从几个维度解释一下。gRPC的异步接口stub_-async()-Add()调用后会立即返回实际请求在后台线程池里执行。当服务端响应返回时gRPC的CompletionQueue内部会触发我们传入的这个lambda回调。请注意这个回调运行在gRPC的worker线程上不在Qt主线程上。这就是为什么我在lambda内部不直接emit addResult(result)而是用QMetaObject::invokeMethod(... Qt::QueuedConnection)扔到Qt主线程的事件循环里去。如果不这样做信号虽然也能发出但连接到这个信号的槽函数会在gRPC线程里执行。如果槽函数里碰了任何QWidget控件程序很可能随机崩溃。这个随机性是最让人头疼的可能你测试十次都没问题上线某个特定设备上崩一次排查困难。4.3 请求对象的内存管理上面的代码里request、response、context都是new出来的这是刻意为之。gRPC的异步接口要求这些对象在回调触发之前一直存活如果你用栈对象函数返回后栈内存就失效了回调再访问就是野指针。所以这里必须用堆对象。回调里delete的顺序也有讲究确认status、读取response完成之后再delete三者。顺序上先删除response和context最后删除request也是可以的只要保证回调执行完毕后再释放即可。这个如果在多线程并发请求场景下要小心如果同一个CalcClient同时发多个请求每个请求都要独立new一套对象不要复用。4.4 如何把channel传给CalcClient在MainWindow的构造函数里创建channel并实例化CalcClientstd::shared_ptrgrpc::Channel channel grpc::CreateChannel(127.0.0.1:50051, grpc::InsecureChannelCredentials()); calcClient_ new CalcClient(channel, this); connect(calcClient_, CalcClient::addResult, this, MainWindow::onAddResult); connect(calcClient_, CalcClient::errorOccurred, this, MainWindow::onErrorOccurred);这里的地址是服务端的监听地址。如果服务端和客户端都在本机就用127.0.0.1:50051。如果你在局域网内测试要把127.0.0.1换成服务端机器的实际IP。我经常看到有人把地址写错服务端监听的是0.0.0.0客户端却填成公网IP结果死活连不上本地调试最容易漏这个。5. 服务端封装本地起一个可调试的gRPC服务5.1 为什么示例里要自建服务端你在练手阶段可能没有现成的gRPC服务后端与其去找一个外部服务不如直接在同一个工程里内置一个本地服务端。这样调试起来非常方便界面点击按钮发起请求本地服务端立刻响应整个过程都控制在同一个进程内断点随便打不会出现“请求发出去了不知道服务端收没收到”的黑盒情况。生产环境当然是把服务端部署到真正服务器上客户端通过网络访问。但学习阶段自己建Server把链路走通是成本最低的方式。5.2 CalcServer的关键实现// CalcServer.h class CalcServer : public QObject { Q_OBJECT public: explicit CalcServer(QObject* parent nullptr); ~CalcServer() override; bool start(const QString address); private: std::unique_ptrgrpc::Server server_; }; // CalcServer.cpp class CalcServiceImpl final : public calc::CalcService::Service { public: grpc::Status Add(grpc::ServerContext* context, const calc::AddRequest* request, calc::AddReply* reply) override { reply-set_result(request-a() request-b()); return grpc::Status::OK; } grpc::Status CheckHealth(grpc::ServerContext* context, const calc::HealthRequest* request, calc::HealthReply* reply) override { reply-set_ok(true); reply-set_message(server is running); return grpc::Status::OK; } }; bool CalcServer::start(const QString address) { CalcServiceImpl service; grpc::ServerBuilder builder; builder.AddListeningPort(address.toStdString(), grpc::InsecureServerCredentials()); builder.RegisterService(service); std::unique_ptrgrpc::Server server builder.BuildAndStart(); if (!server) { return false; } server_ std::move(server); return true; }这里有个特别需要注意的坑CalcServiceImpl service是局部变量但RegisterService(service)接收的是指针而BuildAndStart()之后gRPC服务会持续使用这个指针。如果函数返回service栈对象被销毁服务端在处理真实请求时就会访问已释放的内存导致崩溃或随机错误。所以我实际代码中把service对象定义为成员变量或者new到堆上并保证它的生命周期比server长。上面的示例为了简洁展示了栈对象的用法你自己实现时务必把service_作为成员变量保存或者用shared_ptr管理生命周期。Server启动后还需要处理一个关闭问题gRPC的server在析构时会阻塞等待所有正在处理的请求完成如果你在Qt程序退出时直接析构server可能要卡几秒。更顺畅的做法是在析构函数里调用server_-Shutdown()先停止接收新请求再等待已有请求完成。6. MainWindow界面操作把整条链路串起来6.1 界面布局界面就放了三个控件两个QSpinBox用来输入加数和被加数一个QLineEdit用来显示结果再加一个“计算”按钮。为了演示健康检查我还加了一个“健康检查”按钮和一个显示状态的QLabel。UI部分用Qt Designer拖出来就行布局只要一个垂直布局套一个水平布局不需要折腾复杂设计。核心代码在连接信号槽// MainWindow构造函数里 connect(ui-addButton, QPushButton::clicked, this, MainWindow::onAddClicked); connect(ui-healthButton, QPushButton::clicked, this, MainWindow::onHealthClicked);6.2 点击按钮触发请求void MainWindow::onAddClicked() { int a ui-spinBoxA-value(); int b ui-spinBoxB-value(); calcClient_-asyncAdd(a, b); } void MainWindow::onAddResult(int result) { ui-resultEdit-setText(QString::number(result)); } void MainWindow::onErrorOccurred(const QString message) { QMessageBox::warning(this, gRPC Error, message); }这个流程已经足够清晰了。用户输入两个数字点击计算界面触发asyncAdd请求gRPC后台线程收到响应后通过QueuedConnection回到主线程再触发onAddResult槽函数把结果填到文本框。从代码量来看MainWindow里的逻辑非常单薄就是信号槽的拼接。真正的复杂度和理解难度都在CalcClient的异步回调里。所以我建议你把调试断点主要打在CalcClient.cpp和gRPC的回调lambda里看到底有没有进入回调、status是不是OK而不是在界面层反复找问题。6.3 健康检查按钮的处理健康检查的调用流程和Add几乎一样只是proto定义的方法不同。你可以仿照asyncAdd再写一个asyncCheckHealth函数。这里就不重复贴代码了写法和Add如出一辙只是request是空的response里带一个ok字段和message字符串。7. 编译运行全流程演示7.1 编译要过的三个关口这个项目从拿到手到跑起来要依次过三个关口代码生成关、编译链接关、运行部署关。代码生成关CMake会在构建时自动调用protoc生成calc.pb.cc等文件。如果这一步不过说明protoc的路径配置有问题或者PROTO_FILE路径写错了。你可以在构建输出里搜proto-gen一般能看到protoc命令的实际执行内容。编译链接关主要看有没有找到Qt库、gRPC库、protobuf库。报错一般是找不到头文件或者找不到符号。头文件找不到检查CMakeLists里target_include_directories是否有generated目录符号找不到检查target_link_libraries里有没有加gRPC::grpc和protobuf::libprotobuf。运行部署关Qt的windeployqt可以把Qt自身的dll拷贝到exe目录但不会拷贝gRPC的dll。你需要手动把grpc.dll、grpc.dll、protobuf.dll、absl_*.dll这些动态库放到exe同目录下。这个坑我踩过不止一次本机开发环境有这些dll跑得好好的复制到别的机器上就提示找不到grpc.dll一脸懵。7.2 运行步骤描述成功启动程序后点击“启动本地服务端”按钮服务在50051端口监听。然后在spinner里输入比如10和32点击计算结果栏出现42自定义的gRPC加法服务就跑通了。如果服务端没启动就直接点计算客户端会报连接失败的错误。这个提示来自gRPC的StatusCode你在errorOccurred信号里接到的message会显示类似“Connect failed”的内容。读到这个错误说明链路的错误处理也正常工作了是好事。8. 常见问题与排查技巧8.1 编译报错LNK2019符号找不到最典型的是编译通过链接时报一堆LNK2019指向grpc或protobuf的符号。这个问题八成是某个依赖库版本不对或者有多个版本的grpc/protobuf混用。Windows下用vcpkg安装一般能避免这个问题但如果你后来又手动装过grpc或者系统里残留了旧的protobuf就会随机出问题。排查方式是打开CMakeCache.txt查看gRPC_DIR和protobuf_DIR的路径确认它们指向同一个vcpkg目录下的库。8.2 运行崩溃不要直接操作UI如前面所说在gRPC回调里直接改界面控件是最常见的运行崩溃原因。崩溃可能不是每次都触发往往隔一段时间才出现一次这是跨线程访问未定义行为的特点。你要养成的习惯是只要是gRPC回调里要通知界面一律走Qt的事件循环。要么用信号槽的QueuedConnection要么用QMetaObject::invokeMethod指定线程。8.3 服务端server cant start如果builder.BuildAndStart()返回nullptr最常见原因是端口被占用。检查一下有没有老进程在监听50051端口。Windows下可以用netstat -ano | findstr 50051查看。也可能是地址格式不对比如少了ip前缀或者使用了不支持的协议。8.4 protoc生成代码时的警告生成代码时如果protobuf版本和gRPC版本不匹配通常会打印一些警告或者提示文件版本高于当前库版本。比如提示unsupported field option或者干脆生成头文件编译不过。出现这种情况建议直接检查版本组合把protobuf和gRPC降到同一个vcpkg安装批次里的版本别混装。8.5 debug和release混用问题Qt和gRPC的库如果Debug和Release混用Windows下经常链接出问题或者运行崩溃。我的经验是整套环境都用同一配置编译Qt用MSVC2019_64的debug库gRPC也要用debug库不要debug的Qt配release的gRPC。vcpkg默认安装的是release库你在调试版本项目里链接的时候最好用vcpkg install grpc:x64-windows之后把CMake构建类型也设为Release省得模型不一致的麻烦。9. 从练手到实战还能怎么扩展这个例子跑通之后不要急着觉得自己已经会了它只是把Unary模式最基本的链路打通了。真实项目里你会遇到更多场景。第一个是服务端地址配置化。现在地址是硬编码在MainWindow里的实战中一般会把地址放在配置文件里甚至做成界面可动态修改。当服务端重启、IP换了客户端不需要重新编译就能切换。第二个是请求超时和重试机制。gRPC的默认行为是不设置超时的话可能等待很久。你可以在ClientContext上设置deadline比如context-set_deadline(std::chrono::system_clock::now() std::chrono::seconds(5))超时后回调会收到DEADLINE_EXCEEDED状态你可以捕获这个状态提示用户重新操作。第三个是流式通信。如果页面要持续更新数据比如画一个实时曲线那Unary模式每次拉一次全量数据效率太低这时候就要用gRPC的服务端流或者双向流。热词里有人提到qt怎么绘制三维曲线、qt绘图效率、qt曲线刷新放另一个线程如果数据源换成gRPC流式接口那个刷新问题才真正有解。第四个是线程池。当前例子里只有一个CalcClient如果界面有多个并发请求建议用独立的CompletionQueue配合几张线程把异步处理能力提升上去。官方C示例里有一个async client的写法涉及ThreadPool和NextCallback的配合想深入可以去看那个。我个人在实际操作中的体会是gRPC和Qt的组合并没有想象中那么可怕难点几乎都集中在异步线程模型和工程配置这两块。把proto的设计、Client的封装、线程切换这三件事做好后面加业务逻辑就是在重复同一套路。最后再分享一个小技巧当请求失败时不要只把error_message打出来最好把status.error_code也打上也就是StatusCode枚举值。因为很多时候error_message是空的但error_code已经告诉你是连接失败还是超时还是参数错误。如果我在第一次排查时就养成这个习惯很多问题能少花一半时间。你现在可以动手把proto文件改成你自己的接口定义加一个乘法、字符串拼接或者文件上传的服务照着这个例子扩展大概率能直接跑通。等你把这个流程走到熟悉下一阶段再去看流式接口、SSL加密、拦截器会发现一切水到渠成。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号