Введение
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. Без этого обработчика, если сервер недоступен или возникла любая сетевая проблема, программа просто «молчит» без какой-либо диагностики — пользователь не понимает, что пошло не так.