Для работы со стеком TCP/IP Qt предоставляет классы QTcpServer, QTcpSocket, а также QUdpSocket. Для первого знакомства с работой локальной сети напишем Echo Сервер . Задача эхо сервера отправлять назад к отправителю полученные от него данные, как это делает эхо с человеческим голосом. Для подключения к серверу будет использоваться telnet.
TELNET (англ. TErminaL NETwork) — сетевой протокол для реализации текстового интерфейса по сети (в современной форме — при помощи транспорта TCP). Название «telnet» имеют также некоторые утилиты, реализующие клиентскую часть протокола.
В предлагаемой программе объект класса QTcpServer будет слушать один из портов стека протоколов TCP/IP от всех хостов в сети. Прослушивание порта устанавливается методом listen() с указанием специфицированного IP-адреса или диапазона IP-адресов, а также порта прослушивания.
Когда происходит подключение клиента к порту вызывается сигнал newConnection() , который будет поключён к слоту slotNewConnection(), в данном слоте будет инициировано подключение клиента в качестве объекта QTcpSocket на стороне сервера с помощью метода nextPendingConnection(), который возвращает указатель на объект QTcpSocket.
К новому сокету будет подключено два слота. Первый слот slotServerRead() будет подключён к сигналу readyRead от сокета и будет вызываться в том случае, если на сокет пришли данные, которые готовы к чтению. Второй слот slotClientDisconnected() подключён к сигналу disconnected() , который вызывается в том случае, когда клиент отключился от сервера и необходимо закрыть соединение со стороны сервера.
Структура проекта для работы с QTcpServer
Будет создано консольное приложение, следовательно классов вроде MainWindow в приложении использовано не будет.
- EchoServer.pro - профайл проекта;
- main.cpp - основной файл исходных кодов;
- mytcpserver.h - заголовочный файл сервера;
- mytcpserver.cpp - файл исходных кодов сервера;
EchoServer.pro
В данном файле необходимо добавить Qt модуль для работы с сетью.
QT += network
main.cpp
Всё, что нужно сделать в данном файле, это подключить заголовочный файл сервера и создать экземпляр сервера.
#include <QCoreApplication> #include "mytcpserver.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); MyTcpServer server; return a.exec(); }
mytcpserver.h
Данный класс является обёрткой для работы с QTcpServer и наследован от QObject для применения сигналов и слотов .
#ifndef MYTCPSERVER_H #define MYTCPSERVER_H #include <QObject> #include <QTcpServer> #include <QTcpSocket> class MyTcpServer : public QObject { Q_OBJECT public: explicit MyTcpServer(QObject *parent = 0); public slots: void slotNewConnection(); void slotServerRead(); void slotClientDisconnected(); private: QTcpServer * mTcpServer; QTcpSocket * mTcpSocket; }; #endif // MYTCPSERVER_H
mytcpserver.cpp
#include "mytcpserver.h" #include <QDebug> #include <QCoreApplication> MyTcpServer::MyTcpServer(QObject *parent) : QObject(parent) { mTcpServer = new QTcpServer(this); connect(mTcpServer, &QTcpServer::newConnection, this, &MyTcpServer::slotNewConnection); if(!mTcpServer->listen(QHostAddress::Any, 6000)){ qDebug() << "server is not started"; } else { qDebug() << "server is started"; } } void MyTcpServer::slotNewConnection() { mTcpSocket = mTcpServer->nextPendingConnection(); mTcpSocket->write("Hello, World!!! I am echo server!\r\n"); connect(mTcpSocket, &QTcpSocket::readyRead, this, &MyTcpServer::slotServerRead); connect(mTcpSocket, &QTcpSocket::disconnected, this, &MyTcpServer::slotClientDisconnected); } void MyTcpServer::slotServerRead() { while(mTcpSocket->bytesAvailable()>0) { QByteArray array = mTcpSocket->readAll(); mTcpSocket->write(array); } } void MyTcpServer::slotClientDisconnected() { mTcpSocket->close(); }
Работа с Echo Server
После того, как Вы соберёте проект и у Вас запустится консольное приложение, воспользуйтесь любым программным обеспечением, которое поддерживает telnet, например Putty, и подключитесь на настроенный порт. Попробуйте отослать данные сервер, чтобы убедиться, что он Вам их вернёт. В некоторых случаях этого может не произойти или Вы вообще не сможете подключиться, тогда отключите FireWall, часто проблема скрывается именно в нём.
Демонстрация работы приложения с дополнительными комментариями представлена в видеоуроке.
Ссылка на скачивание проекта в zip-архиве: echoserver.zip
Добрый день, разрешите вопрос:
во всех примерах работы с QTcpServer его всегда создают в main.cpp, нет ли возможности корректно сохдать его в MainWindow.cpp ?
Просто если перенести код в MainWindow.cpp
то возникает ошибка на клиенте QNativeSocketEngine::write() was not called in QAbstractSocket::ConnectedState
Переменную myserver нужно объявить в заголовочном файле, в противном случае после выполнения конструктора окна эта переменная удаляется, поскольку создана на стеке конструктора.
спасибо, но уже не актуально. Я уже написал классы многопоточного сервера и они переносимы, раскидал их по четырем приложениям, даже не меняя код.
Hi, great post.
If I were to reimplement QTcpServer for the purposes of SSL, shouldn't incomingConnection(int socket) just be automatically called after a connection occurs?