QtのQAbstractSocket::SocketError徹底解説!種類と原因、解決策
QAbstractSocket::SocketError (enum) の主な種類
-
QAbstractSocket::UnknownSocketError
(-1)- 意味
特定できないエラーが発生しました。 - 考えられる原因
上記のいずれにも分類されない、または詳細不明なエラー。通常はerrorString()
メソッドで詳細なエラーメッセージを取得することを試みます。
- 意味
-
QAbstractSocket::SslHandshakeFailedError
(13)- 意味
SSL/TLSハンドシェイクに失敗し、接続が閉じられました(QSslSocket
でのみ使用されます)。
- 意味
-
QAbstractSocket::ProxyAuthenticationRequiredError
(12)- 意味
ソケットがプロキシを使用しており、プロキシ認証が必要です。
- 意味
-
QAbstractSocket::UnsupportedSocketOperationError
(10)- 意味
要求されたソケット操作がローカルのオペレーティングシステムでサポートされていません(例: IPv6のサポート不足など)。
- 意味
-
QAbstractSocket::AddressInUseError
(8)- 意味
QUdpSocket::bind()
で指定されたアドレスが既に使用されており、排他的に設定されています。 - 考えられる原因
既に他のプロセスが同じポートを占有している、など。
- 意味
-
QAbstractSocket::NetworkError
(7)- 意味
ネットワークでエラーが発生しました(例: ネットワークケーブルが抜かれたなど)。 - 考えられる原因
物理的なネットワーク接続の問題、ネットワークアダプターの無効化など。
- 意味
-
QAbstractSocket::DatagramTooLargeError
(6)- 意味
データグラム(UDPパケット)がオペレーティングシステムの制限(最小で8192バイト)を超えました。 - 考えられる原因
UDPで大きすぎるデータを送信しようとした場合。
- 意味
-
QAbstractSocket::SocketTimeoutError
(5)- 意味
ソケット操作がタイムアウトしました。 - 考えられる原因
waitForConnected()
やwaitForReadyRead()
などの待機関数が指定された時間内に完了しなかった場合など。
- 意味
-
QAbstractSocket::SocketResourceError
(4)- 意味
ローカルシステムがリソース不足になりました(例: ソケットが多すぎるなど)。 - 考えられる原因
開いているソケットの数がシステムの制限を超えた、メモリが不足している、など。
- 意味
-
QAbstractSocket::SocketAccessError
(3)- 意味
アプリケーションに必要な権限がないため、ソケット操作に失敗しました。 - 考えられる原因
特定のポートを使用するための権限がない(例: 1024番未満のポートを使用しようとした場合など)。
- 意味
-
QAbstractSocket::HostNotFoundError
(2)- 意味
ホストアドレスが見つかりませんでした。 - 考えられる原因
指定されたホスト名(ドメイン名)が解決できない、IPアドレスが誤っている、など。
- 意味
-
QAbstractSocket::RemoteHostClosedError
(1)- 意味
リモートホスト(接続先のサーバーなど)が接続を閉じました。 - 考えられる原因
相手のサーバーが正常に切断した、またはクラッシュした、など。
- 意味
-
- 意味
接続が相手(ピア)によって拒否されたか、タイムアウトしました。 - 考えられる原因
相手のサーバーが起動していない、指定されたポートでリッスンしていない、ファイアウォールによって接続がブロックされている、など。
- 意味
QAbstractSocket
は、エラーが発生すると error(QAbstractSocket::SocketError socketError)
シグナルを発します。このシグナルをスロットに接続することで、エラーの種類を捕捉し、適切なエラーハンドリングを行うことができます。
例
#include <QTcpSocket>
#include <QDebug>
// ... どこかでソケットを作成し、エラーシグナルを接続
QTcpSocket *socket = new QTcpSocket(this);
connect(socket, &QTcpSocket::errorOccurred, this, &MyClass::handleSocketError); // Qt 5以降推奨
// または Qt 4 形式:
// connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(handleSocketError(QAbstractSocket::SocketError)));
// ...
void MyClass::handleSocketError(QAbstractSocket::SocketError socketError)
{
switch (socketError) {
case QAbstractSocket::ConnectionRefusedError:
qDebug() << "接続が拒否されました。";
break;
case QAbstractSocket::RemoteHostClosedError:
qDebug() << "リモートホストが接続を閉じました。";
break;
case QAbstractSocket::HostNotFoundError:
qDebug() << "ホストが見つかりません。";
break;
case QAbstractSocket::SocketTimeoutError:
qDebug() << "ソケット操作がタイムアウトしました。";
break;
case QAbstractSocket::NetworkError:
qDebug() << "ネットワークエラーが発生しました。";
break;
case QAbstractSocket::UnknownSocketError:
qDebug() << "不明なソケットエラー: " << socket->errorString();
break;
default:
qDebug() << "その他のソケットエラー: " << socketError << " - " << socket->errorString();
break;
}
}
よくあるエラーとトラブルシューティング
QAbstractSocket::ConnectionRefusedError (接続拒否エラー)
- トラブルシューティング
- サーバーの稼働状況を確認する
接続しようとしているサーバーが実際に起動しており、指定されたポートでリッスンしているかを確認します。サーバー側のログも確認しましょう。 - IPアドレスとポート番号の確認
クライアントとサーバーで指定しているIPアドレス(例:127.0.0.1
や実際のサーバーのIP)とポート番号が一致しているか、誤字脱字がないかを確認します。 - ファイアウォールの確認
クライアント側、サーバー側、またはその間のネットワーク機器(ルーターなど)のファイアウォール設定を確認します。指定されたポートでの通信が許可されているかを確認し、必要であれば例外を追加します。 - サーバーの最大接続数
サーバー側で受け入れ可能な接続数に上限がある場合、その上限に達している可能性があります。サーバーアプリケーションの設定を確認し、必要であれば増やします。 - ネットワーク接続の確認
単純にネットワークケーブルが抜けていたり、Wi-Fi接続が切れていたりする可能性もあります。
- サーバーの稼働状況を確認する
- 発生状況
- クライアントがサーバーに接続しようとしたが、サーバーが接続を拒否した。
- 指定したIPアドレスやポートにサーバーが起動していない。
- ファイアウォールによって接続がブロックされている。
- サーバー側の接続キューが満杯になっている。
QAbstractSocket::HostNotFoundError (ホストが見つからないエラー)
- トラブルシューティング
- ホスト名のスペルミス
ホスト名に誤字脱字がないか再確認します。 - インターネット接続の確認
クライアントPCがインターネットに接続されているか、DNSサーバーにアクセスできるかを確認します。ウェブブラウザで他のサイトにアクセスできるか試すのが簡単です。 - DNS設定の確認
PCのネットワーク設定でDNSサーバーのアドレスが正しく設定されているかを確認します。場合によっては、Google Public DNS (8.8.8.8, 8.8.4.4) などの信頼性の高いDNSサーバーを一時的に使用してみるのも有効です。 - ローカルホストファイル
極稀に、ローカルのhostsファイルに誤ったエントリがあるために発生することもあります。
- ホスト名のスペルミス
- 発生状況
- 指定されたホスト名(例:
www.example.com
)がDNSで解決できない。 - ホスト名が間違っている。
- DNSサーバーに問題があるか、設定が間違っている。
- 指定されたホスト名(例:
QAbstractSocket::RemoteHostClosedError (リモートホストによる接続終了エラー)
- トラブルシューティング
- サーバーのログ確認
サーバー側のアプリケーションログを確認し、なぜ接続が閉じられたのか(エラー、タイムアウト、正常終了など)の手がかりを探します。 - プロトコルの確認
クライアントとサーバーが同じプロトコル(例: HTTP, カスタムプロトコル)を使用しており、データのフォーマットも期待通りであるかを確認します。不正なデータを送ると、サーバーが接続を切断することがあります。 - タイムアウト設定
クライアント側やサーバー側のタイムアウト設定を確認します。アイドル状態が続くと切断される場合があります。 - サーバーの安定性
サーバーアプリケーション自体が不安定で、頻繁にクラッシュしている可能性も考えられます。
- サーバーのログ確認
- 発生状況
- 接続先のサーバーが正常に接続を閉じました。
- サーバーがクラッシュした、または予期せず終了した。
- サーバー側でタイムアウト設定があり、一定時間通信がなかったために接続が切断された。
- プロトコルの不一致や不正なデータが送信されたため、サーバーが接続を切断した。
QAbstractSocket::SocketTimeoutError (ソケットタイムアウトエラー)
- トラブルシューティング
- タイムアウト値の調整
waitForConnected()
やwaitForReadyRead()
に渡すタイムアウト値を増やしてみます。ただし、闇雲に増やすとアプリケーションの応答性が悪くなるため注意が必要です。 - ネットワーク状況の確認
ネットワークが混雑していないか、サーバーへのpingなどで遅延がないかを確認します。 - サーバーの負荷
サーバーが高い負荷状態にある場合、応答が遅れることがあります。サーバーのリソース(CPU, メモリ)使用率を確認します。 - 非同期処理の活用
可能な限りwaitFor...
系関数を使わず、シグナルとスロットによる非同期処理を基本とするようにコードを修正します。これにより、タイムアウトによるブロックを防ぎ、アプリケーションの応答性を保つことができます。
- タイムアウト値の調整
- 発生状況
connectToHost()
やwaitForConnected()
、waitForReadyRead()
などの操作が、指定された時間内に完了しなかった。- ネットワークの遅延が大きい。
- サーバーからの応答が遅い。
QAbstractSocket::NetworkError (ネットワークエラー)
- トラブルシューティング
- 物理接続の確認
ネットワークケーブルが正しく接続されているか、Wi-Fi接続がアクティブかを確認します。 - ネットワークアダプターの確認
デバイスマネージャーなどでネットワークアダプターが正常に動作しているかを確認します。 - PCの再起動
一時的なネットワークの問題であれば、PCの再起動で解決することがあります。 - 他のアプリケーションでのネットワーク使用
他のアプリケーションでネットワークが正常に利用できるかを確認し、問題がQtアプリケーションに固有のものか、システム全体の問題かを見極めます。
- 物理接続の確認
- 発生状況
- 物理的なネットワーク接続の問題(ケーブルが抜けた、Wi-Fiが切れた)。
- ネットワークアダプターの問題。
- ネットワークドライバの問題。
QAbstractSocket::UnknownSocketError (不明なソケットエラー)
- トラブルシューティング
- errorString() の活用
QAbstractSocket::errorString()
メソッドを呼び出して、より詳細なエラーメッセージを取得します。このメッセージは、具体的な原因を特定するための重要な手がかりとなります。 - オペレーティングシステムのエラーコード
errorString()
がシステムからのエラーメッセージを返す場合、それを元にオペレーティングシステムのドキュメントやオンラインリソースで検索します。 - デバッグログの強化
ソケットの接続状態の変化 (stateChanged
シグナル) や送受信データなど、より詳細なログを出力するようにアプリケーションを修正し、エラー発生時の状況を把握します。 - QtバージョンとOSの互換性
特定のQtバージョンやOSの組み合わせで稀に発生する問題である可能性もあります。最新のQtバージョンに更新したり、既知のバグ情報を調べたりすることも有効です。
- errorString() の活用
- 発生状況
- Qtが特定できない、または分類できないエラーが発生した場合。
- 公式ドキュメントとフォーラム
Qtの公式ドキュメント (QAbstractSocket
クラスリファレンス) やQtフォーラムは、問題解決のための貴重な情報源です。同様の問題に遭遇した他の開発者の経験や解決策が見つかるかもしれません。 - 最小限の再現コード
問題が発生した場合、その問題を再現できる最小限のQtコードを作成してみましょう。これにより、問題の切り分けが容易になります。 - 非同期処理の理解
Qtのネットワークモジュールは基本的に非同期で動作します。connected()
,disconnected()
,readyRead()
,error()
などのシグナルを適切に処理することが重要です。同期的にwaitFor...
系関数を多用すると、アプリケーションがフリーズしたり、思わぬタイムアウトが発生したりする原因となることがあります。 - デバッグ出力の活用
qDebug()
を使って、ソケットの接続状態 (state()
) や、データの送受信状況などをこまめにログに出力することで、エラーが発生するまでの流れを追跡しやすくなります。 - errorString() を常に使用する
error()
シグナルを受け取ったら、必ずsocket->errorString()
を呼び出して、人間が読める形式のエラーメッセージを取得するようにしましょう。これはデバッグの第一歩です。
例1:基本的なエラーハンドリング (QTcpSocket クライアント)
この例では、TCPクライアントがサーバーに接続しようと試み、接続の成功または失敗(エラー)をコンソールに出力します。
クライアント側のコード (tcpclient.h
)
#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include <QObject>
#include <QTcpSocket>
#include <QHostAddress> // QHostAddress を使用するためにインクルード
class TcpClient : public QObject
{
Q_OBJECT
public:
explicit TcpClient(QObject *parent = nullptr);
void connectToServer(const QString &address, quint16 port);
signals:
void connected();
void disconnected();
void error(QAbstractSocket::SocketError socketError); // エラーシグナルを定義
private slots:
void onConnected();
void onDisconnected();
void onErrorOccurred(QAbstractSocket::SocketError socketError); // エラーハンドリング用のスロット
private:
QTcpSocket *socket;
};
#endif // TCPCLIENT_H
クライアント側のコード (tcpclient.cpp
)
#include "tcpclient.h"
#include <QDebug> // デバッグ出力用
TcpClient::TcpClient(QObject *parent) : QObject(parent)
{
socket = new QTcpSocket(this);
// シグナルとスロットの接続
connect(socket, &QTcpSocket::connected, this, &TcpClient::onConnected);
connect(socket, &QTcpSocket::disconnected, this, &TcpClient::onDisconnected);
// errorOccurred シグナルを接続 (Qt 5 以降推奨)
connect(socket, &QTcpSocket::errorOccurred, this, &TcpClient::onErrorOccurred);
// Qt 4 スタイルで error(QAbstractSocket::SocketError) を接続する場合:
// connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onErrorOccurred(QAbstractSocket::SocketError)));
}
void TcpClient::connectToServer(const QString &address, quint16 port)
{
qDebug() << "Attempting to connect to" << address << ":" << port;
socket->connectToHost(address, port);
}
void TcpClient::onConnected()
{
qDebug() << "Connected to server!";
emit connected();
// 接続成功後、サーバーにデータを送信するなどの処理を追加できます
// socket->write("Hello, server!");
}
void TcpClient::onDisconnected()
{
qDebug() << "Disconnected from server.";
emit disconnected();
}
// エラーハンドリングのスロット
void TcpClient::onErrorOccurred(QAbstractSocket::SocketError socketError)
{
qWarning() << "Socket Error occurred: " << socketError;
qWarning() << "Error string: " << socket->errorString(); // より詳細なエラーメッセージ
switch (socketError) {
case QAbstractSocket::ConnectionRefusedError:
qDebug() << "Error: Connection was refused by the peer (server).";
qDebug() << "Possible causes: Server not running, incorrect IP/port, firewall.";
break;
case QAbstractSocket::HostNotFoundError:
qDebug() << "Error: Host address was not found.";
qDebug() << "Possible causes: Incorrect hostname, DNS issues.";
break;
case QAbstractSocket::SocketTimeoutError:
qDebug() << "Error: Socket operation timed out.";
qDebug() << "Possible causes: Network delay, server unresponsiveness.";
break;
case QAbstractSocket::NetworkError:
qDebug() << "Error: A network error occurred.";
qDebug() << "Possible causes: Disconnected network cable, network adapter issues.";
break;
case QAbstractSocket::RemoteHostClosedError:
qDebug() << "Error: The remote host closed the connection.";
qDebug() << "Possible causes: Server shutdown, crash, or idle timeout.";
break;
case QAbstractSocket::UnknownSocketError:
qDebug() << "Error: An unknown socket error occurred.";
break;
default:
qDebug() << "Unhandled socket error code: " << socketError;
break;
}
emit error(socketError); // 外部にエラーを通知する場合
}
メイン関数 (main.cpp
)
#include <QCoreApplication>
#include "tcpclient.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
TcpClient client;
// 接続テスト
// サーバーが実行されていない、またはポートが間違っている場合: ConnectionRefusedError
// 存在しないホスト名の場合: HostNotFoundError
// 接続まで時間がかかる場合: SocketTimeoutError (connectToHost にはタイムアウトがありませんが、
// waitForConnected() を使うと発生します)
client.connectToServer("127.0.0.1", 12345); // ローカルホストの12345ポート
// エラーシグナルを受け取った際の追加処理 (オプション)
QObject::connect(&client, &TcpClient::error, [](QAbstractSocket::SocketError err){
qDebug() << "Main application received client error:" << err;
// エラーによってはアプリケーションを終了するなどの処理
// QCoreApplication::quit();
});
return a.exec();
}
この例の解説:
- TcpClient クラス
QTcpSocket
のインスタンスをメンバとして持ちます。- コンストラクタでソケットのシグナル(
connected
,disconnected
,errorOccurred
)を適切なスロットに接続します。 connectToServer
メソッドで指定されたホストとポートに接続を開始します。
- onErrorOccurred(QAbstractSocket::SocketError socketError) スロット
QTcpSocket
からerrorOccurred
シグナルが発せられたときに呼び出されます。- 引数の
socketError
をswitch
文で評価し、エラーの種類に応じて異なるメッセージを出力します。 socket->errorString()
を呼び出すことで、Qtが提供するより詳細なエラーメッセージを取得できます。これはデバッグに非常に役立ちます。- ここでは
qDebug()
やqWarning()
でコンソールに出力していますが、実際にはユーザーインターフェースにエラーメッセージを表示したり、ログファイルに記録したりすることが多いでしょう。
- main.cpp
TcpClient
のインスタンスを作成し、connectToServer
を呼び出して接続を開始します。client
オブジェクトから発せられるカスタムエラーシグナル (client.error
) を捕捉し、アプリケーションレベルでのエラー処理を行うことも可能です。
connectToHost()
は非同期操作であり、すぐに戻ります。接続が確立するのを待つには waitForConnected()
を使用できます。この関数はタイムアウトを持つため、QAbstractSocket::SocketTimeoutError
を発生させる可能性があります。
#include <QCoreApplication>
#include <QTcpSocket>
#include <QDebug>
#include <QTimer> // QTimer を使用して非同期接続をデモンストレーション
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTcpSocket socket;
// シグナルとスロットの接続
QObject::connect(&socket, &QTcpSocket::connected, [&](){
qDebug() << "Connected to server (Async)!";
socket.write("Hello from async client!");
});
QObject::connect(&socket, &QTcpSocket::disconnected, [&](){
qDebug() << "Disconnected from server (Async).";
});
QObject::connect(&socket, &QTcpSocket::errorOccurred, [&](QAbstractSocket::SocketError socketError){
qWarning() << "Socket Error occurred (Async): " << socketError;
qWarning() << "Error string: " << socket.errorString();
if (socketError == QAbstractSocket::SocketTimeoutError) {
qDebug() << "Connection attempt timed out. Server might be slow or unreachable.";
} else if (socketError == QAbstractSocket::ConnectionRefusedError) {
qDebug() << "Connection refused. Is the server running and on correct port?";
}
// 他のエラーも同様に処理
});
qDebug() << "Attempting to connect asynchronously to 127.0.0.1:12345";
socket.connectToHost("127.0.0.1", 12345); // 非同期接続開始
// QTimer を使用して、接続が確立するのを少し待ってから、接続状態をチェック
QTimer::singleShot(3000, [&]() { // 3秒後に実行
if (socket.state() == QAbstractSocket::ConnectedState) {
qDebug() << "Socket is in ConnectedState after 3 seconds.";
} else {
qDebug() << "Socket is NOT in ConnectedState after 3 seconds. Current state:" << socket.state();
// ここで waitForConnected を使うと、強制的に同期的に待つことができます。
// しかし、通常はシグナル/スロットで非同期に処理することが推奨されます。
// 以下はデモンストレーション目的で、waitForConnected を使う場合の例:
qDebug() << "Trying to wait for connection for 5 seconds...";
if (!socket.waitForConnected(5000)) { // 5秒待機
qDebug() << "waitForConnected timed out!";
qDebug() << "Current error:" << socket.errorString();
// この時点で SocketTimeoutError が発生している可能性があります
} else {
qDebug() << "waitForConnected succeeded!";
}
}
a.quit(); // アプリケーションを終了
});
return a.exec();
}
- 非同期接続
- 最初の
socket.connectToHost("127.0.0.1", 12345);
は非同期で接続を開始します。 - アプリケーションはブロックされず、すぐに次のコード行に進みます。
- 接続の成功/失敗は
connected()
またはerrorOccurred()
シグナルによって通知されます。
- 最初の
- QTimer::singleShot
- これは、数秒後に特定のコードを実行するためのものです。ここでは、接続が完了したかどうかを確認するために使用しています。
- socket.waitForConnected(5000)
- この行は、接続が確立されるのを最大5000ミリ秒(5秒)待機します。
- もしタイムアウト内に接続が確立されなければ、
false
を返し、ソケットの状態はQAbstractSocket::UnconnectedState
のままか、QAbstractSocket::ConnectingState
のままになります。 - そして、ソケットの
error()
メソッドはQAbstractSocket::SocketTimeoutError
を返します。 - 注意点
waitForConnected()
のようなwaitFor...
関数は、アプリケーションのメインスレッドをブロックするため、GUIアプリケーションでは通常推奨されません。バックグラウンドスレッドで実行するか、シグナル/スロットによる非同期処理を優先すべきです。
QAbstractSocket::errorString() の活用
これは代替というよりは補完的な方法ですが、最も重要かつ頻繁に使用されます。
QAbstractSocket::errorOccurred(QAbstractSocket::SocketError)
シグナルを受け取った際、socket->errorString()
を呼び出すことで、より具体的で人間が読めるエラーメッセージを取得できます。
特徴
- デバッグ時に特に役立ちます。
SocketError
列挙型だけでは特定できない、オペレーティングシステム固有のエラーメッセージや、より詳細なコンテキストを提供します。
コード例
void MySocketHandler::onErrorOccurred(QAbstractSocket::SocketError socketError)
{
qWarning() << "ソケットエラー発生: " << socketError;
qWarning() << "詳細メッセージ: " << socket->errorString(); // ここが重要
if (socketError == QAbstractSocket::UnknownSocketError) {
// UnknownSocketError の場合、errorString() が特に重要になる
qDebug() << "不明なエラーですが、システムからのメッセージは: " << socket->errorString();
}
// 他のエラータイプも同様に処理
}
ロギングシステムとの連携
アプリケーションが独自のロギングシステム(例: Log4Qt、カスタムログクラス)を持っている場合、SocketError
をそのシステムに統合することで、エラーの追跡と分析を容易にできます。
特徴
- 本番環境での問題特定に不可欠です。
- 重大度レベル(例: DEBUG, INFO, WARNING, ERROR, CRITICAL)を設定し、フィルタリングや通知を行うことができます。
- エラー発生日時、エラータイプ、詳細メッセージ、ソケットの状態などを一元的に記録できます。
コード例 (概念)
// MyLogger クラスがあると仮定
// MyLogger::logError(const QString& message, QAbstractSocket::SocketError err, const QString& details);
void MySocketHandler::onErrorOccurred(QAbstractSocket::SocketError socketError)
{
QString errorMessage = QString("ソケットエラー [%1]: %2")
.arg(static_cast<int>(socketError))
.arg(socket->errorString());
// ロギングシステムにエラーを記録
MyLogger::logError(errorMessage, socketError, "ソケットの状態: " + QString::number(socket->state()));
// 必要に応じてユーザーに通知
// QMessage::critical(this, "ネットワークエラー", errorMessage);
}
FSM (有限状態機械) を使用したエラー状態管理
より複雑なネットワーク通信アプリケーションでは、ソケットの状態とエラーを有限状態機械 (FSM) で管理することが有効です。Qt では QStateMachine
や QState
を使用してFSMを構築できます。
特徴
- 複雑な再接続ロジックや、複数のエラー条件の組み合わせを扱う場合に、コードの可読性と保守性を向上させます。
- エラー発生時に、特定の状態に遷移させ、その状態でエラーリカバリロジック(例: 再接続試行、エラー通知)を実行させることができます。
- ソケットの各状態(Disconnected, HostLookup, Connecting, Connected, Closing)と、エラーによって遷移する状態を明確に定義できます。
コード例 (概念)
// QStateMachine と QState を使用した FSM の一部
// (簡略化された例)
QStateMachine *machine = new QStateMachine(this);
QState *disconnectedState = new QState(machine);
QState *connectingState = new QState(machine);
QState *connectedState = new QState(machine);
QState *errorState = new QState(machine); // エラー専用の状態
// 状態遷移の定義
disconnectedState->addTransition(socket, &QTcpSocket::connected, connectedState);
disconnectedState->addTransition(socket, &QTcpSocket::errorOccurred, errorState); // Disconnected からエラーへ
connectingState->addTransition(socket, &QTcpSocket::connected, connectedState);
connectingState->addTransition(socket, &QTcpSocket::errorOccurred, errorState); // Connecting からエラーへ
connectedState->addTransition(socket, &QTcpSocket::disconnected, disconnectedState);
connectedState->addTransition(socket, &QTcpSocket::errorOccurred, errorState); // Connected からエラーへ
// エラーステートでの処理
QObject::connect(errorState, &QState::entered, [=](){
qDebug() << "ソケットがエラー状態に入りました。";
qDebug() << "エラー情報: " << socket->errorString();
// ここでエラーに応じたリカバリロジック(例: ログ記録、ユーザー通知、再接続タイマーの開始)
// 例: ConnectionRefused の場合は再接続を試みる
if (socket->error() == QAbstractSocket::ConnectionRefusedError) {
qDebug() << "接続拒否エラーのため、5秒後に再接続を試みます...";
QTimer::singleShot(5000, socket, [=](){
socket->connectToHost("target_host", 12345);
// 再接続試行後、Connecting または ErrorState に戻る遷移を定義
machine->postEvent(new QEvent(QEvent::User)); // 例として FSM にイベントを通知
});
}
});
machine->setInitialState(disconnectedState);
machine->start();
// クライアントの接続開始など...
// socket->connectToHost("target_host", 12345); // これにより connectingState に遷移する可能性がある
Qt の慣例ではシグナルとスロットによるエラー通知が主ですが、C++の一般的なパターンとしてカスタム例外クラスを定義し、特定のエラー発生時にそれをスローする方法も考えられます。ただし、これは Qt のイベントループや非同期処理の哲学とはあまり整合性がなく、通常は推奨されません。
特徴
- しかし、Qtのネットワーク処理は本質的に非同期であるため、例外をスローするとイベントループが中断される可能性があり、設計が複雑になります。
- 同期的な処理フローではエラーハンドリングが簡潔になる場合があります。
注意点
- Qt のネットワークプログラミングでは、
QAbstractSocket::errorOccurred
シグナルを受け取り、スロットで処理する非同期パターンが標準的であり、推奨されます。例外を使用すると、非同期イベント処理の利点が失われる可能性があります。
QAbstractSocket::SocketError
のハンドリングにおいて、最も一般的で推奨される方法は、errorOccurred
シグナルを捕捉し、そのスロット内で socket->errorString()
を利用して詳細なエラー情報を取得し、そのエラーの種類に基づいて適切なロギング、ユーザー通知、およびリカバリロジックを実行することです。