Doc: Review QtGRPC chat example

As part of reviewing the application examples and following a structure, this
change covers:
-Language changes
-Follows the strucutre
-Includes \sa link to All Qt Examples

Fixes: QTBUG-137964
Pick-to: 6.10 6.9 6.8
Change-Id: Ic14d7042e6db277c1584b69ba46f7748f53642ab
Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
This commit is contained in:
Jaishree Vyas 2025-08-19 17:49:25 +02:00
parent b2d9291983
commit 49c0107975
1 changed files with 56 additions and 53 deletions

View File

@ -15,21 +15,40 @@
the \e ChatRoom, such as text messages, images, user activity or any other the \e ChatRoom, such as text messages, images, user activity or any other
files from their disk with all other participants. files from their disk with all other participants.
\inlineimage chat_room.webp \inlineimage {chat_room.webp} {Mobile chat window}
\inlineimage chat_login.webp \inlineimage {chat_login.webp} {Login window}
Some key topics covered in this example are: \section1 Running the example
\list \list
\li Communication through long-lived \l{QGrpcBidiStream}s. \li Ensure that the \c{qtgrpc_chat_server} is running and successfully
listening.
\li If you are on the same machine as the server, the default
\c{localhost} address should suffice when running the
\c{qtgrpc_chat_client}. If you are using a device other than the
one hosting the server, specify the correct IP address of the host
running the server in the Settings dialog.
\li Ensure that the \c{GRPC_CHAT_USE_EMOJI_FONT} CMake option is
enabled on the client to build with a smooth emoji experience 🚀.
\endlist
\image {chat_settings.webp} {Connection settings window img}
To run the example from \l{\QC Documentation}{Qt Creator}, open the
\uicontrol Welcome mode and select the example from \uicontrol Examples.
For more information, see \l{\QC: Tutorial: Build and run}.
\section1 Relevant modules and classes.
This example introduces the following Qt modules and classes.
\list
\li Communication through long-lived \l{QGrpcBidiStream}.
\li Using the QtGrpc client from a worker \l{QThread}{thread}. \li Using the QtGrpc client from a worker \l{QThread}{thread}.
\li Using the QtProtobufQtCoreTypes module in the protobuf schema. \li Using the QtProtobufQtCoreTypes module in the protobuf schema.
\li Secure communication through \l{Secure Sockets Layer (SSL) Classes}{SSL}. \li Secure communication through \l{Secure Sockets Layer (SSL) Classes}{SSL}.
\li Visualizing QtProtobuf messages in a QML ListView. \li Visualizing QtProtobuf messages in a QML ListView.
\endlist \endlist
\note Make sure to read the prerequisites in \l{Running the Example}.
\section1 Protobuf Schema \section1 Protobuf Schema
The Protobuf schema defines the structure of messages and services used in The Protobuf schema defines the structure of messages and services used in
@ -56,7 +75,7 @@
through the \c{ChatRoom} streaming RPC. Every \c{ChatMessage} must include through the \c{ChatRoom} streaming RPC. Every \c{ChatMessage} must include
a \c{username} and \c{timestamp} to identify the sender. a \c{username} and \c{timestamp} to identify the sender.
We include the \c{QtCore/QtCore.proto} import to enable the types of the You include the \c{QtCore/QtCore.proto} import to enable the types of the
QtProtobufQtCoreTypes module, allowing seamless conversion between QtProtobufQtCoreTypes module, allowing seamless conversion between
QtCore-specific types and their Protobuf equivalents. QtCore-specific types and their Protobuf equivalents.
@ -81,12 +100,12 @@
\snippet chat/server/main.cpp server-1 \snippet chat/server/main.cpp server-1
We declare the \c{QtGrpcChatService} class, which subclasses the You declare the \c{QtGrpcChatService} class, which subclasses the
\c{CallbackService} of the generated \c{QtGrpcChat} service. \c{CallbackService} of the generated \c{QtGrpcChat} service.
\snippet chat/server/main.cpp server-2 \snippet chat/server/main.cpp server-2
We override the virtual functions to implement the functionality for the Also override the virtual functions to implement the functionality for the
two \gRPC methods provided by the service: two \gRPC methods provided by the service:
\list \list
@ -107,9 +126,9 @@
\snippet chat/server/main.cpp server-4 \snippet chat/server/main.cpp server-4
The \c{startSharedWrite} method is a member function of the The \c{startSharedWrite} method is a member function of the
\c{ChatRoomReactor}. If the reactor (i.e. the client) is currently writing, \c{ChatRoomReactor}. If the reactor is currently writing, the message is
the message is buffered in a queue. Otherwise, a write operation is buffered in a queue. Otherwise, a write operation is initiated. There is a
initiated. There is a single and unique message shared between all clients. single and unique message shared between all clients.
Each copy of the \c{response} message increases the \c{use_count}. Once all Each copy of the \c{response} message increases the \c{use_count}. Once all
clients have finished writing the message, and its \c{use_count} drops to 0 its clients have finished writing the message, and its \c{use_count} drops to 0 its
resources are freed. resources are freed.
@ -141,15 +160,15 @@
\snippet chat/client/CMakeLists.txt client-setup-1 \snippet chat/client/CMakeLists.txt client-setup-1
First, we generate the source files from the Protobuf schema. Since the First, generate the source files from the Protobuf schema. Since the
\c{qtgrpcchat.proto} file does not contain any \c{message} definitions, \c{qtgrpcchat.proto} file does not contain any \c{message} definitions,
only \l{qt_add_grpc}{qtgrpcgen} generation is required. We also provide the only \l{qt_add_grpc}{qtgrpcgen} generation is required. Also provide the
\c{PROTO_INCLUDES} of the \c{ProtobufQtCoreTypes} module to ensure the \c{PROTO_INCLUDES} of the \c{ProtobufQtCoreTypes} module to ensure the
\c{"QtCore/QtCore.proto"} import is valid. \c{"QtCore/QtCore.proto"} import is valid.
\snippet chat/client/CMakeLists.txt client-setup-2 \snippet chat/client/CMakeLists.txt client-setup-2
We ensure that the independent \c{qtgrpc_chat_client_proto} target is Ensure that the independent \c{qtgrpc_chat_client_proto} target is
publicly linked against its dependencies, including the publicly linked against its dependencies, including the
\c{ProtobufQtCoreTypes} module. The application target is then linked \c{ProtobufQtCoreTypes} module. The application target is then linked
against this library. against this library.
@ -185,7 +204,7 @@
\snippet chat/client/chatengine.cpp client-3 \snippet chat/client/chatengine.cpp client-3
\dots 0 \dots 0
In the \c{ChatEngine} constructor, we assign the \c{ClientWorker} to its In the \c{ChatEngine} constructor, assign the \c{ClientWorker} to its
dedicated worker thread and continue handling and forwarding its signals to dedicated worker thread and continue handling and forwarding its signals to
make them available on the QML side. make them available on the QML side.
@ -198,12 +217,12 @@
own thread, it is important to use \l{QMetaObject::}{invokeMethod} to call own thread, it is important to use \l{QMetaObject::}{invokeMethod} to call
its member functions safely. its member functions safely.
In the \c{ClientWorker}, we check whether the client is uninitialized or if In the \c{ClientWorker}, you check whether the client is uninitialized or if
the host URI has changed. If either condition is met, we call the host URI has changed. If either condition is met, call
\c{initializeClient}, which creates a new QGrpcHttp2Channel. Since this \c{initializeClient}, which creates a new QGrpcHttp2Channel. Since this
is an expensive operation, we minimize its occurrences. is an expensive operation, minimize its occurrences.
To handle the \c{Register} RPC, we use the To handle the \c{Register} RPC, use the
\l{QGrpcCallOptions::}{setDeadlineTimeout} option to guard against server \l{QGrpcCallOptions::}{setDeadlineTimeout} option to guard against server
inactivity. It is generally recommended to set a deadline for unary RPCs. inactivity. It is generally recommended to set a deadline for unary RPCs.
@ -211,7 +230,7 @@
\dots \dots
\snippet chat/client/clientworker.cpp client-5b \snippet chat/client/clientworker.cpp client-5b
When logging into the \c{ChatRoom}, we use the When logging into the \c{ChatRoom},you can use the
\l{QGrpcCallOptions::}{setMetadata} option to provide user credentials, as \l{QGrpcCallOptions::}{setMetadata} option to provide user credentials, as
required by the server for authentication. The actual call and connection required by the server for authentication. The actual call and connection
setup are handled in the \c{connectStream} method. setup are handled in the \c{connectStream} method.
@ -223,8 +242,8 @@
\snippet chat/client/clientworker.cpp client-6c \snippet chat/client/clientworker.cpp client-6c
\dots \dots
We implement basic reconnection logic in case the stream finishes abruptly To implement basic reconnection logic in case the stream finishes abruptly
while we are still connected. This is done by simply calling while you are still connected. This is done by simply calling
\c{connectStream} again with the \c{QGrpcCallOptions} from the initial \c{connectStream} again with the \c{QGrpcCallOptions} from the initial
call. This ensures that all required connections are also updated. call. This ensures that all required connections are also updated.
@ -233,7 +252,7 @@
mode can be triggered, e.g., by using the FileDialog or switching to mode can be triggered, e.g., by using the FileDialog or switching to
another app. This mode shuts down network access, closing all active another app. This mode shuts down network access, closing all active
QTcpSocket connections and causing the stream to be QTcpSocket connections and causing the stream to be
\l{QGrpcBidiStream::}{finished}. We address this issue with the \l{QGrpcBidiStream::}{finished}. You can address this issue with the
reconnection logic. reconnection logic.
\snippet chat/client/clientworker.cpp client-6d \snippet chat/client/clientworker.cpp client-6d
@ -244,7 +263,7 @@
When messages are received, the \c{ClientWorker} performs some When messages are received, the \c{ClientWorker} performs some
pre-processing, such as saving the \c{FileMessage} content, so that the pre-processing, such as saving the \c{FileMessage} content, so that the
\c{ChatEngine} only needs to focus on the models. We use the \c{ContentFields} \c{ChatEngine} only needs to focus on the models. Use the \c{ContentFields}
enum to safely check the \c{oneof content} field of our ChatMessage sum type. enum to safely check the \c{oneof content} field of our ChatMessage sum type.
\snippet chat/client/chatengine.cpp client-7a \snippet chat/client/chatengine.cpp client-7a
@ -274,7 +293,7 @@
\snippet chat/client/Main.qml client-qml-2 \snippet chat/client/Main.qml client-qml-2
In \c{Main.qml}, we handle core signals emitted by the \c{ChatEngine}. Most \c{Main.qml} handles core signals emitted by the \c{ChatEngine}. Most
of these signals are handled globally and are visualized in any state of of these signals are handled globally and are visualized in any state of
the application. the application.
@ -304,11 +323,11 @@
\snippet chat/client/ChatView.qml client-qml-4e \snippet chat/client/ChatView.qml client-qml-4e
In \c{ChatView.qml}, the ListView displays messages in the \c{ChatRoom}. In \c{ChatView.qml}, the ListView displays messages in the \c{ChatRoom}.
This is slightly more complex, as we need to handle the \c{ChatMessage} sum This is slightly more complex, as you need to handle the \c{ChatMessage} sum
type conditionally. type conditionally.
To handle this, we use a DelegateChooser, which allows us to select the You can use a DelegateChooser, which allows us to select the appropriate
appropriate delegate based on the type of message. We use the default delegate based on the type of message. Use the default
\c{whatThis} role in the model, which provides the message type for each \c{whatThis} role in the model, which provides the message type for each
\c{ChatMessage} instance. The \c{DelegateBase} component then accesses the \c{ChatMessage} instance. The \c{DelegateBase} component then accesses the
\c{display} role of the model, making the chatMessage data available for \c{display} role of the model, making the chatMessage data available for
@ -356,14 +375,14 @@
pre-installed in the client's trust store like those of public CAs. pre-installed in the client's trust store like those of public CAs.
\endlist \endlist
We used \l{https://www.openssl.org/}{OpenSSL} to create these files and set Use \l{https://www.openssl.org/}{OpenSSL} to create these files and set
up our \gRPC communication to use SSL/TLS. up our \gRPC communication to use SSL/TLS.
\snippet chat/server/main.cpp server-ssl \snippet chat/server/main.cpp server-ssl
We provide the \e{Private Key} and \e{Certificate} to the \gRPC server. You provide the \e{Private Key} and \e{Certificate} to the \gRPC server.
With that, we construct the \c{SslServerCredentials} to enable TLS on the With that, you can construct the \c{SslServerCredentials} to enable TLS on the
server-side. In addition to secure communication, we also allow unencrypted server-side. In addition to secure communication, also allow unencrypted
access. access.
The server listens on the following addresses: The server listens on the following addresses:
@ -377,29 +396,13 @@
\snippet chat/client/clientworker.cpp client-ssl \snippet chat/client/clientworker.cpp client-ssl
The client loads the \e{Root CA Certificate}, as we self-signed the CA. The client loads the \e{Root CA Certificate}, as you self-signed the CA.
This certificate is used to create the QSslCertificate. It is important to This certificate is used to create the QSslCertificate. It is important to
provide the \c{"h2"} protocol with provide the \c{"h2"} protocol with
\l{QSslConfiguration::}{setAllowedNextProtocols}, as we are using HTTP/2. \l{QSslConfiguration::}{setAllowedNextProtocols}, as you are using HTTP/2.
\section1 Running the example \section1 Source files
\list \sa {All Qt Examples}
\li Ensure that the \c{qtgrpc_chat_server} is running and successfully
listening.
\li If you are on the same machine as the server, the default
\c{localhost} address should suffice when running the
\c{qtgrpc_chat_client}. If you are using a device other than the
one hosting the server, specify the correct IP address of the host
running the server in the Settings dialog.
\li Ensure that the \c{GRPC_CHAT_USE_EMOJI_FONT} CMake option is
enabled on the client to build with a smooth emoji experience 🚀.
\endlist
\image chat_settings.webp
To run the example from \l{\QC Documentation}{Qt Creator}, open the
\uicontrol Welcome mode and select the example from \uicontrol Examples.
For more information, see \l{\QC: Tutorial: Build and run}.
*/ */