MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
Qtの基礎 - ネットワークのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
Qtの基礎 - ネットワーク
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == <br><br> == HTTPリクエスト / HTTPレスポンス (非同期) == ==== 一定のタイミングで取得 ==== # ネットワークアクセスマネージャの設定 #: <code>QNetworkAccessManager</code>クラスを使用して、HTTPリクエストを送信して、HTTPレスポンスを受信する。 #: <code>QNetworkAccessManager</code>クラスは非同期で動作し、<code>QNetworkReply</code>クラスのオブジェクトを通じて結果を返す。 # タイマの設定 #: <code>QTimer</code>クラスを使用して、指定した間隔ごとにスロット(関数)を実行する。 #: このスロット内でHTTPリクエストを発行する。 # マルチスレッドの実装 #: Qtのマルチスレッド処理の方法はいくつか存在する。 #: しかし、<code>QNetworkAccessManager</code>クラス自身は非同期処理を行うため、マルチスレッドを明示的に扱う必要はない。 #: もし、ダウンロード処理のみを別のスレッドで行う場合は、<code>QThread</code>クラスまたは<code>QtConcurrent</code>クラスを使用して、その中で<code>QNetworkAccessManager</code>クラスのオブジェクトを作成および使用する。 <syntaxhighlight lang="c++"> #include <QCoreApplication> #include <QTimer> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QObject> class Downloader : public QObject { Q_OBJECT private: QNetworkAccessManager m_Manager; QTimer m_Timer; public: Downloader() { connect(&m_Timer, &QTimer::timeout, this, &Downloader::startDownload); connect(&m_Manager, &QNetworkAccessManager::finished, this, &Downloader::downloadFinished); m_Timer.start(60000); // 1分ごと } private slots: void startDownload() { QUrl url("https://www.google.com"); QNetworkRequest request(url); m_Manager.get(request); } void downloadFinished(QNetworkReply *reply) { if (reply->error()) { qDebug() << "Download error:" << reply->errorString(); return; } QString data = reply->readAll(); qDebug() << data; } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Downloader downloader; return a.exec(); } </syntaxhighlight> <br> 以下の例では、ダウンロード処理を実行するために専用のスレッドを作成して、メインスレッドはタイマを使用して定期的にこのダウンロード処理をトリガしている。<br> 1分ごとにDownloader::downloadメソッドを呼び出し、ダウンロードが完了するとダウンロードしたコンテンツがコンソールに表示される。<br> <br> <u>※注意</u><br> <u>実際の設計では、適切なエラーハンドリングとリソース管理 (例: QThread::quitとQThread::waitを実行してスレッドを適切に終了させる処理)が必要となる。</u><br> <syntaxhighlight lang="c++"> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QThread> #include <QTimer> #include <QUrl> #include <QObject> class Downloader : public QObject { Q_OBJECT private: QNetworkAccessManager m_Manager; public: Downloader(QObject *parent = nullptr) : QObject(parent) { connect(&m_Manager, &QNetworkAccessManager::finished, this, &Downloader::onDownloadFinished); } void download(const QUrl &url) { QNetworkRequest request(url); m_Manager.get(request); } signals: void downloadFinished(const QString &result); private slots: void onDownloadFinished(QNetworkReply *reply) { if (reply->error()) { std::cerr << QString("Download error : %1").arg(reply->errorString()).toStdString() << std::endl; emit downloadFinished(reply->errorString()); } else { QString data = reply->readAll(); emit downloadFinished(data); } reply->deleteLater(); } }; int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QThread downloaderThread; Downloader downloader; downloader.moveToThread(&downloaderThread); downloaderThread.start(); QTimer timer; QObject::connect(&timer, &QTimer::timeout, [&downloader](){ downloader.download(QUrl("http://www.google.com")); }); QObject::connect(&downloader, &Downloader::downloadFinished, [](const QString &result){ std::cout << QString("Downloaded content : %1").arg(result).toStdString() << std::endl; }); // 1分ごとにダウンロード timer.start(60000); return app.exec(); } </syntaxhighlight> <br> ==== 非同期で取得 (タイムアウト設定あり) ==== 以下の例では、<code>QNetworkAccessManager</code>クラスを使用して、Webページのデータを非同期に取得して、<code>QTimer</code>クラスでタイムアウトを実装している。<br> <code>QTimer::timeout</code>シグナルを使用して、指定した時間が経過した後に接続を中断 (<code>reply->abort()</code>) することにより、タイムアウト処理を行っている。<br> <br> また、<code>QNetworkReply::finished</code>シグナルを使用して、リクエストの完了を検知して、結果を出力している。<br> <syntaxhighlight lang="c++"> #include <QNetworkAccessManager> #include <QNetworkReply> #include <QTimer> // QNetworkAccessManagerのインスタンスを作成 QNetworkAccessManager manager; // Webページを取得するためのリクエストを作成 QNetworkRequest request(QUrl("http://www.example.com")); // リクエストを送信し、応答を受け取るためのQNetworkReplyオブジェクトを取得 QNetworkReply *reply = manager.get(request); // タイマオブジェクトを生成して、3秒後にタイムアウトするように設定 QTimer timer; timer.setSingleShot(true); timer.start(3000); // 3秒後にタイムアウト // タイマがタイムアウトした場合の処理を記述 QObject::connect(&timer, &QTimer::timeout, [&]() { if (reply->isRunning()) { // リクエストがまだ完了していない場合、接続を中断 reply->abort(); std::cerr << QString("タイムアウトしました").toStdString() << std::endl; } }); // リクエストが完了した場合の処理を接続 QObject::connect(reply, &QNetworkReply::finished, [&]() { if (reply->error() == QNetworkReply::NoError) { // リクエストが成功して、エラーがない場合は成功 QByteArray responseData = reply->readAll(); std::cout << QString("受信データ : %1").arg(responseData).toStdString() << std::endl; } else if (reply->error() == QNetworkReply::OperationCanceledError) { std::cerr << QString("リクエストがキャンセルされました").toStdString() << std::endl; } else { std::cerr << QString("エラー : %1").arg(reply->errorString()).toStdString() << std::endl; } reply->deleteLater(); }); </syntaxhighlight> <br> ==== 非同期で取得 (マルチスレッド) ==== 以下の例では、マルチスレッドで2つの異なるWebサイト (https://www.example1.com および https://www.example2.com) とTCP/IP通信を行っている。<br> <br> <code>QtConcurrent::run</code>メソッドを使用して、2つの異なるWebサイトとの通信を並行して行い、<br> 接続を確立した後、各サーバにHTTP GETリクエストを送信する。<br> <br> また、このサンプルコードでは、以下に示すような仕様がある。<br> * SSL証明書の検証 *: QSslSocket::VerifyPeerモードを設定して、ピア証明書の検証を有効にする。 *: onEncryptedスロットにより、ピア証明書の有効性を追加で確認している。 *: 接続を確立した場合、ピア証明書の有効性を確認する。 * ネットワークエラーとタイムアウトの処理 *: 接続タイムアウト (10秒) を設定している。 *: onErrorスロットでエラーをログに記録して、接続を切断する。 * バッファリングの実装 *: 受信したデータをバッファリングしている。 *: onReadyReadスロットにより、バッファからデータを行単位で処理する。 *: 完全なレスポンスを受信した時に接続を切断する。 <syntaxhighlight lang="c++"> // NetworkManager.hファイル #include <QCoreApplication> #include <QSslSocket> #include <QSslCertificate> #include <QtConcurrent> #include <QFuture> #include <QTimer> #include <QDebug> class NetworkManager : public QObject { Q_OBJECT private: QByteArray m_buffer; public: explicit NetworkManager(QObject *parent = nullptr) : QObject(parent) {} void connectToHostEncrypted(const QString &hostName, quint16 port) { QSslSocket *socket = new QSslSocket(this); connect(socket, &QSslSocket::encrypted, this, &NetworkManager::onEncrypted); connect(socket, &QSslSocket::disconnected, this, &NetworkManager::onDisconnected); connect(socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::errorOccurred), this, &NetworkManager::onError); connect(socket, &QSslSocket::readyRead, this, &NetworkManager::onReadyRead); connect(socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &NetworkManager::onSslErrors); // Set up SSL configuration QSslConfiguration sslConfig = socket->sslConfiguration(); sslConfig.setPeerVerifyMode(QSslSocket::VerifyPeer); socket->setSslConfiguration(sslConfig); // Set up timeout QTimer *timer = new QTimer(this); timer->setSingleShot(true); connect(timer, &QTimer::timeout, this, [=]() { if (socket->state() != QAbstractSocket::ConnectedState) { qDebug() << "Connection timeout for" << hostName; socket->abort(); } }); timer->start(10000); // 10 seconds timeout socket->connectToHostEncrypted(hostName, port); } private slots: void onEncrypted() { QSslSocket *socket = qobject_cast<QSslSocket*>(sender()); qDebug() << "Encrypted connection established with:" << socket->peerName(); // Verify certificate QSslCertificate peerCert = socket->peerCertificate(); if (peerCert.isNull()) { qDebug() << "Invalid peer certificate for" << socket->peerName(); socket->disconnectFromHost(); return; } // Send HTTP GET request QString request = "GET / HTTP/1.1\r\nHost: " + socket->peerName() + "\r\n\r\n"; socket->write(request.toUtf8()); } void onDisconnected() { QSslSocket *socket = qobject_cast<QSslSocket*>(sender()); qDebug() << "Disconnected from:" << socket->peerName(); socket->deleteLater(); } void onError(QAbstractSocket::SocketError socketError) { QSslSocket *socket = qobject_cast<QSslSocket*>(sender()); qDebug() << "Error occurred for" << socket->peerName() << ":" << socket->errorString(); socket->disconnectFromHost(); } void onReadyRead() { QSslSocket *socket = qobject_cast<QSslSocket*>(sender()); m_buffer.append(socket->readAll()); // Process the buffer while (m_buffer.size() > 0) { // Find the end of a line int endOfLine = m_buffer.indexOf("\r\n"); if (endOfLine == -1) { // We don't have a complete line yet break; } // Extract and process the line QByteArray line = m_buffer.left(endOfLine); m_buffer = m_buffer.mid(endOfLine + 2); qDebug() << "Received data from" << socket->peerName() << ":" << line; } // If we've received the entire response, disconnect if (m_buffer.contains("\r\n\r\n")) { socket->disconnectFromHost(); } } void onSslErrors(const QList<QSslError> &errors) { QSslSocket *socket = qobject_cast<QSslSocket*>(sender()); qDebug() << "SSL Errors occurred for" << socket->peerName() << ":"; for (const QSslError &error : errors) { qDebug() << error.errorString(); } // In a production environment, you might want to handle specific errors differently // For demonstration purposes, we're disconnecting on any SSL error socket->disconnectFromHost(); } }; </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // main.cppファイル #include "NetworkManager.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); NetworkManager manager; QFuture<void> future1 = QtConcurrent::run([&manager]() { manager.connectToHostEncrypted("www.example1.com", 443); }); QFuture<void> future2 = QtConcurrent::run([&manager]() { manager.connectToHostEncrypted("www.example2.com", 443); }); future1.waitForFinished(); future2.waitForFinished(); return a.exec(); } </syntaxhighlight> <br><br> == HTTPリクエスト / HTTPレスポンス (同期的) == ==== 同期的に取得 1 ==== <code>QNetworkAccessManager</code>クラスを使用してGETリクエストを送信した後、レスポンスを同期的に待機する場合、<br> <code>QEventLoop</code>クラスを使用してレスポンスが完了するまでメインスレッドをブロックする。<br> HTTPレスポンスを受信した後、エラーチェックを行い、必要に応じてレスポンスを処理する。<br> <br> <u>ただし、同期的なHTTPリクエストはUIスレッドでブロックするため、長時間の処理や大量のリクエストの場合は非推奨である。</u><br> 代わりに、非同期的なHTTPリクエストを使用することが推奨される。<br> <syntaxhighlight lang="c++"> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> QNetworkAccessManager manager; // 同期的なGETリクエスト QNetworkReply *reply = manager.get(QNetworkRequest(QUrl("https://www.google.com"))); // レスポンス待機 QEventLoop loop; QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); if (reply->error() == QNetworkReply::NoError) { qDebug() << "Success!"; qDebug() << "Response:" << reply->readAll(); } else { qDebug() << "Error:" << reply->errorString(); } reply->deleteLater(); </syntaxhighlight> <br> ==== 同期的に取得 2 ==== 非公式な方法ではあるが、<code>QNetworkRequest</code>クラスの<code>SynchronousRequestAttribute</code>アトリビューションを有効にすることで実現することもできる。<br> <syntaxhighlight lang="c++"> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> QNetworkAccessManager manager; QNetworkRequest request(QUrl(QStringLiteral("https://www.google.com"))); request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, true); // 同期的なGETリクエスト QNetworkReply *reply = manager.get(request); if (reply->error() == QNetworkReply::NoError) { qDebug() << "Success!"; qDebug() << "Response:" << reply->readAll(); } else { qDebug() << "Error:" << reply->errorString(); } reply->deleteLater(); </syntaxhighlight> <br> ==== 同期的に取得 (タイムアウト設定あり) ==== 以下の例では、<code>QNetworkAccessManager</code>クラスを使用してHTTPリクエストを送信して、レスポンスを待機している。<br> <code>QEventLoop</code>と<code>QTimer</code>を使用して、レスポンスを受信する、または、3秒経過するまで待機する。<br> 3秒経過してもレスポンスを受信しない場合は、タイムアウトとして処理する。<br> <br> この方法により、非同期APIを使用しながらも同期的な振る舞いを実現することができる。<br> <syntaxhighlight lang="c++"> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QTimer> #include <QEventLoop> QNetworkAccessManager manager; QNetworkRequest request(QUrl("http://www.example.com")); QNetworkReply *reply = manager.get(request); // イベントループを作成 QEventLoop loop; // タイマオブジェクトを生成して、3秒後にタイムアウトするように設定 QTimer timer; timer.setSingleShot(true); // タイマのタイムアウトシグナルとイベントループのquitメソッドを接続 QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); // レスポンスが完了したらイベントループを終了するように接続 QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); // タイマを開始 (3秒後にタイムアウト) timer.start(3000); // イベントループを開始 (リクエストが完了する、または、タイムアウトするまで待機) loop.exec(); if (timer.isActive()) { // タイマがアクティブな場合、リクエストが成功しているとする timer.stop(); // タイマを停止 if (reply->error() == QNetworkReply::NoError) { QByteArray responseData = reply->readAll(); std::cout << QString("Response received : %1").arg(responseData).toStdString() << std::endl; } else { std::cerr << QString("エラー : %1").arg(reply->errorString()).toStdString() << std::endl; } } else { // タイマが非アクティブな場合、タイムアウトが発生しているものとする std::cerr << QString("エラー : タイムアウト").toStdString() << std::endl; } reply->deleteLater(); </syntaxhighlight> <br><br> == POST == ==== URLエンコード ==== POSTリクエストにてデータを送信する場合、データ内容に<code>&</code>、<code>#</code>、<code>+</code>、半角スペース等が存在する可能性があるため、URLエンコードする必要がある。<br> <syntaxhighlight lang="c++"> QString message = "& + #"; QByteArray encoded = QUrl::toPercentEncoding(message.toUtf8()); </syntaxhighlight> <br> また、Content-Typeヘッダには<code>application/x-www-form-urlencoded</code>を指定する必要がある。<br> <syntaxhighlight lang="c++"> // ContentTypeHeaderをHTTPリクエストに設定 QNetworkRequest request(QUrl("https://www.example.com")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); request.setHeader(QNetworkRequest::ContentLengthHeader, QString::number(encoded.length())); </syntaxhighlight> <br><br> __FORCETOC__ [[カテゴリ:Qt]]
Qtの基礎 - ネットワーク
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse