Инструменты пользователя

Инструменты сайта


develop:qt:network

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
develop:qt:network [2021/09/30 09:03]
admin
develop:qt:network [2021/10/27 04:33] (текущий)
admin
Строка 379: Строка 379:
  
 ===== Еще пример клиент/сервера ===== ===== Еще пример клиент/сервера =====
-В данном примере изменен подход: файл передается **от клиента серверу**, сервер почти поддерживает **множество клиентов** (создается объект сокета при каждом новом подключенииэтот вопрос еще нужно больше изучить), и главное то что **передается служебная инфа**, благодаря тому что чтение/запись ведется с помощью потоков данных (**QDataStream**).\\+Сервер поддерживает **множественное подключение**для **каждого соединения** создается **собственный контекст**, с собственными **буферами**\\
  
 +При передаче, данные делятся на пакеты дважды, типа программные и физические.\\
 +**Программные** формируются при каждом вызове метода **write()**, если писать через **QDataStream** в **QByteArray**, то перед каждой записью в датастрим будет автоматом добавлено интовое значение, с указанием размера записываемых данных.\\
  
-<details> +**Физические** формируются уже на транспортном уровне, чаще всего для них отводится 65 536 байт (его размер хранится в двухбайтовом поле заголовка т.е. само это поле может содержать максимум такое значение), размер может меняться по согласованию сторон, содержится в TCP заголовках.\\
-<summary>:!: Пример: Сервер</summary> +
-sserv.+
-<code cpp-qt> +
-#ifndef SSERV_H +
-#define SSERV_H+
  
-#include <QDialog> +Передача ведется потоковая, сначала идет служебная инфа, с указанием размера в т.ч., затем все остальное принимаем как содержимое файла, пока не достигнем указанный размер файла.\\
-#include <QLabel> +
-#include <QVector> +
-#include <QTcpServer> +
-#include <QTcpSocket> +
-#include <QMessageBox> +
-#include <QNetworkInterface> +
-#include <QRandomGenerator> +
-#include <QByteArray> +
-#include <QDataStream> +
-#include <QBoxLayout> +
-#include <QFile> +
-#include <QCheckBox> +
-#include <QLineEdit> +
-#include <QTextEdit>+
  
-class sserv : public QDialog +Служебную инфу пишем с помощью датаСтрим, что бы был указан размер этого "программного" пакета, и чтобы нам вырезать точное кол-во байт, занимаемое этой служебной инфой, содержимое файла просто читаем пока не достигнем указанного размера.\\
-+
-    Q_OBJECT+
  
-    QTcpServer *tcpServer; +Cчитывание 4ех байт остается корректным до тех пор пока программный пакет < 4gb, т.к. в эти 4 байта умещено интовое значение, которое принимает максимум 4 млрд.. байтов ~ 4gb.\\ 
-    QCheckBox *checkRunning; +Если программный пакет будет больше, мы просто некорректно считаем значение его размера и следовательно сам пакет не сможем принять (будет '-1')\\
-    QLabel *labPort; +
-    QLineEdit *editPort; +
-    QTextEdit *editEventsLog;+
  
-private slots: +<details> 
-    void slotSetNewConnection(); +<summary>:!: Пример: Сервер</summary>
-    void slotReadDataFromClient(); +
-    void slotStartStop(int);+
  
-public: 
-    sserv(QWidget *parent = nullptr); 
  
-}; +sserv.h 
-#endif // SSERV_H+<code cpp-qt> 
 </code> </code>
  
 sserv.cpp sserv.cpp
 <code cpp-qt> <code cpp-qt>
-#include "sserv.h" 
  
-sserv::sserv(QWidget *parent): QDialog(parent) 
-{ 
-    this->setFont(QFont("Arial", 14)); 
- 
-    checkRunning= new QCheckBox("Running Server", this); 
-    labPort= new QLabel("Port:", this); 
-    editPort= new QLineEdit("2233", this); 
-    editEventsLog= new QTextEdit(this); 
-    editEventsLog->setFont(QFont("Arial", 10)); 
- 
-    QGridLayout *layMain= new QGridLayout(this); 
-    layMain->setColumnStretch(1, 1); 
-    layMain->setColumnStretch(2, 1); 
-    layMain->addWidget(checkRunning, 0, 0); 
-    layMain->addWidget(labPort, 0, 1, Qt::AlignRight); 
-    layMain->addWidget(editPort, 0, 2); 
-    layMain->addWidget(editEventsLog, 1, 0, 1, -1); 
- 
-    tcpServer= new QTcpServer(this); 
-    connect(tcpServer, &QTcpServer::newConnection, this, &sserv::slotSetNewConnection); 
-    connect(checkRunning, &QCheckBox::stateChanged, this, &sserv::slotStartStop); 
-} 
- 
- 
-void sserv::slotSetNewConnection() 
-{ 
-    QTcpSocket *vNewConnect= tcpServer->nextPendingConnection(); 
-    connect(vNewConnect, &QTcpSocket::readyRead, this, &sserv::slotReadDataFromClient); 
-    connect(vNewConnect, &QTcpSocket::disconnected, vNewConnect, &QTcpSocket::deleteLater); 
-    editEventsLog->insertPlainText(tr("\nНовое подключение %1, %2, %3").arg(vNewConnect->peerName()).arg(vNewConnect->peerAddress().toString()).arg(vNewConnect->peerPort())); 
-} 
- 
- 
-void sserv::slotReadDataFromClient() 
-{ 
-    QTcpSocket *vConnect= static_cast<QTcpSocket*>(sender()); 
-    QDataStream vInput(vConnect); 
-    vInput.setVersion(QDataStream::Qt_4_0); 
- 
-    QByteArray vFirstStr; 
-    vInput >> vFirstStr; 
- 
-    // Предполагалось тут считывать тоже через поток, но можно и так тоже 
-    QByteArray vBuffer; 
-    vBuffer= vConnect->readAll(); 
- 
-    QFile vFile("D:\\this-text-getting.txt"); 
-    vFile.open(QIODevice::WriteOnly); 
-    vFile.write(vBuffer); 
-    vFile.close(); 
-} 
- 
- 
-void sserv::slotStartStop(int vState) 
-{ 
-    switch(vState) 
-    { 
-        case Qt::Checked: 
-            editPort->setEnabled(false); 
-            if(!tcpServer->listen(QHostAddress::LocalHost, editPort->text().toInt())) 
-                editEventsLog->insertPlainText("\nНе удается запустить сервер - "+ tcpServer->errorString()); 
-            else 
-                editEventsLog->insertPlainText("\nСервер запущен на порту - "+ QString::number(tcpServer->serverPort())); 
-            break; 
-        case Qt::Unchecked: 
-            tcpServer->close(); 
-            editPort->setEnabled(true); 
-            editEventsLog->insertPlainText("\nСервер остановлен"); 
-            break; 
-        default: 
-            editEventsLog->insertPlainText(tr("\nПередано не корректное значение, текущее состояние- ").arg(tcpServer->isListening() ? "работает":"не работает")); 
-            break; 
-    } 
-} 
 </code> </code>
 </details> </details>
Строка 511: Строка 411:
 <details> <details>
 <summary>:!: Пример: Клиент</summary> <summary>:!: Пример: Клиент</summary>
-sc.h +**Примечания:**\\ 
-<code cpp-qt> +:?: Установка соединения (**connectToHost()**) проходит асинхронно, после ее вызова уже можно писать данные в сокет (**write()**), данные будут собираться в буфер (**неизвестно какое ограничение**), и будут ожидать записи.\\ 
-#ifndef SC_H +Правда в случае ошибки соединения, буфер очистится, при проверке, данные были доступны вплоть до последнего сигнала об ошибке, при повторном запуске отправки, буфер уже был очищен.\\ 
-#define SC_H+:?: Запись можно делать из файла сразу в метод **write()**, без создания дополнительных **QByteArray** и **QDataStream**, как было в прошлых примерах.\\ 
 +**write()** принимает **QByteArray**, метод **read()** возвращает **QByteArray**, для записи служебных данных можно сделать отдельный метод для конвертации, вот тут та и понадобится **QDataStream**, который корректно запишет нужные данные в **QByteArray**.\\ 
 +:?: Что касается **проверки и установления** коннекта:\\ 
 +в нашем случае сокет может быть в след состояниях:\\ 
 +**0** не подключен - нужно **вызвать коннект**\\ 
 +**1,2** - выполняет поиск и начал устанавливать соединение - соединения еще нет, но и делать вроде ничего не надо, правда неизвестно может ли оно быть зависшим в этот состоянии ?, думаю тут- **ничего не делать**\\ 
 +**3** - установленно - **ничего не делать**\\ 
 +**6** - вот вот закроется - т.е. запрошен дисконнект, ожидается отправка очереди данных и коннект захлопнется, в таком случае явно нужно снова **вызвать коннект**\\
  
-#include <QDialog> +Что касается отправки файла, пишем данные кусками в метод **write()**, он возвращает кол-во записанных байт, это кол-во отнимаем от размера файла, так управляем циклом\\ 
-#include <QLabel> +В случае ошибки, **write()** возвращает **-1** и все идет по п#зде, поэтому необходимо проверять значение пред тем как с ним работать (отнимать от общем суммы)\\ 
-#include <QLineEdit> +Так же, после вызова **write()** и до фактической отправки данных нужно какое то время, до этого данные копятся в буфере (видно в **bytesToWrite()**), если писать в цикле то данные **не успевают** уходить в **заполняют буфер** до талого, поэтому нужен метод **wait()**, либо сигнало/слот можно прикрутить какой нибудь\\
-#include <QPushButton> +
-#include <QTcpSocket> +
-#include <QDataStream> +
-#include <QByteArray> +
-#include <QComboBox> +
-#include <QMessageBox> +
-#include <QHBoxLayout> +
-#include <QVBoxLayout> +
-#include <QTimer> +
-#include <QNetworkInterface> +
-#include <QGridLayout> +
-#include <QFile> +
-#include <QIODevice> +
-#include <QTextEdit>+
  
-class sc : public QDialog +Данные **пишутся** так же **пакетами**, как были записаны в **write()**, по одному после каждого **wait()**\\ 
-+Если нет коннекта, данные не уходят, **bytesToWrite()** не очищается, последующие вызовы **write()** возвращают **-1**, **wait()** возвращает **false**\\
-    Q_OBJECT+
  
-    QLabel *labPortServ; 
-    QLineEdit *editPort; 
-    QPushButton *butSendFile, *butConnectToServ; 
-    QTextEdit *editEventsLog; 
-    QTcpSocket *tcpSocket; 
  
-private slots: 
-    void slotDisplayError(QAbstractSocket::SocketError vSockError); 
-    void slotSendFile(); 
-    void slotConNocon(); 
-    void slotChangeState(QAbstractSocket::SocketState); 
  
-public: +sc.h 
-    sc(QWidget *parent = nullptr); +<code cpp-qt> 
-}; +
-#endif // SC_H+
 </code> </code>
  
 sc.cpp sc.cpp
 <code cpp-qt> <code cpp-qt>
-#include "sc.h" 
  
-sc::sc(QWidget *parent): QDialog(parent) +</code> 
-{ +</details>
-    this->setFont(QFont("Arial", 14));+
  
-    QGridLayout *layMain= new QGridLayout(this); 
-    layMain->setColumnStretch(0,1); 
-    layMain->setColumnStretch(1,1); 
  
-    labPortServ= new QLabel("Укажите порт:", this); 
-    editPort= new QLineEdit("2233", this); 
-    butConnectToServ= new QPushButton("Connect To Serv", this); 
-    butSendFile= new QPushButton("Отправить файл", this); 
-    editEventsLog= new QTextEdit(this); 
-    editEventsLog->setFont(QFont("Arial", 10)); 
  
-    layMain->addWidget(labPortServ, 0, 0, Qt::AlignRight); 
-    layMain->addWidget(editPort, 0, 1); 
-    layMain->addWidget(butConnectToServ, 0, 2); 
-    layMain->addWidget(butSendFile, 1, 0, 1, 3); 
-    layMain->addWidget(editEventsLog, 2, 0, 1, 3); 
  
-    tcpSocket= new QTcpSocket(this); 
-    connect(tcpSocket, &QTcpSocket::errorOccurred, this, &sc::slotDisplayError); 
-    connect(butConnectToServ, &QPushButton::clicked, this, &sc::slotConNocon); 
-    connect(butSendFile, &QPushButton::clicked, this, &sc::slotSendFile); 
-    connect(tcpSocket, &QTcpSocket::stateChanged, this, &sc::slotChangeState); 
-} 
  
- 
-void sc::slotSendFile() 
-{ 
-    QFile vFile("D:\\this-text_small.txt"); 
-    vFile.open(QIODevice::ReadOnly); 
-    QByteArray vBuffer; 
- 
-    QDataStream vOut(&vBuffer, QIODevice::WriteOnly); 
-    vOut.setVersion(QDataStream::Qt_4_0); 
-    vOut << "-=This Service String=-"; 
-    vOut << vFile.readAll(); 
- 
-    tcpSocket->write(vBuffer); 
-    editEventsLog->insertPlainText("\nFile is written"); 
-} 
- 
- 
-void sc::slotConNocon() 
-{ 
-    if(tcpSocket->state() == QAbstractSocket::ConnectedState) 
-    { 
-        tcpSocket->abort(); 
-        tcpSocket->close(); 
-    } 
-    else if (tcpSocket->state() == QAbstractSocket::UnconnectedState) 
-    { 
-        tcpSocket->abort(); 
-        tcpSocket->connectToHost(QHostAddress::LocalHost, editPort->text().toInt()); 
-    } 
-} 
- 
- 
-void sc::slotChangeState(QAbstractSocket::SocketState vSocket) 
-{ 
-    switch (vSocket) 
-    { 
-        case QAbstractSocket::UnconnectedState: 
-            editEventsLog->insertPlainText("\n0 The socket is not connected"); 
-            break; 
-        case QAbstractSocket::HostLookupState: 
-            editEventsLog->insertPlainText("\n1 The socket is performing a host name lookup"); 
-            break; 
-        case QAbstractSocket::ConnectingState: 
-            editEventsLog->insertPlainText("\n2 The socket has started establishing a connection"); 
-            break; 
-        case QAbstractSocket::ConnectedState: 
-            editEventsLog->insertPlainText("\n3 A connection is established"); 
-            break; 
-        case QAbstractSocket::BoundState: 
-            editEventsLog->insertPlainText("\n4 The socket is bound to an address and port"); 
-            break; 
-        case QAbstractSocket::ClosingState: 
-            editEventsLog->insertPlainText("\n6 The socket is about to close (data may still be waiting to be written)"); 
-            break; 
-        case QAbstractSocket::ListeningState: 
-            editEventsLog->insertPlainText("\n5 For internal use only"); 
-            break; 
-    } 
-} 
- 
- 
-void sc::slotDisplayError(QAbstractSocket::SocketError vSockError) 
-{ 
-    editEventsLog->insertPlainText("\n--== Error ==-- "+ tcpSocket->errorString()); 
-} 
-</code> 
-</details> 
  
  
develop/qt/network.1632992629.txt.gz · Последнее изменение: 2021/09/30 09:03 — admin