TCPネットワークデバッガの開発
本プロジェクトでは、UIの簡素化を図りつつ、TCP通信の核心技術に焦点を当てたネットワークデバッガを開発する。主な目的は、クライアント・サーバー間の接続確立、データ送受信の実装であり、同時にQtのGUIコンポーネントの再利用も行う。
サーバー側の実装フロー
QtでTCPサーバーを構築するには、QTcpServer クラスを使用し、以下の手順を踏む:
- サーバーの初期化:
QTcpServerをインスタンス化し、指定されたポートでクライアントの接続を待機(listen)させる。 - 新規接続の処理:
newConnectionシグナルをスロットに接続し、nextPendingConnection()を通じて新しいQTcpSocketを取得する。 - データの受信:<クライアントソケットの
readyReadシグナルをハンドリングし、readAll()でデータを読み取る。 - データの送信:
write()メソッドでクライアントへ応答を送信する。 - 切断処理:
disconnectedシグナルを監視し、接続終了時の後片付けを行う。
#include <QTcpServer>
#include <QTcpSocket>
class TcpServerHandler : public QObject {
Q_OBJECT
public:
explicit TcpServerHandler(QObject *parent = nullptr) : QObject(parent) {
server = new QTcpServer(this);
connect(server, &QTcpServer::newConnection, this, &TcpServerHandler::handleNewConnection);
if (!server->listen(QHostAddress::Any, 8080)) {
qDebug() << "サーバー起動失敗";
}
}
private slots:
void handleNewConnection() {
QTcpSocket *client = server->nextPendingConnection();
connect(client, &QTcpSocket::readyRead, this, &TcpServerHandler::readClientData);
connect(client, &QTcpSocket::disconnected, client, &QTcpSocket::deleteLater);
qDebug() << "クライアント接続:" << client->peerAddress().toString();
}
void readClientData() {
QTcpSocket *client = qobject_cast<QTcpSocket*>(sender());
QByteArray data = client->readAll();
qDebug() << "受信データ:" << data;
// エコーバック
client->write("受信しました: " + data);
}
private:
QTcpServer *server;
};
クライアント側の実装フロー
クライアントはQTcpSocketを使用してサーバーに接続し、双方向通信を実現する。
- ソケットの作成:
QTcpSocketを生成。 - サーバーへの接続:
connectToHost()でIPアドレスとポートを指定して接続試行。 - 接続状態の監視:
connectedおよびerrorシグナルを捕捉し、成功/失敗を判定。 - データ送信:
write()でサーバーにメッセージを送信。 - データ受信:
readyReadでレスポンスを受信。
class TcpClientHandler : public QObject {
Q_OBJECT
public:
explicit TcpClientHandler(QObject *parent = nullptr) : QObject(parent) {
socket = new QTcpSocket(this);
connect(socket, &QTcpSocket::connected, this, &TcpClientHandler::onConnected);
connect(socket, &QTcpSocket::readyRead, this, &TcpClientHandler::onReadyRead);
connect(socket, &QAbstractSocket::errorOccurred, this, &TcpClientHandler::onError);
// 接続タイマー(タイムアウト対策)
timeoutTimer = new QTimer(this);
timeoutTimer->setSingleShot(true);
timeoutTimer->setInterval(5000);
connect(timeoutTimer, &QTimer::timeout, this, &TcpClientHandler::onConnectTimeout);
socket->connectToHost("127.0.0.1", 8080);
timeoutTimer->start();
}
private slots:
void onConnected() {
timeoutTimer->stop();
qDebug() << "サーバーに接続成功";
socket->write("Hello Server!");
}
void onReadyRead() {
QByteArray data = socket->readAll();
qDebug() << "サーバーからの応答:" << data;
}
void onError() {
if (timeoutTimer->isActive()) return; // タイムアウト優先
qDebug() << "接続エラー:" << socket->errorString();
}
void onConnectTimeout() {
socket->abort();
qDebug() << "接続タイムアウト";
}
private:
QTcpSocket *socket;
QTimer *timeoutTimer;
};
UI機能の拡張
ユーザーインターフェースには以下のような拡張機能を実装:
動的IPアドレス検出
現在のネットワークインターフェースからIPv4アドレスを自動取得し、コンボボックスに追加する。
void populateIpAddresses() {
QList<QHostAddress> addresses = QNetworkInterface::allAddresses();
for (const QHostAddress &addr : addresses) {
if (addr.protocol() == QAbstractSocket::IPv4Protocol && !addr.isLoopback()) {
ui->comboBox_ip->addItem(addr.toString());
}
}
}
テキスト表示の色分け
受信ログに色をつけることで、送信元や種類を視覚的に識別可能にする。
void appendColoredText(const QString &text, QColor color) {
QTextCursor cursor = ui->textEdit_log->textCursor();
QTextCharFormat format;
format.setForeground(color);
cursor.mergeCharFormat(format);
cursor.insertText(text + "\n");
ui->textEdit_log->ensureCursorVisible();
}
複数クライアントへの個別送信
サーバーは接続中のすべてのクライアントをリスト管理し、コンボボックスから選択した特定のクライアントにのみデータを送信できるようにする。
void sendToSelectedClient(const QString &message) {
QString targetPort = ui->comboBox_clients->currentText();
QList<QTcpSocket *> clients = server->findChildren<QTcpSocket *>();
for (QTcpSocket *client : clients) {
if (targetPort == "全員" || QString::number(client->peerPort()) == targetPort) {
client->write(message.toUtf8());
}
}
}
通信の信頼性に関する理論知識
TCP(Transmission Control Protocol)は、接続指向かつ信頼性のあるトランスポートプロトコルである。主な特性として以下がある:
- 接続確立(3ウェイハンドシェイク):SYN → SYN-ACK → ACK の流れで接続を確立。
- 順序保証:パケットが到着順に関係なく、正しい順序でアプリケーションに提供される。
- 再送制御:ACKがなければパケットを再送。
- フロー制御:ウィンドウサイズにより送信速度を調整。
- 接続終了(4ウェイハンドシェイク):FINとACKのやり取りで安全に切断。
まとめ
本プロジェクトを通じて、QtにおけるTCP通信の基本構造、信号・スロット機構の活用、GUIとの連携方法を体系的に学習できた。特に、非同期通信におけるエラーハンドリングやUIスレッドとの整合性確保が重要なポイントである。