I have overloaded the incomingConnection function of QTcpServer at the server side with following code
void CtiServer::incomingConnection(int socketDescriptor)
{
qDebug() << QString("CtiServer thread id = %1, thread = %2").arg(QString::number((uint)QThread::currentThreadId(), 16)).arg(QString::number((qlonglong)QThread::currentThread(), 16));
ServerThread *thread = new ServerThread(socketDescriptor, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
Then in every thread i created a new QTcpSocket subclass object named ServerSocket,
void ServerThread::run()
{
qDebug() << QString("ServerThread thread id = %1, thread = %2").arg(QString::number((uint)QThread::currentThreadId(), 16)).arg(QString::number((qlonglong)QThread::currentThread(), 16));
serverSocket = new ServerSocket();
serverSocket->setSocketDescriptor(socketDescriptor);
connect(serverSocket, SIGNAL(readyRead()), serverSocket, SLOT(handleRequest()));
serverSocket->waitForDisconnected(-1);
}
The problem is whether serverSocket->waitForDisconnected(-1) is right or not.
I am trying to maintain the socket with this statement, util client send some message to notify the server socket that the connection should be shutdown, so that I could keep a long connection between server and client.
As a result, I could connect the disconnected signal and know when the client is down without bothering to set up some heart-beat protocol to detect whether a client is still alive or dead.
Or should I set up a heart-beat protocol and use code like following:
sock.disconnectFromHost();
sock.waitForDisconnected();
instead of sock.waitForDisconnected(-1).
Related
I'm moving my application from Qt 4.7 to Qt 6.3. In Qt 4.7 all works fine. In Qt 6.3 I have some issues when tcp server closes connection, I establish again connection, and I try to write data.
This is the function I use to write to socket:
void NetworkFacility::Write(QTcpSocket& oTCPSocket, const QVariant& oV)
{
//Controls Socket is valid
if (oTCPSocket.state() == QAbstractSocket::ConnectedState)
{
QByteArray block; //ByteArray to serialiaze object
MyPacket oPacket; //Packet to send
//Set Size of QVariant object
oPacket.setPacketSize(getQVariantSize(oV));
//Set QVariant
oPacket.setTransport(oV);
//Create stream to write on ByteArray
QDataStream out(&block, QIODevice::WriteOnly);
//Sets version
out.setVersion(QDataStream::Qt_4_7);
//Serializes
out << oPacket;
//TX to socket
oTCPSocket.write(block);
}
}
I manage disconnection this way:
void MyClient::remoteNodeDisconnected()
{
m_pTCPSocket->flush();
m_pTCPSocket->close();
}
void MyClient::ManagesTCPError(QAbstractSocket::SocketError socketError)
{
//Clears last packets
m_pTCPSocket->flush();
}
This is connection portion of code after disconnection:
m_pTCPSocket->connectToHost(m_sIpAddress, m_iIpPort);
//Waits connection
if (m_pTCPSocket->waitForConnected(MSEC_WAIT_FOR_CONNECTION))
{
//Print connected and exit from while loop
break;
}
Finally this is the way in which I manage the remote server connecte:
void MyClient::remoteNodeConnected()
{
//Improve Network latency on this connection
m_pTCPSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
}
The issue is that on the first connection all works fine. If the server disconnects (i.e. I umplugg the server cable in my LAN or I shutdown and restarts the server application) and then connects again the call to:
oTCPSocket.write(block);
in Networkfacility::Write method generates a crash.
Why the write method generates a crash after reconnection?
I fixed the issue by removing completely th oTCPSocket instance.
void MyClient::remoteNodeDisconnected()
{
m_pTCPSocket->flush();
m_pTCPSocket->close();
m_pTCPSocket.deleteLater();
m_pTCPSocket = 0;
}
I already saw several libraries for Server Sent Events, unfortunately, not for Qt. I also looked at the specification of SSE (just plain HTTP) and it seems that implementing SSE in Qt would require to:
Use QNetworkAccessManager in streaming mode (download)
Accept the content type header of SSE: application/events-stream
Reconnect when the connection is lost or closed
Attach a slot to the QNAM when new bytes are received (check for data : {...})
I'm not sure if it's so "easy"? Did I miss something?
I created a small demo with Qt and Server Sent Events.
The demo connects to a given EventSource URL (first argument) and prints every event to the command line.
Qt supports SSE out of the box since SSE is pure HTTP with a reconnection layer on top of it.
Prepare the request: set the text/event-stream accept header, allow redirects, disable the cache.
QNetworkRequest Network::Manager::prepareRequest(const QUrl &url)
{
QNetworkRequest request(url);
request.setRawHeader(QByteArray("Accept"), QByteArray(ACCEPT_HEADER));
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); // Events shouldn't be cached
return request;
}
Connect the readyRead signal to a slot.
void Network::Manager::getResource(const QUrl &url)
{
qDebug() << "GET resource:" << url;
QNetworkRequest request = this->prepareRequest(url);
m_reply = this->QNAM()->get(request);
connect(m_reply, SIGNAL(readyRead()), this, SLOT(streamReceived()));
}
Every time a new event is received by the QNetworkAccessManager, you can read it using readAll. We reset the retries counter after every successful event.
void Network::Manager::streamReceived()
{
qDebug() << "Received event from stream";
qDebug() << QString(m_reply->readAll()).simplified().replace("data: ", "");
qDebug() << "-----------------------------------------------------";
m_retries = 0;
}
In case we lost the connection or the connection times out, the finished() signal is triggered of the QNetworkAccessManager. We try to reconnect to the event source (We connected this slot to the signal when we created our QNetworkAccessManager instance):
void Network::Manager::streamFinished(QNetworkReply *reply)
{
qDebug() << "Stream finished:" << reply->url();
qDebug() << "Reconnecting...";
if(m_retries < MAX_RETRIES) {
m_retries++;
this->getResource(reply->url());
}
else {
qCritical() << "Unable to reconnect, max retries reached";
}
}
You can find the demo here: https://github.com/DylanVanAssche/Qt-Server-Sent-Events-Demo
I used QSerialPort to communicate with a serial port. But how to emit a signal when it is changed(connected or disconnected)?
OS is win7. Thanks.
I think I have a work-around for your problem (fixed mine at least).
I have a ComPort class that handles all my serial functionality of my GUI. When the user selected a COM-port, then a QTimer is started that checks the status periodically. When the timer is finished, the signal timeout() is emitted for which I wrote a custom slot:
// Custom slot to be called when timer times out.
void ComPort::checkComPortStatus()
{
QSerialPortInfo *info = new QSerialPortInfo;
QList<QSerialPortInfo> list = info->availablePorts();
bool port_still_available = false;
for (QSerialPortInfo &port : list) {
if (port.portName() == serial_port->portName())
port_still_available = true;
}
if (!port_still_available) {
qDebug() << "Selected COM-port," << serial_port->portName() << ", no longer available!";
delete serial_port;
serial_port = nullptr;
qDebug() << "COM-port is deleted.";
delete timer;
timer = new QTimer(this);
qDebug() << "COM-port timer is deleted.";
selectedPort_label->setText("Error: disconnected");
}
}
This, ofcourse, is only useful when the port is still closed. When the port is opened, a standard signal is emited by QT (please consult the documentation):
errorOccurred(QSerialPort::SerialPortError error)
I hope this helps!
My gui have a two button's, one to start a server & other to stop a server.
Brief :---
Once server is started, on every new client request i will create a new thread & this will handle communication with the client.
Detail :--
When start button is pressed, I am creating an object 'tcpserverobjectWrapper' which creates an another object 'tcpserverobject' this object creats an Qtcpserver.
Now this Qtcpserver is listing for new connection. And when a new connection comes i create an 'TcpSocketThreadWrapperObject' object which
creates a thread & this thread handles communication with client . Also 'tcpserverobject' keeps the list of new client request objects created
'QList<TcpSocketThreadWrapperObject *> TcpSocketThreadWrapperObjectList;' .
I am able to connect to server from telnet clients & it creates new thread for each client & works fine.
When stop button pressed i am able to stop server & client threads.
But i have two problems here :---
1> Everytime client send some data to server. I get this kind of QsocketNotifier. What is this ?
QSocketNotifier: socket notifiers cannot be enabled from another thread
QSocketNotifier: socket notifiers cannot be disabled from another thread
2> If i press stop button on GUI i am able to stop the threads succesfully.
But how to stop the threads & delete the objects created for every client when client send 'STOP command' to server or closes the connection with server ?
I will also have to delete the following objects created on each client request ?
client request --> TcpSocketThreadWrapperObject -- creates --> TcpSocketThreadObject -- creates --> TcpSocketThreadObject
Can someone suggest how to solve above two problems ? Reply on this will be appreciated.
Here is the code :---
================= start & stop buttons handler =====
void MainWindow::on_actionStop_triggered()
{
if(b_threadAlreadyStarted)
{
/* ------------------ Tcp server object ------------------------*/
b_threadAlreadyStarted = false;
delete p_tcpserverobjectWrapper;
/* ------------------ Tcp server object ------------------------*/
}
}
void MainWindow::on_actionStart_triggered()
{
if(!b_threadAlreadyStarted)
{
/* ------------------ Tcp server object ------------------------*/
b_threadAlreadyStarted =true;
p_tcpserverobjectWrapper = new tcpserverobjectWrapper(this,modelCANalyzer);
qDebug() << " \n start ";
/* ------------------ Tcp server object ------------------------*/
}
}
======== tcpserverobjectWrapper class ===============
// Main server object wrapper
class tcpserverobjectWrapper : public QObject
{
Q_OBJECT
public:
explicit tcpserverobjectWrapper(QMainWindow *ptrWidget, QStandardItemModel *modelCANalyzer, QObject *parent=0);
~tcpserverobjectWrapper();
//Device thread object
tcpserverobject *m_tcpserverobject;
};
tcpserverobjectWrapper::tcpserverobjectWrapper(QMainWindow *ptrWidget , QStandardItemModel *modelCANalyzer,QObject *parent) :
QObject(parent)
{
m_tcpserverobject = new tcpserverobject ;
//save model
m_tcpserverobject->modeltable = modelCANalyzer;
m_tcpserverobject->ptrmainwindow = ptrWidget;
qDebug() << "\n tcp server thread started";
}
tcpserverobjectWrapper::~tcpserverobjectWrapper()
{
qDebug() << " \n called delete later on tcpserverobjectWrapper .. !!";
m_tcpserverobject->deleteLater(); // ---------------------> change it to - delete m_tcpserverobject
qDebug() << " \n tcp server object successfully quited .. !! ";
}
========== tcpserverobject object ==================
class tcpserverobject : public QObject
{
Q_OBJECT
public:
explicit tcpserverobject(QObject *parent = 0);
~tcpserverobject();
/*!
Pointer to QStandardItemModel to be used inside - canTableView
*/
QStandardItemModel *modeltable;
//mainwindow pointer
QMainWindow *ptrmainwindow;
// Create list of new -- socket thread wrapper objects
QList<TcpSocketThreadWrapperObject *> TcpSocketThreadWrapperObjectList;
private:
QTcpServer *tcpServer;
signals:
public slots:
void on_newConnection();
};
tcpserverobject::tcpserverobject(QObject *parent) :
QObject(parent), tcpServer(0)
{
tcpServer = new QTcpServer;
// Connect slot of the server
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(on_newConnection()));
//lisen on socket
if (!tcpServer->listen(QHostAddress::LocalHost, SERVER_PORT )) {
qDebug() << "\n returning from server listning error .. !!! ";
return;
}
qDebug() << "\n server listning";
}
tcpserverobject::~tcpserverobject()
{
// to do
while (!TcpSocketThreadWrapperObjectList.isEmpty())
delete TcpSocketThreadWrapperObjectList.takeFirst();
}
void tcpserverobject::on_newConnection()
{
QByteArray block;
block.append(" \n Hello from server .. !!!") ;
QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
connect(clientConnection, SIGNAL(disconnected()),
clientConnection, SLOT(deleteLater()));
// Create new thread for this .. client request ..!!
qDebug() << "\n New connection request ..!!!";
qDebug() << "\n New client from:" << clientConnection->peerAddress().toString();
clientConnection->write(block);
clientConnection->flush();
// create new tcp object
TcpSocketThreadWrapperObject* TcpSocketThreadWrapperObjectPtr = new TcpSocketThreadWrapperObject(clientConnection);
// Append object to the list
TcpSocketThreadWrapperObjectList.append(TcpSocketThreadWrapperObjectPtr);
}
============ TcpSocketThreadWrapperObject ==============
// Main device thread object
class TcpSocketThreadWrapperObject : public QObject
{
Q_OBJECT
public:
explicit TcpSocketThreadWrapperObject(QTcpSocket *m_pTcpSocket , QObject *parent = 0);
~TcpSocketThreadWrapperObject();
/*!
pointer for write thread
*/
QThread m_TcpSocketRWThread;
/// pointer to the socketthread object
class TcpSocketThreadObject *m_pTcpSocketThreadObject;
signals:
public slots:
};
// constructor for the deviceThreadObject
TcpSocketThreadWrapperObject::TcpSocketThreadWrapperObject(QTcpSocket *m_pTcpSocket , QObject *parent) :
QObject(parent)
{
m_pTcpSocketThreadObject = new TcpSocketThreadObject(m_pTcpSocket);
//set flag for event loop -- make while(1)
m_pTcpSocketThreadObject->m_bQuit = false;
// connect the signal & slot
connect(&m_TcpSocketRWThread,SIGNAL(started()),m_pTcpSocketThreadObject,SLOT(dowork_socket()));
// Move thread to object
m_pTcpSocketThreadObject->moveToThread(&m_TcpSocketRWThread);
//Start the thread
m_TcpSocketRWThread.start();
}
TcpSocketThreadWrapperObject::~TcpSocketThreadWrapperObject()
{
//set flag for event loop -- make while(0)
m_pTcpSocketThreadObject->m_bQuit = false;
// Wait for the thread to terminate
m_TcpSocketRWThread.quit();
m_TcpSocketRWThread.wait();
// Delete the object
m_pTcpSocketThreadObject->deleteLater();
qDebug() << "\n deleted - TcpSocketThreadWrapperObject";
}
======== TcpSocketThreadObject object ========
class TcpSocketThreadObject : public QObject
{
Q_OBJECT
public:
explicit TcpSocketThreadObject(QTcpSocket *m_pTcpSocketTemp , QObject *parent = 0);
~TcpSocketThreadObject();
/*!
Pointer to TCP socket -- created by the server
*/
QTcpSocket *m_pclientConnectionSocket;
/*!
Termination control main thread
*/
volatile bool m_bQuit;
signals:
public slots:
void dowork_socket();
};
// constructor for the deviceThreadObject
TcpSocketThreadObject::TcpSocketThreadObject(QTcpSocket *m_pTcpSocketTemp , QObject *parent) :
QObject(parent)
{
m_pclientConnectionSocket = m_pTcpSocketTemp;
// todo
}
TcpSocketThreadObject::~TcpSocketThreadObject()
{
// todo
}
void TcpSocketThreadObject::dowork_socket()
{
QByteArray block;
block.append(" \n hi again .. !!!") ;
// Write to socket
m_pclientConnectionSocket->write(block);
m_pclientConnectionSocket->flush();
// Close socket
m_pclientConnectionSocket->disconnectFromHost();
qDebug() << "\n entring loop of socket thread ..!!!";
while(!m_bQuit)
{
// while loop --> send/rx command from client
}
}
======= output of one client connected to server =====
New connection request ..!!!
New client from: "127.0.0.1"
QSocketNotifier: socket notifiers cannot be enabled from another thread
QSocketNotifier: socket notifiers cannot be disabled from another thread
entring loop of socket thread ..!!!
I think the simplest solution to all of this is to stop using QThread.
QTcpSocket and QTcpServer are both asynchronous, so when you receive a connection, you only need to create a class to wrap the QTcpSocket and this class handles the reading of the data, having connected the QTcpSocket signals to the class's slots.
If you're getting a lot of connections, then you'll be creating lots of threads. More threads than processor cores is just a waste of time.
If each connection is requesting a lot of work to be done by the server, you can then create separate worker objects and move those to a different thread, but overall, I recommend that you do not use separate threads for QTcpServer and QTcpSockets.
I am implementing 'Local fortune server' example (from Qt 4.7.3) as a service on Windows.
What i want is that when someone paused the service, the local server should notify the error to the connected local socket (local fortune client). The error can be QLocalSocket::ServerNotFoundError.
Now, How to generate this error from server example. Please look at the following code where i want to generate this error.
void FortuneServer::incomingConnection(quintptr socketDescriptor)
{
if (disabled) {
**// here i want to emit QLocalSocket::ServerNotFoundError**
return;
}
QString fortune = fortunes.at(qrand() % fortunes.size());
FortuneThread *thread = new FortuneThread(socketDescriptor, fortune, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
void FortuneServer:: pause()
{
disabled = true;
}
If you want your server to notify your clients I think you should create and send your own message like :
QString fortune = "Server down";
But this will occure when your server has incoming message.
or you can shutdown the server whith the QTcpServer::close() method
void FortuneServer:: pause()
{
this.close();
}
your client app will lost its connection and you should be able to get the signal you want in it with this :
connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(displayError(QAbstractSocket::SocketError)));
You can't emit QLocalSocket::ServerNotFoundError, because it's not a signal. You should define your own signal and emit it instead (you can pass values in signals). You should also implement slot and connect signal to it. Read more about signals and slots.