📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)
ページの作成:「== 概要 == <br><br> == HTTPリクエスト / HTTPレスポンス == ==== 同期的に取得 1 ==== <code>QNetworkAccessManager</code>クラスを使用してGETリクエストを送信した後、レスポンスを同期的に待機する場合、<br> <code>QEventLoop</code>クラスを使用してレスポンスが完了するまでメインスレッドをブロックする。<br> HTTPレスポンスを受信した後、エラーチェックを行い、必要に…」 |
|||
| (同じ利用者による、間の8版が非表示) | |||
| 2行目: | 2行目: | ||
<br><br> | <br><br> | ||
== HTTPリクエスト / HTTPレスポンス == | == 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 ==== | ==== 同期的に取得 1 ==== | ||
<code>QNetworkAccessManager</code>クラスを使用してGETリクエストを送信した後、レスポンスを同期的に待機する場合、<br> | <code>QNetworkAccessManager</code>クラスを使用してGETリクエストを送信した後、レスポンスを同期的に待機する場合、<br> | ||
| 59行目: | 417行目: | ||
reply->deleteLater(); | 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> | </syntaxhighlight> | ||
<br><br> | <br><br> | ||