Показаны различия между двумя версиями страницы.
Предыдущая версия справа и слева Предыдущая версия Следующая версия | Предыдущая версия | ||
develop:qt:network [2021/09/24 06:01] admin |
develop:qt:network [2021/10/27 04:33] (текущий) admin |
||
---|---|---|---|
Строка 8: | Строка 8: | ||
===== QTcpServer ===== | ===== QTcpServer ===== | ||
- | ----- | ||
Позволяет принимать Tcp-соединения, | Позволяет принимать Tcp-соединения, | ||
Строка 20: | Строка 19: | ||
===== QAbstractSocket ===== | ===== QAbstractSocket ===== | ||
- | ----- | ||
{{ : | {{ : | ||
Строка 79: | Строка 77: | ||
----- | ----- | ||
+ | |||
+ | |||
+ | ===== QHostAddress ===== | ||
+ | ----- | ||
+ | Класс содержит адреса v4/v6.\\ | ||
+ | Адрес устанавливается и извлекается, | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ===== Пример клиент/ | ||
+ | |||
+ | ==== Сервер ==== | ||
+ | ----- | ||
+ | Сервер на базе **QTcpServer**, | ||
+ | При приеме входящего подключения отправляет клиенту указанную инфу и закрывает соединение.\\ | ||
+ | Данные **передаются в открытом виде** \\ | ||
+ | |||
+ | При приеме соединения вызывается сигнал **NewConnection()**, | ||
+ | Внутри создаем объект пользовательского подключения, | ||
+ | Запись делаем методом **write()**, | ||
+ | Пакеты по 60-65 кБ.\\ | ||
+ | |||
+ | В данном примере открываем файл и заносим его содержимое в массив **QByteArray**, | ||
+ | Далее передаем получившийся **QByteArray** в метод **write()**, | ||
< | < | ||
- | < | + | < |
+ | Основан на примере " | ||
+ | sserv.h | ||
<code cpp-qt> | <code cpp-qt> | ||
+ | #ifndef SSERV_H | ||
+ | #define SSERV_H | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | class sserv : public QDialog | ||
+ | { | ||
+ | Q_OBJECT | ||
+ | |||
+ | QLabel *labStatusText; | ||
+ | QTcpServer *tcpServer; | ||
+ | // | ||
+ | |||
+ | private slots: | ||
+ | void slotSendFortune(); | ||
+ | |||
+ | public: | ||
+ | sserv(QWidget *parent = nullptr); | ||
+ | |||
+ | }; | ||
+ | #endif // SSERV_H | ||
+ | </ | ||
+ | |||
+ | sserv.cpp | ||
+ | <code cpp-qt> | ||
+ | #include " | ||
+ | |||
+ | sserv:: | ||
+ | { | ||
+ | tcpServer= new QTcpServer(this); | ||
+ | if(!tcpServer-> | ||
+ | { | ||
+ | QMessageBox:: | ||
+ | this-> | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | QString vIpAddr; | ||
+ | QList< | ||
+ | for(int i= 0; i < vListIp.size(); | ||
+ | { | ||
+ | if(vListIp.at(i) != QHostAddress:: | ||
+ | { | ||
+ | vIpAddr= vListIp.at(i).toString(); | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | if(vIpAddr.isEmpty()) | ||
+ | vIpAddr= QHostAddress(QHostAddress:: | ||
+ | |||
+ | this-> | ||
+ | labStatusText= new QLabel(this); | ||
+ | labStatusText-> | ||
+ | |||
+ | QBoxLayout *layCenter= new QBoxLayout(QBoxLayout:: | ||
+ | layCenter-> | ||
+ | |||
+ | |||
+ | this-> | ||
+ | |||
+ | connect(this-> | ||
+ | } | ||
+ | |||
+ | |||
+ | void sserv:: | ||
+ | { | ||
+ | // Тут была передача строки из массива " | ||
+ | / | ||
+ | QDataStream vOutStreem(& | ||
+ | vOutStreem.setVersion(QDataStream:: | ||
+ | vOutStreem << this-> | ||
+ | |||
+ | QTcpSocket *vClientConnection= this-> | ||
+ | connect(vClientConnection, | ||
+ | |||
+ | vClientConnection-> | ||
+ | vClientConnection-> | ||
+ | |||
+ | // Передаем указанный файл | ||
+ | QFile vFile(" | ||
+ | vFile.open(QIODevice:: | ||
+ | |||
+ | QByteArray vBlockData= vFile.readAll(); | ||
+ | |||
+ | QTcpSocket *vClientConnection= this-> | ||
+ | connect(vClientConnection, | ||
+ | vClientConnection-> | ||
+ | vClientConnection-> | ||
+ | } | ||
</ | </ | ||
</ | </ | ||
+ | ==== Клиент ==== | ||
+ | ----- | ||
+ | Клиент сам подключается к серверу и сервер сразу же начинает передачу. На клиенте просто обрабатываем сигнал **readyRead()**.\\ | ||
+ | |||
+ | Объект сокета должен быть глобальным, | ||
+ | Читаем в **QByteArray** и далее работаем с ними. Если сохраняем в файл (как в примере) то необходимо открывать его в режиме **добавления**, | ||
+ | |||
+ | |||
+ | < | ||
+ | < | ||
+ | sc.h | ||
+ | <code cpp-qt> | ||
+ | #ifndef SC_H | ||
+ | #define SC_H | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | class sc : public QDialog | ||
+ | { | ||
+ | Q_OBJECT | ||
+ | |||
+ | QComboBox *comboHostServ; | ||
+ | QLineEdit *editPortServ; | ||
+ | QLabel *labComboCapt, | ||
+ | QPushButton *butGetFortune, | ||
+ | |||
+ | QTcpSocket *tcpSocket; | ||
+ | QDataStream VInputStream; | ||
+ | QString VCurrFortune; | ||
+ | |||
+ | private slots: | ||
+ | void slotRequestNewFortune(); | ||
+ | void slotReadFortune(); | ||
+ | void slotDisplayError(QAbstractSocket:: | ||
+ | |||
+ | public: | ||
+ | sc(QWidget *parent = nullptr); | ||
+ | }; | ||
+ | #endif // SC_H | ||
+ | </ | ||
+ | |||
+ | sc.cpp | ||
+ | <code cpp-qt> | ||
+ | #include " | ||
+ | |||
+ | sc:: | ||
+ | { | ||
+ | this-> | ||
+ | QGridLayout *layMainGrid= new QGridLayout(this); | ||
+ | layMainGrid-> | ||
+ | layMainGrid-> | ||
+ | |||
+ | labComboCapt= new QLabel(" | ||
+ | comboHostServ= new QComboBox(this); | ||
+ | comboHostServ-> | ||
+ | layMainGrid-> | ||
+ | layMainGrid-> | ||
+ | |||
+ | labEditCapt= new QLabel(" | ||
+ | editPortServ= new QLineEdit(this); | ||
+ | editPortServ-> | ||
+ | layMainGrid-> | ||
+ | layMainGrid-> | ||
+ | |||
+ | labStatus= new QLabel(" | ||
+ | layMainGrid-> | ||
+ | |||
+ | butGetFortune= new QPushButton(" | ||
+ | butQuit= new QPushButton(" | ||
+ | layMainGrid-> | ||
+ | layMainGrid-> | ||
+ | |||
+ | layMainGrid-> | ||
+ | |||
+ | tcpSocket= new QTcpSocket(this); | ||
+ | VInputStream.setDevice(tcpSocket); | ||
+ | VInputStream.setVersion(QDataStream:: | ||
+ | |||
+ | connect(tcpSocket, | ||
+ | connect(tcpSocket, | ||
+ | connect(butGetFortune, | ||
+ | connect(butQuit, | ||
+ | } | ||
+ | |||
+ | |||
+ | void sc:: | ||
+ | { | ||
+ | butGetFortune-> | ||
+ | tcpSocket-> | ||
+ | tcpSocket-> | ||
+ | } | ||
+ | |||
+ | |||
+ | void sc:: | ||
+ | { | ||
+ | // Тут было получение строки, | ||
+ | / | ||
+ | QString vNextFortune; | ||
+ | VInputStream >> vNextFortune; | ||
+ | |||
+ | if(!VInputStream.commitTransaction()) | ||
+ | return; | ||
+ | |||
+ | if(vNextFortune== VCurrFortune) | ||
+ | { | ||
+ | QTimer:: | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | VCurrFortune= vNextFortune; | ||
+ | labStatus-> | ||
+ | butGetFortune-> | ||
+ | |||
+ | // Получение файла | ||
+ | QByteArray vGettingData; | ||
+ | vGettingData= tcpSocket-> | ||
+ | |||
+ | QFile vFile(" | ||
+ | vFile.open(QIODevice:: | ||
+ | vFile.write(vGettingData); | ||
+ | vFile.close(); | ||
+ | |||
+ | |||
+ | butGetFortune-> | ||
+ | } | ||
+ | |||
+ | |||
+ | void sc:: | ||
+ | { | ||
+ | switch (vSockError) | ||
+ | { | ||
+ | case QAbstractSocket:: | ||
+ | break; | ||
+ | case QAbstractSocket:: | ||
+ | QMessageBox:: | ||
+ | break; | ||
+ | case QAbstractSocket:: | ||
+ | QMessageBox:: | ||
+ | break; | ||
+ | default: | ||
+ | QMessageBox:: | ||
+ | break; | ||
+ | } | ||
+ | butGetFortune-> | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ===== Еще пример клиент/ | ||
+ | Сервер поддерживает **множественное подключение**, | ||
+ | |||
+ | При передаче, | ||
+ | **Программные** формируются при каждом вызове метода **write()**, | ||
+ | |||
+ | **Физические** формируются уже на транспортном уровне, | ||
+ | |||
+ | Передача ведется потоковая, | ||
+ | |||
+ | Служебную инфу пишем с помощью датаСтрим, | ||
+ | |||
+ | Cчитывание 4ех байт остается корректным до тех пор пока программный пакет < 4gb, т.к. в эти 4 байта умещено интовое значение, | ||
+ | Если программный пакет будет больше, | ||
+ | |||
+ | < | ||
+ | < | ||
+ | |||
+ | |||
+ | sserv.h | ||
+ | <code cpp-qt> | ||
+ | |||
+ | </ | ||
+ | |||
+ | sserv.cpp | ||
+ | <code cpp-qt> | ||
+ | |||
+ | </ | ||
+ | </ | ||
+ | |||
+ | |||
+ | < | ||
+ | < | ||
+ | **Примечания: | ||
+ | :?: Установка соединения (**connectToHost()**) проходит асинхронно, | ||
+ | Правда в случае ошибки соединения, | ||
+ | :?: Запись можно делать из файла сразу в метод **write()**, | ||
+ | **write()** принимает **QByteArray**, | ||
+ | :?: Что касается **проверки и установления** коннекта: | ||
+ | в нашем случае сокет может быть в след состояниях: | ||
+ | **0** - не подключен - нужно **вызвать коннект**\\ | ||
+ | **1,2** - выполняет поиск и начал устанавливать соединение - соединения еще нет, но и делать вроде ничего не надо, правда неизвестно может ли оно быть зависшим в этот состоянии ?, думаю тут- **ничего не делать**\\ | ||
+ | **3** - установленно - **ничего не делать**\\ | ||
+ | **6** - вот вот закроется - т.е. запрошен дисконнект, | ||
+ | |||
+ | Что касается отправки файла, пишем данные кусками в метод **write()**, | ||
+ | В случае ошибки, | ||
+ | Так же, после вызова **write()** и до фактической отправки данных нужно какое то время, до этого данные копятся в буфере (видно в **bytesToWrite()**), | ||
+ | |||
+ | Данные **пишутся** так же **пакетами**, | ||
+ | Если нет коннекта, | ||
+ | |||
+ | |||
+ | |||
+ | sc.h | ||
+ | <code cpp-qt> | ||
+ | |||
+ | </ | ||
+ | |||
+ | sc.cpp | ||
+ | <code cpp-qt> | ||
+ | |||
+ | </ | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | < | ||
+ | < | ||
+ | <code cpp-qt> | ||
+ | |||
+ | </ | ||
+ | </ |