Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Simple program with QTcpServer and multiple QTcpSocket sockets - crash on program exit if sockets on server side are not disconnectFromHost

Simple program with QTcpServer and multiple QTcpSocket sockets - crash on program exit if sockets on server side are not disconnectFromHost

Scheduled Pinned Locked Moved Solved General and Desktop
5 Posts 2 Posters 1.0k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Matthew11M Offline
    Matthew11M Offline
    Matthew11
    wrote on last edited by Matthew11
    #1

    Hi,

    I have simple single-threaded program with QTcpServer that manages multiple clients.

    The idea is simple: when socket sends some ID packet, I bind that socket with that particular ID - ID is just an int - in vector of pairs {int, QTcpSocket* const} on the server side. Then when it's needed the data can be passed from client to client by server. When one client is disconnected then I want to remove that binding from the vector. Which I do on the disconnected() signal with freeing the memory after that socket (using deleteLater()).

    The problem: It crashes whenever the server closes (the program to be specific) while having connected clients. But when clients are first disconnected then everything works fine. The solution is just to disconnect all connected clients firstly in the server-wrapper destructor. When I comment out disconnectSockets(), it's crashing the vector object/memory or it's just the side effect - look at the count value of vector in dtor and when in slot onDisconnected - with info:

    virtual Server::~Server() Elements in vector: 2
    Server::makeConnections()::<lambda()> Disconnected. Elements in vector: 21845
    ASSERT: "asize >= 0 && asize <= aalloc" in file ../../Qt/5.12.5/gcc_64/include/QtCore/qvector.h, line 554
    
    // or
    
    virtual Server::~Server() Elements in vector: 2
    Server::makeConnections()::<lambda()> Disconnected. Elements in vector: 21845
    terminate called after throwing an instance of 'std::bad_alloc'
      what():  std::bad_alloc
    

    Question: What am i missing? Why the vector memory is affected in that case, and why it is mandatory to disconnect all clients first in that case? I don't see any note about it in the documentation. Just to note when no vectors, are used, then everything is ok.

    The program is a simple QtQuick application, in the UI is only one button, which onClicked simply closing program. The code:

    client.h

    #ifndef CLIENT_H
    #define CLIENT_H
    
    #include <QObject>
    #include <QTcpSocket>
    #include <QHostAddress>
    
    class Client : public QObject
    {
        Q_OBJECT
    public:
        Client(int _id) : id(_id) {}
    
        bool setup(const QString& _address, const quint16 _port)
        {
            socket.connectToHost(QHostAddress(_address), _port);
            return socket.waitForConnected(1000);
        }
    
        void send()
        {
            QByteArray _data(QString::number(id).toUtf8());
            socket.write(_data);
        }
    
    private:
        int id;
        QTcpSocket socket;
    };
    
    #endif // CLIENT_H
    

    server.h

    #ifndef SERVER_H
    #define SERVER_H
    
    #include <QObject>
    #include <QTcpSocket>
    #include <QTcpServer>
    #include <QVector>
    #include <QPair>
    #include <QHostAddress>
    
    class Server : public QObject
    {
        Q_OBJECT
    public:
        Server()
        {
            connect(&server, &QTcpServer::newConnection, [this](){ makeConnections(); });
        }
    
        ~Server()
        {
            qInfo() << Q_FUNC_INFO << "Elements in vector:" << bindings.count();
            //disconnectSockets(); // <-- when this line is commented, the program will crash
        }
    
        bool setup(const QString& _address, const quint16 _port)
        {
            return server.listen(QHostAddress(_address), _port);
        }
    
    private:
        QTcpServer server;
        QVector<QTcpSocket*> connectedSockets;
        QVector<QPair<int, QTcpSocket* const>> bindings;
    
        void makeConnections()
        {
            QTcpSocket* const _socket = server.nextPendingConnection();
            connectedSockets.append(_socket);
    
            connect(_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), [_socket](){
                qCritical() << Q_FUNC_INFO << "Socket error occurred: " << _socket->error();
            });
    
            connect(_socket, &QTcpSocket::disconnected, [_socket, this](){
                qInfo() << Q_FUNC_INFO << "Disconnected" << "Elements in vector:" << bindings.count();
                connectedSockets.removeOne(_socket);
                removeBinding(_socket);
                _socket->deleteLater();
            });
    
            connect(_socket, &QAbstractSocket::readyRead, [_socket, this](){ processData(_socket); });
        }
    
        void processData(QTcpSocket* const _socket)
        {
            const auto _data = _socket->readAll();
            bindings.append({QString(_data[0]).toInt(), _socket});
        }
    
        void removeBinding(QTcpSocket* const _socket)
        {
            for(const auto _binding : bindings)
            {
                if(_binding.second == _socket)
                {
                    qInfo() << Q_FUNC_INFO << (bindings.removeOne({_binding.first, _binding.second}) ? "Removed one" : "");
                }
            }
        }
    
        void disconnectSockets()
        {
            for(const auto _socket : connectedSockets)
            {
                _socket->disconnectFromHost();
            }
        }
    };
    
    #endif // SERVER_H
    

    main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QTimer>
    
    #include "client.h"
    #include "server.h"
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
        Server server;
        server.setup("127.0.0.1", 4444);
    
        Client clientA(0);
        clientA.setup("127.0.0.1", 4444);
        clientA.send();
    
        Client clientB(1);
        clientB.setup("127.0.0.1", 4444);
        clientB.send();
    
        QQmlApplicationEngine engine;
        engine.load("qrc:/main.qml");
    
        return app.exec();
    }
    

    pro

    QT += quick network
    
    CONFIG += c++11
    CONFIG -= gui
    
    DEFINES += QT_DEPRECATED_WARNINGS
    
    SOURCES += \
            main.cpp
    
    RESOURCES += qml.qrc
    
    HEADERS += \
        client.h \
        server.h
    
    1 Reply Last reply
    0
    • Christian EhrlicherC Online
      Christian EhrlicherC Online
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by Christian Ehrlicher
      #5

      Ok, now you removed the line so the second error steps in.
      Since you did not disconnect the signal and using the 3-arg connect() function Qt can not know that the receiver is no longer alive and will access the deleted object.

      See also https://github.com/KDE/clazy/blob/master/docs/checks/README-connect-3arg-lambda.md

      And it's also in the docs: https://doc.qt.io/qt-5/qobject.html#connect-4

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      1 Reply Last reply
      2
      • Christian EhrlicherC Online
        Christian EhrlicherC Online
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote on last edited by
        #2

        So does your current version crash or not?
        When you take a look at the backtrace you will see that you're modifying the vector while iterating over it I would guess.

        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
        Visit the Qt Academy at https://academy.qt.io/catalog

        1 Reply Last reply
        0
        • Matthew11M Offline
          Matthew11M Offline
          Matthew11
          wrote on last edited by Matthew11
          #3

          @Christian-Ehrlicher said:

          So does your current version crash or not?

          I edited just a little previous post, to explicitly point where is the problem (server.h):

          ~Server()
          {
              qInfo() << Q_FUNC_INFO << "Elements in vector:" << bindings.count();
              //disconnectSockets(); // <-- when this line is commented, the program will crash
          }
          

          When uncommented works like a charm, when commented it crashes in:

          connect(_socket, &QTcpSocket::disconnected, [_socket, this](){
              qInfo() << Q_FUNC_INFO << "Disconnected" << "Elements in vector:" << bindings.count(); // here the vector memory is already corrupted
              connectedSockets.removeOne(_socket);
              removeBinding(_socket);
              _socket->deleteLater();
          });
          
          1 Reply Last reply
          0
          • Christian EhrlicherC Online
            Christian EhrlicherC Online
            Christian Ehrlicher
            Lifetime Qt Champion
            wrote on last edited by
            #4

            I already told you why it crashes when you don't disconnect the signals before looping over the sockets to close them.

            Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
            Visit the Qt Academy at https://academy.qt.io/catalog

            1 Reply Last reply
            0
            • Christian EhrlicherC Online
              Christian EhrlicherC Online
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on last edited by Christian Ehrlicher
              #5

              Ok, now you removed the line so the second error steps in.
              Since you did not disconnect the signal and using the 3-arg connect() function Qt can not know that the receiver is no longer alive and will access the deleted object.

              See also https://github.com/KDE/clazy/blob/master/docs/checks/README-connect-3arg-lambda.md

              And it's also in the docs: https://doc.qt.io/qt-5/qobject.html#connect-4

              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
              Visit the Qt Academy at https://academy.qt.io/catalog

              1 Reply Last reply
              2

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved