- 1. QLocalServer
- 2. QLocalSocket
Der Artikel beschreibt die Verwendung von QLocalServer und QLocalSocket. Das Beispiel ist eine Überarbeitung des Codes aus Schlees Buch Qt 5.3. Professionelle C++-Programmierung“ für QTcpServer bzw. QTcpSocket. Trotz der Tatsache, dass die Klassennamen ähnlich sind und im selben Modul verwendet werden, gibt es einige signifikante Unterschiede. Wir werden sie im Laufe der Präsentation berücksichtigen.
QLocalServer und QLocalSocket implementieren Named-Pipe- oder Unix-Domain-Socket-Mechanismen. Mehr darüber können Sie [hier] D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%B0%D0%BD%D0%B0%D0%BB) und hier .
Das Beispiel besteht aus zwei Teilen. Die erste implementiert QLocalServer, die zweite implementiert QlocalSocket.
Der Server implementiert die folgende Funktionalität: Auf dem Monitor wird ein Widget mit einem Textfeld angezeigt, das von außen kommende Informationen aus dem Socket anzeigt. Der Server wartet auf eingehende Verbindungen und sendet bei erfolgreicher Verbindung eine Nachricht an den Socket darüber. Außerdem leitet der Server Informationen weiter, die von dem Socket an den Socket übertragen werden.
Der Socket wiederum zeigt ein Widget mit einem Textfeld und einem Button auf dem Monitor an. Das Textfeld zeigt Informationen, die vom Server kommen, und Dienstinformationen des Sockets selbst (Erkennung einer Verbindung zu Beginn einer Sitzung oder ein Fehler beim Herstellen einer Verbindung zum Server, wenn dieser nicht verfügbar ist). Wenn eine Schaltfläche gedrückt wird, sendet der Socket Informationen an den Server.
Schauen wir uns den Beispielcode genauer an.
QLocalServer
# QLocalServer.pro # Кроме модулей core gui подключаем network QT += core gui network greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = QLocalServer TEMPLATE = app DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ main.cpp \ mylocalserver.cpp HEADERS += \ mylocalserver.h
// main.cpp #include "mylocalserver.h" #include <QApplication> int main(int argc, char* argv[]) { QApplication app(argc, argv); // Создаём и показываем объект класса MyLocalServer, определённого далее, // для запуска сервера с именем "MyLocalServer" MyLocalServer server("MyLocalServer"); server.show(); return app.exec(); }
// mylocalserver.h #ifndef MYLOCALSERVER_H #define MYLOCALSERVER_H #include <QWidget> #include <QLocalServer> #include <QLocalSocket> #include <QTextEdit> class MyLocalServer : public QWidget { Q_OBJECT public: MyLocalServer(QString serverName, QWidget* parent = 0); ~MyLocalServer(); private: // Указатель на QLocalServer QLocalServer* localServer; // Указатель на QTextEdit, в котором будет показываться поступающая // от клиента информация QTextEdit* textEdit; // Переменная для хранения размера получаемого от клиента блока quint16 nextBlockSize; // Метод для отправки клиенту подтверждения о приёме информации void sendToClient(QLocalSocket* localSocket, const QString& string); public slots: // Слот обработки нового клиентского подключения virtual void slotNewConnection(); // Слот чтения информации от клиента void slotReadClient(); }; #endif // MYLOCALSERVER_H
// mylocalserver.cpp #include "mylocalserver.h" #include <QVBoxLayout> #include <QMessageBox> #include <QLabel> #include <QTime> MyLocalServer::MyLocalServer(QString serverName, QWidget* parent) : QWidget(parent), nextBlockSize(0) // Устанавливаем nextBlockSize равным нулю { // Создаём и запускаем сервер командой listen. // Если сервер не может быть запущен, выдать сообщение об ошибке и завершить работу программы localServer = new QLocalServer(this); if(!localServer->listen(serverName)) { QMessageBox::critical(0, "Server error", "Unable to start server:" + localServer->errorString()); localServer->close(); return; } // Соединяем сигнал сервера о наличии нового подключения с обработчиком нового клиентского подключения connect(localServer, SIGNAL(newConnection()), this, SLOT(slotNewConnection())); // Формируем окно для просмотра текстовых сообщений от клиента textEdit = new QTextEdit; textEdit->setReadOnly(true); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(new QLabel(serverName)); layout->addWidget(textEdit); setLayout(layout); } MyLocalServer::~MyLocalServer() { } // Слот обработки нового клиентского подключения void MyLocalServer::slotNewConnection() { // Получаем сокет, подключённый к серверу QLocalSocket* localSocket = localServer->nextPendingConnection(); // Соединяем сигнал отключения сокета с обработчиком удаления сокета connect(localSocket, SIGNAL(disconnected()), localSocket, SLOT(deleteLater())); // Соединяем сигнал сокета о готовности передачи данных с обработчиком данных connect(localSocket, SIGNAL(readyRead()), this, SLOT(slotReadClient())); // Отправляем информацию клиенту о соединении с сервером sendToClient(localSocket, "Server response: Connected!"); } // Слот чтения информации от клиента void MyLocalServer::slotReadClient() { // Получаем QLocalSocket после срабатывания сигнала о готовности передачи данных QLocalSocket* localSocket = (QLocalSocket*)sender(); // Создаём входной поток получения данных на основе сокета QDataStream in(localSocket); // Устанавливаем версию сериализации данных потока. У клиента и сервера они должны быть одинаковыми in.setVersion(QDataStream::Qt_5_3); // Бесконечный цикл нужен для приёма блоков данных разных размеров, от двух байт и выше for(;;) { // Если размер блока равен нулю if(!nextBlockSize) { // Если размер передаваемого блока меньше двух байт, выйти из цикла if(localSocket->bytesAvailable() < (int)sizeof(quint16)) break; // Извлекаем из потока размер блока данных in >> nextBlockSize; } // Извлекаем из потока время и строку QTime time; QString string; in >> time >> string; // Преобразуем полученные данные и показываем их в виджете QString message = time.toString() + " " + "Client has sent - " + string; textEdit->append(message); nextBlockSize = 0; // Отправляем ответ клиенту sendToClient(localSocket, "Server response: received \"" + string + "\""); } } // Метод для отправки клиенту подтверждения о приёме информации void MyLocalServer::sendToClient(QLocalSocket* localSocket, const QString& string) { // Поскольку заранее размер блока неизвестен (параметр string может быть любой длины), // вначале создаём объект array класса QByteArray QByteArray array; // На его основе создаём выходной поток QDataStream out(&array, QIODevice::WriteOnly); // Устанавливаем версию сериализации данных потока out.setVersion(QDataStream::Qt_5_3); // Записываем в поток данные для отправки. На первом месте идёт нулевой размер блока out << quint16(0) << QTime::currentTime() << string; // Перемещаем указатель на начало блока out.device()->seek(0); // Записываем двухбайтное значение действительного размера блока без учёта пересылаемого размера блока out << quint16(array.size() - sizeof(quint16)); // Отправляем получившийся блок клиенту localSocket->write(array); }
QLocalSocket
# QLocalSocket.pro # Кроме модулей core gui подключаем network QT += core gui network greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = QLocalSocket TEMPLATE = app DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ main.cpp \ mylocalsocket.cpp HEADERS += \ mylocalsocket.h
// main.cpp #include "mylocalsocket.h" #include <QApplication> int main(int argc, char* argv[]) { QApplication app(argc, argv); // Создаём и показываем объект класса MyLocalSocket, определённого далее, // для запуска клиента, подключаемого к серверу с именем "MyLocalServer" MyLocalSocket socket("MyLocalServer"); socket.show(); return app.exec(); }
// mylocalsocket.h #ifndef MYLOCALSOCKET_H #define MYLOCALSOCKET_H #include <QWidget> #include <QLocalSocket> #include <QTextEdit> #include <QPushButton> class MyLocalSocket : public QWidget { Q_OBJECT public: MyLocalSocket(QString serverName, QWidget* parent = 0); ~MyLocalSocket(); private: // Указатель на QLocalSocket QLocalSocket* localSocket; // Указатели на элементы интерфейса QTextEdit* textEdit; QPushButton* sendRevision; // Размер принимаемого от сервера блока quint16 nextBlockSize; // Номер ревизии, отправляемый серверу // Увеличивается при каждом нажатии QPushButton int revision; private slots: // Слот чтения информации, получаемой от сервера void slotReadyRead(); // Слот обработки ошибок сокета void slotError(QLocalSocket::LocalSocketError error); // Слот передачи информации на сервер void slotSendToServer(); // Слот обработки сигнала соединения с сервером void slotConnected(); }; #endif // MYLOCALSOCKET_H
// mylocalsocket.cpp #include "mylocalsocket.h" #include <QVBoxLayout> #include <QLabel> #include <QTime> #include <QMessageBox> MyLocalSocket::MyLocalSocket(QString serverName, QWidget* parent) : QWidget(parent), nextBlockSize(0), revision(0) // Устанавливаем nextBlockSize и revision равными нулю { // Инициализируем сокет localSocket = new QLocalSocket(this); // Устанавливаем соединение между сигналом ошибки сокета с обработчиком ошибок connect(localSocket, QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error), this, &MyLocalSocket::slotError); // Устанавливаем имя сервера, к которому сокет должен подключаться localSocket->setServerName(serverName); // Устанавливаем соединение между сигналом подключения сокета к серверу // и обработчиком сигнала connect(localSocket, SIGNAL(connected()), SLOT(slotConnected())); // Соединяем сигнал сокета о готовности приёма данных данных с обработчиком данных connect(localSocket, SIGNAL(readyRead()), SLOT(slotReadyRead())); // Инициализируем элементы интерфейса textEdit = new QTextEdit; sendRevision = new QPushButton("Send next revision"); // Соединяем нажатие кнопки с обработчиком, передающим информацию о ревизии на сервер connect(sendRevision, SIGNAL(clicked()), this, SLOT(slotSendToServer())); // Настраиваем элементы интерфейса и формируем вид окна клиента textEdit->setReadOnly(true); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(new QLabel("Sender revisions")); layout->addWidget(textEdit); layout->addWidget(sendRevision); setLayout(layout); // Подключаем сокет к серверу localSocket->connectToServer(); } MyLocalSocket::~MyLocalSocket() { } // Слот чтения информации, получаемой от сервера void MyLocalSocket::slotReadyRead() { // Всё аналогично приёму информации на стороне сервера QDataStream in(localSocket); in.setVersion(QDataStream::Qt_5_3); for(;;) { if(!nextBlockSize) { if(localSocket->bytesAvailable() < (int)sizeof(quint16)) break; } in >> nextBlockSize; if(localSocket->bytesAvailable() < nextBlockSize) break; QTime time; QString string; in >> time >> string; textEdit->append(time.toString() + " " + string); nextBlockSize = 0; } } // Слот обработки ошибок сокета void MyLocalSocket::slotError(QLocalSocket::LocalSocketError error) { QString strError = "Error: " + (error == QLocalSocket::ServerNotFoundError ? "The server was not found." : error == QLocalSocket::PeerClosedError ? "The server is closed." : error == QLocalSocket::ConnectionRefusedError ? "The connection was refused." : QString(localSocket->errorString())); textEdit->append(strError); } // Слот передачи информации на сервер void MyLocalSocket::slotSendToServer() { // Блок для передачи формируется аналогично тому, как это делается на сервере QByteArray arrayBlock; QDataStream out(&arrayBlock, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_3); ++revision; QString message = "Revision: " + QString("%1").arg(revision); out << quint16(0) << QTime::currentTime() << message; out.device()->seek(0); out << quint16(arrayBlock.size() - sizeof(quint16)); localSocket->write(arrayBlock); } // Слот обработки сигнала соединения с сервером void MyLocalSocket::slotConnected() { textEdit->append("Received the connected() signal"); }
Das erste Merkmal ist, wie das Slot-Fehlersignal mit dem Fehlerbehandlungssignal für QLocalSocket verbunden ist: über QOverload.
connect(localSocket, QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error), this, &MyLocalSocket::slotError);
Das zweite Merkmal ist, dass für QLocalSocket diese Verbindung deklariert werden muss, bevor eine Verbindung zum Server hergestellt wird, während für QTcpSocket die Verbindung des Fehlersignals und des Fehlerbehandlungsslots nach der Verbindung mit dem Server deklariert werden kann. Weitere Informationen finden Sie im Forumsthema: https://evileg.com/en/forum/topic/965/ .
Als Ergebnis sollte der Monitor ein solches Bild haben.
Siehe GitHub: QLocalServer , QLocalSocket .
Остальные сигналы и слоты соединены между собой в старом стиле, так как фрагмент кода сокета, показанный ниже, компилировался с ошибками.