Различные статьи по Qt и c++

QTcpSocket для начинающих: первое сетевое соединение

Введение

QTcpSocket — класс для подключения к серверу по протоколу TCP, например для получения данных от удалённого устройства или взаимодействия с собственным сервером приложения. Если вы делаете первые шаги в сетевом программировании на Qt, в этой статье разберём самый базовый, понятный сценарий: подключение к серверу, отправка простого сообщения и приём ответа.

Концепция

Работа с QTcpSocket асинхронна и построена на сигналах: вызов connectToHost() не блокирует программу, а инициирует подключение в фоне; о результате сообщают сигналы connected() (соединение установлено) и errorOccurred() (что-то пошло не так). Когда от сервера приходят данные, испускается сигнал readyRead(), в обработчике которого нужно вызвать readAll(), чтобы получить пришедшие байты.

Пример кода: QTcpSocket для начинающих

#include <QTcpSocket>
#include <QApplication>
#include <QDebug>

class SimpleTcpClient : public QObject
{
    Q_OBJECT
public:
    SimpleTcpClient()
    {
        // Подключаем обработчики ДО попытки подключения к серверу
        connect(&m_socket, &QTcpSocket::connected, this, &SimpleTcpClient::onConnected);
        connect(&m_socket, &QTcpSocket::readyRead, this, &SimpleTcpClient::onReadyRead);
        connect(&m_socket, &QTcpSocket::errorOccurred, this, &SimpleTcpClient::onErrorOccurred);
    }

    void connectToServer(const QString &host, quint16 port)
    {
        qDebug() << "Пытаемся подключиться к" << host << ":" << port;
        m_socket.connectToHost(host, port);
    }

private slots:
    void onConnected()
    {
        qDebug() << "Подключение установлено!";

        // Отправляем простое текстовое сообщение на сервер
        m_socket.write("Привет, сервер!\n");
    }

    void onReadyRead()
    {
        const QByteArray data = m_socket.readAll();
        qDebug() << "Получен ответ от сервера:" << data;
    }

    void onErrorOccurred(QAbstractSocket::SocketError socketError)
    {
        Q_UNUSED(socketError);
        qDebug() << "Ошибка соединения:" << m_socket.errorString();
    }

private:
    QTcpSocket m_socket;
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    SimpleTcpClient client;
    client.connectToServer("example.com", 80);

    return app.exec();
}

#include "main.moc"

Пояснения к коду

Все обработчики сигналов (connected, readyRead, errorOccurred) подключаются в конструкторе SimpleTcpClient, то есть до вызова connectToServer() — это важно, поскольку соединение асинхронно, и если подписаться на сигналы слишком поздно (после того, как соединение уже могло быть установлено), можно пропустить момент срабатывания сигнала. connectToHost() сразу возвращает управление, а реальный результат подключения становится известен позже, через сигналы.

onReadyRead() показывает типичный паттерн обработки входящих данных — каждый раз, когда сервер присылает что-то новое, вызывается этот слот, и readAll() забирает все накопившиеся на данный момент байты. onErrorOccurred() обрабатывает любые сетевые проблемы (сервер недоступен, соединение прервано) и выводит понятное текстовое описание через errorString().

Подводные камни

  • Подключение сигналов после вызова connectToHost(). Если подписаться на connected() слишком поздно, можно «упустить» момент, когда сигнал уже сработал (особенно при подключении к очень быстро отвечающему локальному серверу) — правильный порядок: сначала все connect(), затем connectToHost().
  • Ожидание, что connectToHost() сразу подключит сокет. Это асинхронный вызов — сразу после него m_socket.state() может ещё быть ConnectingState, а не ConnectedState; реальный результат нужно узнавать через сигналы, а не проверять состояние немедленно после вызова.
  • Предположение, что весь ответ сервера придёт за один вызов readyRead(). Данные по TCP могут приходить частями — для коротких тестовых сообщений (как в примере) это обычно не проблема, но для более серьёзных протоколов нужно накапливать данные в буфере, пока не придёт всё ожидаемое сообщение целиком (этот аспект подробнее разбирается в основном цикле статей про сеть).
  • Забытая обработка errorOccurred. Без этого обработчика, если сервер недоступен или возникла любая сетевая проблема, программа просто «молчит» без какой-либо диагностики — пользователь не понимает, что пошло не так.