Qt GRPC 服务客户端方法
gRPC 允许您定义四种类型的服务方法
- 一元调用,客户端向服务器发送单个请求并收到单个响应
rpc PingPong (Ping) returns (Pong);
- 服务器流,客户端向服务器发送单个请求并收到一个或多个响应
rpc PingSeveralPong (Ping) returns (stream Pong);
- 客户端流,客户端向服务器发送一个或多个请求并收到单个响应
rpc SeveralPingPong (stream Ping) returns (Pong);
- 双向流,客户端向服务器发送一个或多个请求并收到一个或多个响应
rpc SeveralPingSeveralPong (stream Ping) returns (stream Pong);
请注意,响应的数量可能不与请求的数量一致,也不与请求和响应的顺序一致。这由应用程序的业务逻辑控制。
gRPC 通信始终从客户端开始,在服务器端结束。客户端通过向服务器发送第一条消息来启动通信。服务器通过回复状态码来结束任何类型的通信。
要使用 Qt GRPC C++ API,首先为您的项目定义 pingpong.proto
模式
syntax = "proto3"; package ping.pong; message Ping { uint64 time = 1; sint32 num = 2; } message Pong { uint64 time = 1; sint32 num = 2; } service PingPongService { // Unary call rpc PingPong (Ping) returns (Pong); // Server stream rpc PingSeveralPong (Ping) returns (stream Pong); // Client stream rpc SeveralPingPong (stream Ping) returns (Pong); // Bidirectional stream rpc SeveralPingSeveralPong (stream Ping) returns (stream Pong); }
使用上述模式分割以及 Qt GRPC CMake API 生成 C++ 客户端代码
find_package(Qt6 COMPONENTS Protobuf Grpc) qt_add_executable(pingpong ...) qt_add_protobuf(pingpong PROTO_FILES pingpong.proto) qt_add_grpc(pingpong CLIENT PROTO_FILES pingpong.proto)
生成的 protobuf 消息和客户端 gRPC 代码都将添加到 pingpong
CMake 目标中。
使用 Qt GRPC 中的唯一调用
让我们从一个最简单的通信场景开始 - 一个一元的 gRPC 调用。在这种 RPC 类型中,客户端向服务器发送单个请求消息,并从服务器接收单个响应消息。在服务器发送状态码后,通信结束。
对于唯一调用,qtgrpcgen 工具会生成两种不同的异步方法
namespace ping::pong { namespace PingPongService { class Client : public QAbstractGrpcClient { Q_OBJECT public: std::shared_ptr<QGrpcCallReply> PingPong(const ping::pong::Ping &arg, const QGrpcCallOptions &options = {}); Q_INVOKABLE void PingPong(const ping::pong::Ping &arg, const QObject *context, const std::function<void(std::shared_ptr<QGrpcCallReply>)> &callback, const QGrpcCallOptions &options = {}); ... }; } // namespace PingPongService } // namespace ping::pong
使用 QGrpcCallReply 处理调用回复
第一个变体返回 QGrpcCallReply gRPC 操作。QGrpcCallReply 读取从服务器接收的消息,并获取有关错误或调用结束的通知。
在创建 PingPongService::Client
并将其附加到 QGrpcHttp2Channel 之后,调用 PingPong
方法
qint64 requestTime = QDateTime::currentMSecsSinceEpoch(); ping::pong::Ping request; request.setTime(requestTime); auto reply = cl.PingPong(request,{}); QObject::connect(reply.get(), &QGrpcCallReply::finished, reply.get(), [requestTime, replyPtr = reply.get()]() { auto response = replyPtr->read<ping::pong::Pong>(); qDebug() << "Ping-Pong time difference" << response.time() - requestTime; }); QObject::connect(reply.get(), &QGrpcCallReply::errorOccurred, stream.get() [](const QGrpcStatus &status) { qDebug() << "Error occurred: " << status.code() << status.message(); });
在服务器响应请求后,将发出 QGrpcCallReply::finished 信号。该 reply
对象包含从服务器接收的原始响应数据,可以使用 QGrpcCallReply::read 方法反序列化为 ping::pong::Pong
protobuf 消息。
如果服务器没有响应或请求在服务器端引起错误,将发出 QGrpcCallReply::errorOccurred 信号,并带有相应的 状态码。如果服务器用 QGrpcStatus::Ok 状态码回答,则不会发出 QGrpcCallReply::errorOccurred
信号。
使用回调处理呼叫应答
重载的函数与返回 QGrpcCallReply 的函数相似,但该函数不是返回应答,而是将应答作为参数传递给用于调用的回调函数
... cl.PingPong(request, &a, [requestTime](std::shared_ptr<QGrpcCallReply> reply) { auto response = reply->read<ping::pong::Pong>(); qDebug() << "Ping and Pong time difference" << response.time() - requestTime; });
此变体隐式连接到 QGrpcCallReply::finished 信号,但您不能使用 QGrpcOperation::cancel 函数取消调用,要获取关于发生错误的信息,您需要订阅通用的 QAbstractGrpcClient::errorOccurred 信号。
在 Qt GRPC 中使用服务器流
服务器流扩展了单一调用场景,并允许服务器多次对客户端请求进行响应。一旦服务器发送状态代码,通信就结束。
对于服务器流,qtgrpcgen 工具生成了返回 QGrpcServerStream 指针的方法
std::shared_ptr<QGrpcServerStream> streamPingSeveralPong(const ping::pong::Ping &arg, const QGrpcCallOptions &options = {});
QGrpcServerStream 与 QGrpcCallReply 类似,但服务器响应接收到时会发出 QGrpcServerStream::messageReceived
QObject::connect(stream.get(), &QGrpcServerStream::messageReceived, stream.get(), [streamPtr = stream.get(), requestTime]() { auto response = streamPtr->read<ping::pong::Pong>(); qDebug() << "Ping-Pong next response time difference" << response.time() - requestTime; }); QObject::connect(stream.get(), &QGrpcServerStream::errorOccurred, stream.get() [](const QGrpcStatus &status) { qDebug() << "Error occurred: " << status.code() << status.message(); }); QObject::connect(stream.get(), &QGrpcServerStream::finished, stream.get(), []{ qDebug() << "Bye"; });
注意:接收来自服务器的新的消息时,QGrpcServerStream 会覆盖内部缓冲区。在服务器 finished 通信后,您只能读取从服务器接收到的最后一条消息。
在 Qt GRPC 中使用客户端流
客户端流扩展了单一调用场景,并允许客户端发送多个请求。服务器在结束通信之前只响应一次。
对于客户端流,qtgrpcgen 工具生成了返回 QGrpcClientStream 指针的方法
std::shared_ptr<QGrpcClientStream> streamSeveralPingPong(const ping::pong::Ping &arg, const QGrpcCallOptions &options = {});
要向服务器发送多个请求,请使用 QGrpcClientStream::sendMessage 方法
auto stream = cl.streamSeveralPingPong(request); QTimer timer; QObject::connect(&timer, &QTimer::timeout, stream.get(), [streamPtr = stream.get()](){ ping::pong::Ping request; request.setTime(QDateTime::currentMSecsSinceEpoch()); streamPtr->sendMessage(request); }); QObject::connect(stream.get(), &QGrpcServerStream::finished, stream.get(), [streamPtr = stream.get(), &timer]{ auto response = streamPtr->read<ping::pong::Pong>(); qDebug() << "Slowest Ping time: " << response.time(); timer.stop(); }); QObject::connect(stream.get(), &QGrpcServerStream::errorOccurred, stream.get() [&timer](const QGrpcStatus &status){ qDebug() << "Error occurred: " << status.code() << status.message(); timer.stop(); }); timer.start(1000); return a.exec();
在客户端发送足够多的 Ping
请求后,服务器会以包含最慢 Ping
时间的 Pong
进行响应。
在 Qt GRPC 中使用双向流
双向流结合了服务器和客户端流的功能。生成的方法返回指向 QGrpcBidirStream 的指针,该 API 提供服务器和客户端流的接口
std::shared_ptr<QGrpcBidirStream> streamSeveralPingSeveralPong(const ping::pong::Ping &arg, const QGrpcCallOptions &options = {});
使用双向流组织双方的通信,而不中断连接会话
auto stream = cl.streamSeveralPingSeveralPong(request); qint64 maxPingPongTime = 0; QTimer timer; QObject::connect(&timer, &QTimer::timeout, stream.get(), [streamPtr = stream.get(), &requestTime](){ requestTime = QDateTime::currentMSecsSinceEpoch(); ping::pong::Ping request; request.setTime(requestTime); streamPtr->sendMessage(request); }); QObject::connect(stream.get(), &QGrpcBidirStream::messageReceived, stream.get(), [streamPtr = stream.get(), &timer, &maxPingPongTime, &requestTime]{ auto response = streamPtr->read<ping::pong::Pong>(); maxPingPongTime = std::max(maxPingPongTime, response.time() - requestTime); }); QObject::connect(stream.get(), &QGrpcBidirStream::finished, stream.get(), [streamPtr = stream.get(), &timer, &maxPingPongTime]{ qDebug() << "Maximum Ping-Pong time: " << maxPingPongTime; timer.stop(); }); QObject::connect(stream.get(), &QGrpcBidirStream::errorOccurred, stream.get(), [&timer](const QGrpcStatus &status){ qDebug() << "Error occurred: " << status.code() << status.message(); timer.stop(); }); timer.start(1000);
每次客户端发送 Ping
请求时,服务器都会以 Pong
消息进行响应。直到服务器向客户端发送状态代码结束通信,才会评估 Ping-Pong 时间最大值。
注意:QGrpcBidirStream 接收来自服务器的新的消息时会覆盖内部缓冲区。在服务器 finished 通信后,您只能读取从服务器接收到的最后一条消息。
© 2024 The Qt Company Ltd. 本内包含的文档贡献的版权归其各自的所有者所有。本提供的文档根据自由软件基金会的出版物《GNU 自由文档许可证》第 1.3 版的条款进行许可。Qt 以及相应的徽标是芬兰和/或其他国家/地区的 The Qt Company Ltd. 的商标。所有其他商标是其各自所有者的财产。