チャット

ユーザーアプリケーションで Qt GRPC クライアント API を使用します。

Chat では、チャットユーザーを認証し、チャットクライアント間でショートメッセージを送受信する方法を説明します。このアプリケーションは以下のメッセージ形式をサポートしています:

  • テキスト - メッセージ入力フィールドを使用してメッセージを送信します。
  • 画像 - 画像バッファをクリップボードにコピーし、'Ctrl + V' ショートカットを使用してメッセージを送信します。

チャットクライアントはprotobufスキームで記述されたシンプルなRPCプロトコルを使用します:

package qtgrpc.examples.chat;

message ChatMessage {
    enum ContentType {
        Unknown = 0;
        Text = 1;
        Image = 2;
    };
    uint64 timestamp = 1;
    bytes content = 2;
    ContentType type = 3;
    string from = 4;
}

message ChatMessages {
    repeated ChatMessage messages = 1;
}

message User {
    string name = 1;
    string password = 2;
}

message Users {
    repeated User users = 1;
}

message None {}

service SimpleChat {
    rpc messageList(None) returns (stream ChatMessages) {}
    rpc sendMessage(ChatMessage) returns (None) {}
}

ログイン画面で、ユーザー認証情報を入力してください:

注: ユーザーのリストはサーバー側であらかじめ定義されており、一定です。すべてのユーザーのパスワードはqwerty です。

チャットクライアントは、user-nameuser-password HTTPヘッダーを使用してサーバーを認証します。これらのフィールドはメタデータ内に設定される。各gRPCメッセージはメッセージ・ヘッダにユーザ認証情報を含む。メタデータはQGrpcChannelOptions 内のQGrpcHttp2Channel に一度渡され、暗黙のうちに再利用される:

QGrpcChannelOptions channelOptions;
QHash<QByteArray, QByteArray> metadata = {
    { "user-name", { name.toUtf8() } },
    { "user-password", { password.toUtf8() } },
};
channelOptions.setMetadata(metadata);
std::shared_ptr<QAbstractGrpcChannel>
    channel = std::make_shared<QGrpcHttp2Channel>(url, channelOptions);

チャットクライアントは、gRPCサーバストリーミングへのサブスクリプションを使用してサーバとの通信を開始します:

auto stream = m_client->messageList(qtgrpc::examples::chat::None());
auto streamPtr = stream.get();
auto finishedConnection = std::make_shared<QMetaObject::Connection>();
*finishedConnection = QObject::connect(streamPtr, &QGrpcServerStream::finished, this,
                                       [this, finishedConnection,
                                        stream = std::move(stream)](const QGrpcStatus &status) {
                                           if (!status.isOk()) {
                                               qCritical() << "Stream error(" << status.code()
                                                           << "):" << status.message();
                                           }
                                           if (status.code()
                                               == QtGrpc::StatusCode::Unauthenticated) {
                                               emit authFailed();
                                           } else if (status.code() != QtGrpc::StatusCode::Ok) {
                                               emit networkError(status.message());
                                               setState(Disconnected);
                                           } else {
                                               setState(Disconnected);
                                           }
                                           disconnect(*finishedConnection);
                                       });

QObject::connect(streamPtr, &QGrpcServerStream::messageReceived, this,
                 [this, name, password, stream = streamPtr]() {
                     if (m_userName != name) {
                         m_userName = name;
                         m_password = password;
                         emit userNameChanged();
                     }
                     setState(Connected);
                     if (const auto msg = stream->read<qtgrpc::examples::chat::ChatMessages>())
                         m_messages.append(msg->messages());
                 });

QGrpcServerStream ハンドラは、クライアントアプリケーションが接続すべきシグナルを提供します。

QGrpcServerStream::finished シグナルを使用して、エラーとストリームの正常終了の両方を処理します。ステータスコードは、ストリーム通信中に発生したエラーを示します。

サーバーがストリームに新しいメッセージを送信すると、QGrpcServerStreamQGrpcServerStream::messageReceived シグナルを発信します。このシグナルに接続されたスロットは、チャット・メッセージを処理します。SimpleChat/messageList サーバーストリームから受信したメッセージは、カスタムQAbstractListModel モデルに収集され、ユーザーに表示されます。

QGrpcServerStream::finished シグナルが発信されると、このストリームインスタンスでできることはなくなりますので、新しいサブスクリプションを開始する必要があります。

サブスクリプションに成功すると、チャットクライアントは会話画面に切り替わり、ショートメッセージを見たり送信したりできるようになります:

メッセージを送信するには、単項 RPC 呼び出しSimpleChat/sendMessage を使用します。メッセージを送信するには、単項 RPC 呼び出しChatMessage を使用します。クライアント・アプリケーションは、まず protobuf メッセージのフィールドを設定し、次にクライアント・メソッドを呼び出します:

qtgrpc::examples::chat::ChatMessage msg;
msg.setContent(content.toUtf8());
msg.setType(qtgrpc::examples::chat::ChatMessage::ContentType::Text);
msg.setTimestamp(QDateTime::currentMSecsSinceEpoch());
msg.setFrom(m_userName);

std::unique_ptr<QGrpcCallReply> reply = m_client->sendMessage(msg);
// We explicitly take a copy of the reply pointer, since moving it into
// the lambda would make the get() function invalid.
// Reply's lifetime will be extended until finished() is emitted.
// Qt::SingleShotConnection is needed to destroy the lambda (and its capture).
auto *replyPtr = reply.get();
connect(
    replyPtr, &QGrpcCallReply::finished, this,
    [reply = std::move(reply)](const QGrpcStatus &status) {
        if (!status.isOk())
            qDebug() << "Failed to send message: " << status;
    },
    Qt::SingleShotConnection);

次に、gRPC サーバはクライアント・メッセージを処理し、SimpleChat/messageList ストリームを通じて、接続されているすべてのクライアントにブロードキャストします。

注意: この例では、サーバ実装にリファレンス gRPC C++ API を使用しています。

サンプルプロジェクト @ code.qt.io

©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 ここで提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。