mirror of https://github.com/qt/qtgrpc.git
259 lines
9.1 KiB
C++
259 lines
9.1 KiB
C++
// Copyright (C) 2024 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
|
|
|
//! [gen-includes]
|
|
#include "clientguide.qpb.h"
|
|
#include "clientguide_client.grpc.qpb.h"
|
|
//! [gen-includes]
|
|
|
|
#include <QtGrpc/QGrpcHttp2Channel>
|
|
#include <QtGrpc/qgrpcstream.h>
|
|
|
|
#include <QtCore/QCommandLineParser>
|
|
#include <QtCore/QCoreApplication>
|
|
#include <QtCore/QDateTime>
|
|
#include <QtCore/QProcess>
|
|
#include <QtCore/QThread>
|
|
#include <QtCore/QUrl>
|
|
|
|
#include <limits>
|
|
#include <memory>
|
|
|
|
// We use part of the namespace to clarify the source.
|
|
using namespace client;
|
|
|
|
void startServerProcess();
|
|
QDebug operator<<(QDebug debug, const guide::Response &response);
|
|
|
|
class ClientGuide : public QObject
|
|
{
|
|
public:
|
|
//! [basic-1]
|
|
explicit ClientGuide(std::shared_ptr<QAbstractGrpcChannel> channel)
|
|
{
|
|
m_client.attachChannel(std::move(channel));
|
|
}
|
|
//! [basic-1]
|
|
|
|
//! [basic-2]
|
|
static guide::Request createRequest(int32_t num, bool fail = false)
|
|
{
|
|
guide::Request request;
|
|
request.setNum(num);
|
|
// The server-side logic fails the RPC if the time is in the future.
|
|
request.setTime(fail ? std::numeric_limits<int64_t>::max()
|
|
: QDateTime::currentMSecsSinceEpoch());
|
|
return request;
|
|
}
|
|
//! [basic-2]
|
|
|
|
//! [unary-0]
|
|
void unaryCall(const guide::Request &request)
|
|
{
|
|
std::unique_ptr<QGrpcCallReply> reply = m_client.UnaryCall(request);
|
|
const auto *replyPtr = reply.get();
|
|
QObject::connect(
|
|
replyPtr, &QGrpcCallReply::finished, replyPtr,
|
|
[reply = std::move(reply)](const QGrpcStatus &status) {
|
|
if (status.isOk()) {
|
|
if (const auto response = reply->read<guide::Response>())
|
|
qDebug() << "Client (UnaryCall) finished, received:" << *response;
|
|
else
|
|
qDebug("Client (UnaryCall) deserialization failed");
|
|
} else {
|
|
qDebug() << "Client (UnaryCall) failed:" << status;
|
|
}
|
|
},
|
|
Qt::SingleShotConnection);
|
|
}
|
|
//! [unary-0]
|
|
|
|
//! [sstream-0]
|
|
void serverStreaming(const guide::Request &initialRequest)
|
|
{
|
|
std::unique_ptr<QGrpcServerStream> stream = m_client.ServerStreaming(initialRequest);
|
|
const auto *streamPtr = stream.get();
|
|
|
|
QObject::connect(
|
|
streamPtr, &QGrpcServerStream::finished, streamPtr,
|
|
[stream = std::move(stream)](const QGrpcStatus &status) {
|
|
if (status.isOk())
|
|
qDebug("Client (ServerStreaming) finished");
|
|
else
|
|
qDebug() << "Client (ServerStreaming) failed:" << status;
|
|
},
|
|
Qt::SingleShotConnection);
|
|
//! [sstream-0]
|
|
//! [sstream-1]
|
|
QObject::connect(streamPtr, &QGrpcServerStream::messageReceived, streamPtr, [streamPtr] {
|
|
if (const auto response = streamPtr->read<guide::Response>())
|
|
qDebug() << "Client (ServerStream) received:" << *response;
|
|
else
|
|
qDebug("Client (ServerStream) deserialization failed");
|
|
});
|
|
}
|
|
//! [sstream-1]
|
|
|
|
// ! [cstream-0]
|
|
void clientStreaming(const guide::Request &initialRequest)
|
|
{
|
|
m_clientStream = m_client.ClientStreaming(initialRequest);
|
|
for (int32_t i = 1; i < 3; ++i)
|
|
m_clientStream->writeMessage(createRequest(initialRequest.num() + i));
|
|
m_clientStream->writesDone();
|
|
|
|
QObject::connect(m_clientStream.get(), &QGrpcClientStream::finished, m_clientStream.get(),
|
|
[this](const QGrpcStatus &status) {
|
|
if (status.isOk()) {
|
|
if (const auto response = m_clientStream->read<guide::Response>())
|
|
qDebug() << "Client (ClientStreaming) finished, received:"
|
|
<< *response;
|
|
m_clientStream.reset();
|
|
} else {
|
|
qDebug() << "Client (ClientStreaming) failed:" << status;
|
|
qDebug("Restarting the client stream");
|
|
clientStreaming(createRequest(0));
|
|
}
|
|
});
|
|
}
|
|
// ! [cstream-0]
|
|
|
|
// ! [bstream-1]
|
|
void bidirectionalStreaming(const guide::Request &initialRequest)
|
|
{
|
|
m_bidiStream = m_client.BidirectionalStreaming(initialRequest);
|
|
connect(m_bidiStream.get(), &QGrpcBidiStream::finished, this, &ClientGuide::bidiFinished);
|
|
connect(m_bidiStream.get(), &QGrpcBidiStream::messageReceived, this,
|
|
&ClientGuide::bidiMessageReceived);
|
|
}
|
|
// ! [bstream-1]
|
|
|
|
private slots:
|
|
// ! [bstream-2]
|
|
void bidiFinished(const QGrpcStatus &status)
|
|
{
|
|
if (status.isOk())
|
|
qDebug("Client (BidirectionalStreaming) finished");
|
|
else
|
|
qDebug() << "Client (BidirectionalStreaming) failed:" << status;
|
|
m_bidiStream.reset();
|
|
}
|
|
// ! [bstream-2]
|
|
|
|
// ! [bstream-3]
|
|
void bidiMessageReceived()
|
|
{
|
|
if (m_bidiStream->read(&m_bidiResponse)) {
|
|
qDebug() << "Client (BidirectionalStreaming) received:" << m_bidiResponse;
|
|
if (m_bidiResponse.num() > 0) {
|
|
m_bidiStream->writeMessage(createRequest(m_bidiResponse.num() - 1));
|
|
return;
|
|
}
|
|
} else {
|
|
qDebug("Client (BidirectionalStreaming) deserialization failed");
|
|
}
|
|
m_bidiStream->writesDone();
|
|
}
|
|
// ! [bstream-3]
|
|
|
|
private:
|
|
guide::ClientGuideService::Client m_client;
|
|
std::unique_ptr<QGrpcClientStream> m_clientStream;
|
|
// ! [bstream-0]
|
|
std::unique_ptr<QGrpcBidiStream> m_bidiStream;
|
|
guide::Response m_bidiResponse;
|
|
// ! [bstream-0]
|
|
};
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
QCoreApplication app(argc, argv);
|
|
|
|
// Use the -U, -S, -C, -B options to control execution
|
|
QCommandLineParser parser;
|
|
QCommandLineOption enableUnary("U", "Enable UnaryCalls");
|
|
QCommandLineOption enableSStream("S", "Enable ServerStream");
|
|
QCommandLineOption enableCStream("C", "Enable ClientStream");
|
|
QCommandLineOption enableBStream("B", "Enable BiDiStream");
|
|
|
|
parser.addHelpOption();
|
|
parser.addOption(enableUnary);
|
|
parser.addOption(enableSStream);
|
|
parser.addOption(enableCStream);
|
|
parser.addOption(enableBStream);
|
|
parser.process(app);
|
|
|
|
bool defaultRun = !parser.isSet(enableUnary) && !parser.isSet(enableSStream)
|
|
&& !parser.isSet(enableCStream) && !parser.isSet(enableBStream);
|
|
|
|
qDebug("Welcome to the clientguide!");
|
|
qDebug("Starting the server process ...");
|
|
startServerProcess();
|
|
|
|
//! [basic-0]
|
|
auto channel = std::make_shared<QGrpcHttp2Channel>(
|
|
QUrl("http://localhost:50056")
|
|
/* without channel options. */
|
|
);
|
|
ClientGuide clientGuide(channel);
|
|
//! [basic-0]
|
|
|
|
if (defaultRun || parser.isSet(enableUnary)) {
|
|
//! [unary-1]
|
|
clientGuide.unaryCall(ClientGuide::createRequest(1));
|
|
clientGuide.unaryCall(ClientGuide::createRequest(2, true)); // fail the RPC
|
|
clientGuide.unaryCall(ClientGuide::createRequest(3));
|
|
//! [unary-1]
|
|
}
|
|
|
|
if (defaultRun || parser.isSet(enableSStream)) {
|
|
//! [sstream-2]
|
|
clientGuide.serverStreaming(ClientGuide::createRequest(3));
|
|
// ! [sstream-2]
|
|
}
|
|
|
|
if (defaultRun || parser.isSet(enableCStream)) {
|
|
// ! [cstream-1]
|
|
clientGuide.clientStreaming(ClientGuide::createRequest(0, true)); // fail the RPC
|
|
// ! [cstream-1]
|
|
}
|
|
|
|
if (defaultRun || parser.isSet(enableBStream)) {
|
|
// ! [bstream-4]
|
|
clientGuide.bidirectionalStreaming(ClientGuide::createRequest(3));
|
|
// ! [bstream-4]
|
|
}
|
|
|
|
return app.exec();
|
|
}
|
|
|
|
void startServerProcess()
|
|
{
|
|
// For the purpose of this example, we launch the server directly from the
|
|
// client. In a real-world scenario, the server should be running
|
|
// independently, and this code would not be necessary. This approach is
|
|
// used here solely for convenience in demonstrating the full interaction.
|
|
static QProcess serverProcess;
|
|
QObject::connect(&serverProcess, &QProcess::readyReadStandardOutput, [] {
|
|
auto msgs = serverProcess.readAll().split('\n');
|
|
msgs.removeIf([](const QByteArray &s) { return s.isEmpty(); });
|
|
for (const auto &m : std::as_const(msgs)) {
|
|
qDebug().noquote().nospace() << " " << m;
|
|
}
|
|
});
|
|
serverProcess.setProcessChannelMode(QProcess::MergedChannels);
|
|
serverProcess.setReadChannel(QProcess::StandardOutput);
|
|
serverProcess.start("./clientguide_server");
|
|
if (!serverProcess.waitForStarted()) {
|
|
qFatal() << "Couldn't start the server: " << serverProcess.errorString();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
// give the process some time to properly start up the server
|
|
QThread::currentThread()->msleep(250);
|
|
}
|
|
|
|
QDebug operator<<(QDebug debug, const guide::Response &response)
|
|
{
|
|
return debug << "Response( time: " << response.time() << ", num: " << response.num() << " )";
|
|
}
|