IscanderChe
IscanderCheJuly 19, 2019, 2:55 a.m.

Example Using QLocalServer and QLocalSocket

The article describes the use of QLocalServer and QLocalSocket. The example is a reworking of code from Schlee's book Qt 5.3. Professional C++ Programming” dedicated to QTcpServer and QTcpSocket respectively. Despite the fact that the class names are similar and are used in the same module, there are a couple of significant differences. We will consider them in the course of the presentation.

QLocalServer and QLocalSocket implement named pipe or Unix domain socket mechanisms. You can read more about this [here] D0%BD%D0%BD%D1%8B%D0%B9_%D0%BA%D0%B0%D0%BD%D0%B0%D0%BB) and here .

The example has two parts. The first implements QLocalServer, the second implements QlocalSocket.

The server implements the following functionality: a widget with a text field is displayed on the monitor, which will display information coming from the outside, from the socket. The server waits for incoming connections and, in case of a successful connection, sends a message to the socket about it. In addition, the server relays information transmitted from the socket to the socket.

In turn, the socket displays a widget with a text field and a button on the monitor. The text field displays information coming from the server and service information of the socket itself (detection of a connection at the beginning of a session or an error connecting to the server if it is unavailable). When a button is pressed, the socket sends information to the server.


Let's take a closer look at the example code.

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");
}

The first feature is how the slot error signal is connected to the error handling signal for QLocalSocket: via QOverload.

    connect(localSocket, QOverload<QLocalSocket::LocalSocketError>::of(&QLocalSocket::error),
        this, &MyLocalSocket::slotError);

The second feature is that for QLocalSocket this connection must be declared before connecting to the server, while for QTcpSocket the connection of the error signal and the error handling slot can be declared after connecting to the server. For more information, see the forum topic: https://evileg.com/en/forum/topic/965/ .

As a result, the monitor should have such a picture.

Код примеров на GitHub: QLocalServer , QLocalSocket .

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

IscanderChe
  • July 19, 2019, 2:57 a.m.

Остальные сигналы и слоты соединены между собой в старом стиле, так как фрагмент кода сокета, показанный ниже, компилировался с ошибками.

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!");
}

Comments

Only authorized users can post comments.
Please, Log in or Sign up
SH

C++ - Test 001. The first program and data types

  • Result:33points,
  • Rating points-10
г
  • ги
  • April 23, 2024, 3:51 p.m.

C++ - Test 005. Structures and Classes

  • Result:41points,
  • Rating points-8
l
  • laei
  • April 23, 2024, 9:19 a.m.

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:10points,
  • Rating points-10
Last comments
k
kmssrFeb. 8, 2024, 6:43 p.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 10:30 a.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 8:38 a.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
GvozdikDec. 18, 2023, 9:01 p.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
G
GarApril 22, 2024, 5:46 a.m.
Clipboard Как скопировать окно целиком в clipb?
DA
Dr Gangil AcademicsApril 20, 2024, 7:45 a.m.
Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
a
a_vlasovApril 14, 2024, 6:41 a.m.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел ДорофеевApril 14, 2024, 2:35 a.m.
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrexApril 4, 2024, 4:47 a.m.
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

Follow us in social networks