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...
-
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.
-
@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.