QTcpSockets added to QList seem to disappear from list



  • I am creating a QTcpServer to send a MJPEG stream to clients that connect to the server. When I get a newConnection signal from the server I add the QTcpSocket to a QList. Then a different function gets frames and looks to see if the QList is non-empty. The problem is that even after I get a connection the QList from the other function's perspective remains empty.

    I setup the QTcpServer like this:

    server = new QTcpServer(this);
    
    connect(server, SIGNAL(newConnection()), this, SLOT(newConnection()));
    

    Then on each new connection:

    void 
    MjpegServer::newConnection() {
    
        if(!socketMutex.tryLock(1000)) {
            qCritical() << "Could not lock sendFrame Mutex to add new client";
            return;
        }
    
        while(server->hasPendingConnections()) {
            qInfo() << "Got new MJPEG Connection";
            m_clients.append(server->nextPendingConnection());
    
            QByteArray ContentType = ("HTTP/1.0 200 OK\r\n" \
                "Cache-Control: no-cache\r\n" \
                "Pragma: no-cache\r\n" \
                "Connection: close\r\n" \
                "Content-Type: multipart/x-mixed-replace; boundary=mjpegstream\r\n\r\n");
            m_clients.last()->write(ContentType);
            m_clients.last()->flush();
            m_clients.last()->waitForBytesWritten(3000);
    
            connect(m_clients.last(), &QTcpSocket::disconnected, this, &MjpegServer::socketDisconnected);
            connect(m_clients.last(), SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
    
            qInfo() << "Clients now connected: " + QString::number(m_clients.count());
        }
    
        socketMutex.unlock();
    }
    

    When I run the code and a client connects, I get a printout saying that m_clients.count() is 1. I have a function that sends video frames:

    void
    MjpegServer::sendFrame(Mat &img) {
    
        if(!socketMutex.tryLock(1000)) {
            qCritical() << "Could not lock sendFrame Mutex";
            return;
        }
    
        // Only encode if there are connected clients
        if(m_clients.count() == 0) {
            socketMutex.unlock();
            qInfo() << "No clients";
            return;
        }
    
        qInfo() << "Encoding image";
        std::vector<uchar> outbuf;
        std::vector<int> params;
        params.push_back(CV_IMWRITE_JPEG_QUALITY);
        params.push_back(100);
        imencode(".jpg", img, outbuf, params);
    
        std::string content(outbuf.begin(), outbuf.end());
        QByteArray CurrentImg(QByteArray::fromStdString(content));
        QByteArray BoundaryString = ("--mjpegstream\r\nContent-Type: image/jpeg\r\nContent-Length: ");
        BoundaryString.append(QString::number(CurrentImg.length()));
        BoundaryString.append("\r\n\r\n");
    
        for( int i=0; i < m_clients.count(); ++i ) {
            qInfo() << "Writing data to client " + QString::number(i);
            QTcpSocket* socket = m_clients.at(i);
            socket->write(BoundaryString);
            socket->write(CurrentImg); // Write The Encoded Image
            socket->flush();
        }
        socketMutex.unlock();
    }
    

    Right now this only ever prints "No clients". Even after a client has connected. I also have signals connected to monitor when QTcpSockets are disconnected or their state changes, these signals do not fire. The QTcpSockets connect, seem not to disconnect, but the sendFrame function cannot see them in the QList.

    Any help would be appreciated...


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Any chances that you have cleanup functions that is called at some point earlier than you thought ?



  • It definitely feels like something along those lines. I do not see a place where this might be occurring. I'm hoping another set of eyes might be able to see what I do not:

    #include "MjpegServer.h"
    
    MjpegServer::MjpegServer(QObject *parent, int port) :
      QObject(parent), port(port) 
    {
      server = new QTcpServer(this);
    
      connect(server, SIGNAL(newConnection()), this, SLOT(newConnection()));
    
      if (!server->listen(QHostAddress::Any, port))
          qCritical() << "Server NOT started";
      else
          qInfo() << "MJPEG Server started on port " + QString::number(port);
    }
    
    MjpegServer::~MjpegServer() {
    }
    
    void 
    MjpegServer::newConnection() {
    
        if(!socketMutex.tryLock(1000)) {
            qCritical() << "Could not lock sendFrame Mutex to add new client";
            return;
        }
    
        while(server->hasPendingConnections()) {
            qInfo() << "Got new MJPEG Connection";
            m_clients.append(server->nextPendingConnection());
    
            connect(m_clients.last(), SIGNAL(readyRead()), this, SLOT(writeHeader()));
            connect(m_clients.last(), &QTcpSocket::disconnected, this, &MjpegServer::socketDisconnected);
            connect(m_clients.last(), SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
    
            qInfo() << "Clients now connected: " + QString::number(m_clients.count());
        }
    
        socketMutex.unlock();
    }
    
    void
    MjpegServer::socketDisconnected() {
      QTcpSocket *pClient = qobject_cast<QTcpSocket *>(sender());
      qInfo() << "MJPEG socket Disconnected:" << pClient;
      if (pClient) {
        m_clients.removeAll(pClient);
        pClient->deleteLater();
      }
    }
    
    void
    MjpegServer::onSocketStateChanged(QAbstractSocket::SocketState socketState)
    {
        qInfo() << "MJPEG socket state changed to " + QString::number(socketState);
        if (socketState == QAbstractSocket::UnconnectedState)
        {
            QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
            m_clients.removeOne(sender);
        }
    }
    
    void
    MjpegServer::writeHeader() {
      QTcpSocket *pClient = qobject_cast<QTcpSocket *>(sender());
    
      qInfo() << "Writing MJPEG header info";
      QByteArray ContentType = ("HTTP/1.0 200 OK\r\n" \
          "Cache-Control: no-cache\r\n" \
          "Pragma: no-cache\r\n" \
          "Connection: close\r\n" \
          "Content-Type: multipart/x-mixed-replace; boundary=mjpegstream\r\n\r\n");
      pClient->write(ContentType);
      pClient->flush();
    }
    
    void
    MjpegServer::sendFrame(Mat &img) {
    
        if(!socketMutex.tryLock(1000)) {
            qCritical() << "Could not lock sendFrame Mutex";
            return;
        }
    
        // Only encode if there are connected clients
        if(m_clients.count() == 0) {
            socketMutex.unlock();
            qInfo() << "No clients";
            return;
        }
    
        qInfo() << "Encoding image";
        std::vector<uchar> outbuf;
        std::vector<int> params;
        params.push_back(CV_IMWRITE_JPEG_QUALITY);
        params.push_back(100);
        imencode(".jpg", img, outbuf, params);
    
        std::string content(outbuf.begin(), outbuf.end());
        QByteArray CurrentImg(QByteArray::fromStdString(content));
        QByteArray BoundaryString = ("--mjpegstream\r\nContent-Type: image/jpeg\r\nContent-Length: ");
        BoundaryString.append(QString::number(CurrentImg.length()));
        BoundaryString.append("\r\n\r\n");
    
        for( int i=0; i < m_clients.count(); ++i ) {
            qInfo() << "Writing data to client " + QString::number(i);
            QTcpSocket* socket = m_clients.at(i);
            socket->write(BoundaryString);
            socket->write(CurrentImg); // Write The Encoded Image
            socket->flush();
        }
        socketMutex.unlock();
    }
    

    The header file:

    #ifndef MJPEGSERVER_H
    #define MJPEGSERVER_H
    
    #include <iostream>
    #include <string>
    #include <memory>
    #include "stdint.h"
    #include <stdio.h>
    #include <time.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    
    #include "opencv2/opencv.hpp"
    #include "opencv2/core/core.hpp"
    
    #include <QtCore/QDebug>
    #include <QtCore/QCoreApplication>
    #include <QtConcurrent/QtConcurrent>
    #include <QTcpSocket>
    #include <QTcpServer>
    
    using namespace std;
    using namespace cv;
    
    class MjpegServer : public QObject {
      Q_OBJECT
    public:
      MjpegServer(QObject *parent = 0, int port = 8080);
      ~MjpegServer();
    
    Q_SIGNALS:
    
    public Q_SLOTS:
      void sendFrame(Mat &img);
    
    private Q_SLOTS:
      void newConnection();
      void socketDisconnected();
      void onSocketStateChanged(QAbstractSocket::SocketState);
      void writeHeader();
    
    private:
      QTcpServer* server;
      int port = 8080;
      QMutex socketMutex;
      QList<QTcpSocket *> m_clients;
    };
    
    #endif // MJPEGSERVER_H
    


  • @black_spot1984 said in QTcpSockets added to QList seem to disappear from list:

    private:
    QTcpServer* server;
    int port = 8080;
    QMutex socketMutex;
    QList<QTcpSocket *> m_clients;

    It is bad form to declare member variables, but then not actually initialize them in the constructor.


  • Qt Champions 2018

    @Kent-Dorfman I'm fully with you, but to be fair the only member that would need an init here is server, which I'd set to nullptr.

    The class-type variables init themself by their ctor.

    Regards



  • Thank you for taking the time to reply. I did change the initializer in the header file. I wouldn't expect that to make a difference. In testing it did not. The output is still:

    No clients
    No clients
    No clients
    No clients
    No clients
    No clients
    Got new MJPEG Connection
    "Clients now connected: 1"
    Writing MJPEG header info
    No clients
    No clients
    ...
    No clients
    

    I can't think why this behavior might be happening.



  • If anyone is interested, the problem turned out to be that I was trying to run this server from outside the main thread. When run from within the main thread this code works.



  • @black_spot1984 glad you found out and thanks for sharing. If your issue is solved, please don't forget to mark your post as such.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.