Evgenii Legotckoi
8 декабря 2015 г. 21:20

Qt/C++ - Урок 034. Echo Server на основе QTcpServer

Для работы со стеком 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 модуль для работы с сетью.

  1. QT += network

main.cpp

Всё, что нужно сделать в данном файле, это подключить заголовочный файл сервера и создать экземпляр сервера.

  1. #include <QCoreApplication>
  2. #include "mytcpserver.h"
  3.  
  4. int main(int argc, char *argv[])
  5. {
  6. QCoreApplication a(argc, argv);
  7.  
  8. MyTcpServer server;
  9.  
  10. return a.exec();
  11. }

mytcpserver.h

Данный класс является обёрткой для работы с QTcpServer и наследован от QObject для применения сигналов и слотов .

  1. #ifndef MYTCPSERVER_H
  2. #define MYTCPSERVER_H
  3.  
  4. #include <QObject>
  5. #include <QTcpServer>
  6. #include <QTcpSocket>
  7.  
  8. class MyTcpServer : public QObject
  9. {
  10. Q_OBJECT
  11. public:
  12. explicit MyTcpServer(QObject *parent = 0);
  13.  
  14. public slots:
  15. void slotNewConnection();
  16. void slotServerRead();
  17. void slotClientDisconnected();
  18.  
  19. private:
  20. QTcpServer * mTcpServer;
  21. QTcpSocket * mTcpSocket;
  22. };
  23.  
  24. #endif // MYTCPSERVER_H

mytcpserver.cpp

  1. #include "mytcpserver.h"
  2. #include <QDebug>
  3. #include <QCoreApplication>
  4.  
  5. MyTcpServer::MyTcpServer(QObject *parent) : QObject(parent)
  6. {
  7. mTcpServer = new QTcpServer(this);
  8.  
  9. connect(mTcpServer, &QTcpServer::newConnection, this, &MyTcpServer::slotNewConnection);
  10.  
  11. if(!mTcpServer->listen(QHostAddress::Any, 6000)){
  12. qDebug() << "server is not started";
  13. } else {
  14. qDebug() << "server is started";
  15. }
  16. }
  17.  
  18. void MyTcpServer::slotNewConnection()
  19. {
  20. mTcpSocket = mTcpServer->nextPendingConnection();
  21.  
  22. mTcpSocket->write("Hello, World!!! I am echo server!\r\n");
  23.  
  24. connect(mTcpSocket, &QTcpSocket::readyRead, this, &MyTcpServer::slotServerRead);
  25. connect(mTcpSocket, &QTcpSocket::disconnected, this, &MyTcpServer::slotClientDisconnected);
  26. }
  27.  
  28. void MyTcpServer::slotServerRead()
  29. {
  30. while(mTcpSocket->bytesAvailable()>0)
  31. {
  32. QByteArray array = mTcpSocket->readAll();
  33.  
  34. mTcpSocket->write(array);
  35. }
  36. }
  37.  
  38. void MyTcpServer::slotClientDisconnected()
  39. {
  40. mTcpSocket->close();
  41. }

Работа с Echo Server

После того, как Вы соберёте проект и у Вас запустится консольное приложение, воспользуйтесь любым программным обеспечением, которое поддерживает telnet, например Putty, и подключитесь на настроенный порт. Попробуйте отослать данные сервер, чтобы убедиться, что он Вам их вернёт. В некоторых случаях этого может не произойти или Вы вообще не сможете подключиться, тогда отключите FireWall, часто проблема скрывается именно в нём.

Демонстрация работы приложения с дополнительными комментариями представлена в видеоуроке.

Ссылка на скачивание проекта в zip-архиве: echoserver.zip

Видеоурок

Вам это нравится? Поделитесь в социальных сетях!

ИВ
  • 16 марта 2021 г. 0:46
  • (ред.)

Добрый день, разрешите вопрос:
во всех примерах работы с QTcpServer его всегда создают в main.cpp, нет ли возможности корректно сохдать его в MainWindow.cpp ?
Просто если перенести код в MainWindow.cpp

  1. #include "myserver.h"
  2.  
  3. MainWindow::MainWindow( QWidget *parent): QMainWindow(parent) , ui(new Ui::MainWindow)
  4. {
  5. myserver Server;
  6. Server.startServer();
  7. ui->setupUi(this);

то возникает ошибка на клиенте QNativeSocketEngine::write() was not called in QAbstractSocket::ConnectedState

Evgenii Legotckoi
  • 2 июля 2021 г. 16:36

Переменную myserver нужно объявить в заголовочном файле, в противном случае после выполнения конструктора окна эта переменная удаляется, поскольку создана на стеке конструктора.

ИВ
  • 2 июля 2021 г. 16:51

спасибо, но уже не актуально. Я уже написал классы многопоточного сервера и они переносимы, раскидал их по четырем приложениям, даже не меняя код.

e
  • 16 августа 2021 г. 22:43

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?

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь