How to send messages to connected clients from server in Qt - qt

I know that how to send messages to the newly connected client whenever the QTcpServer newConnection is emitted. What I did is like this:
connect(serverConnection.tcpServer, &QTcpServer::newConnection, this, &ServerInterface::sendMessages);
void ServerInterface::sendMessages()
{
QByteArray messagesToClients;
QDataStream out(&messagesToClients, QIODevice::WriteOnly);
QTcpSocket *clientConnection = serverConnection.tcpServer->nextPendingConnection();
out << inputBox->toPlainText(); //get text from QTextEdit
clientConnection->write(messagesToClients);
}
But what I want to do is whenever the send messages button is clicked in the server, it will send messages to currently connected clients. The code I provide can only send only one new message to the newly connected client. I have no idea of how to achieve what I want to do, so can someone provide me a way to do that? I am kind of new to Qt networking.
Thanks.

Just store your connections in container. Like this:
in your ServerInterface h-file:
class ServerInterface {
// your stuff
public slots:
void onClientConnected();
void onClientDisconnected();
private:
QVector<QTcpSocket *> mClients;
};
in your ServerInterface cpp-file :
connect(serverConnection.tcpServer, SIGNAL(newConnection(), this, SLOT(onClientConnected());
void ServerInterface::onClientConnected() {
auto newClient = serverConnection.tcpServer->nextPendingConnection();
mClients->push_back( newClient );
connect(newClient, SIGNAL(disconnected()), this, SLOT(onClientDisconnected());
}
void ServerInterface::onClientDisconnected() {
if(auto client = dynamic_cast<QTcpSocket *>(sender()) {
mClients->removeAll(client);
}
void ServerInterface::sendMessages() {
out << inputBox->toPlainText();
for(auto &client : mClients) {
client->write(messagesToClients);
}
}

Related

connect multiple signal to a slot

I've the follwoing issue.
There are two signals :
void buttonChanged(int);
void pulseWidthValue(int);
buttonChanged is emitted from a slot nextBtn:
void Program::nextBtn()
{
m_currentBtn++;
if(m_currentBtn > btnGrp->buttons().size())
{
m_currentBtn = 0;
phaseOver = true;
saveToXMLFile();
}
emit buttonChanged(m_currentBtn);
}
it's connected as follows:
connect(ui->btn_nextPhase, &QPushButton::clicked, this, &Program::nextBtn);
connect(this, &Program::buttonChanged, this, &Program::paintBtn);
the signal, buttonChanged is used in another slot to paint button:
void Program::paintBtn(int id) // how do I change the def of this function to receive to signal?
{
if(id==1)
{
ui->btn1->setStyleSheet(StyleSheetOn);
ui->btn2->setStyleSheet(StyleSheetOff);
ui->btn3->setStyleSheet(StyleSheetOff);
}
else if(id==2)
{
ui->btn1->setStyleSheet(StyleSheetOff);
ui->btn2->setStyleSheet(StyleSheetOn);
ui->btn3->setStyleSheet(StyleSheetOff);
}
else if(id==3)
{
ui->btn1->setStyleSheet(StyleSheetOff);
ui->btn2->setStyleSheet(StyleSheetOff);
ui->btn3->setStyleSheet(StyleSheetOn);
}
else
{
ui->btn1->setStyleSheet(StyleSheetOff);
ui->btn2->setStyleSheet(StyleSheetOff);
ui->btn3->setStyleSheet(StyleSheetOff);
}
ui->label_7->setText(QString::number(pw_value)); // this pw_value is from the other signal pulseWidthValue
}
the basic idea is , there are 3 phase buttons , clicking Next will switch between these buttons and change its color. Now I need to use the other signal pulseWidthValue inside paintBtn
Now I come to the question:
How do I connect two signals, buttonChanged and pulseWidthValue, (both signals are coming from different functions) to the paintBtn slot?
Qt allows you to connect multiple times even to the same slots. It even allows you to connect between signals.
Check this example, is not meant to be functional but descriptive enought to see many options and accesability.
class A:public QObject
{
Q_OBJECT
...
signals:
void signalA1();
void signalA2(const QString &);
}
class B:public QObject
{
Q_OBJECT...
signals:
void signalB();
public slots:
void slotB();
}
class C:public QObject
{
...
public:
C(QObject *parent):QObject(parent)
{
a=new A(this);
b=new B(this);
}
void connectionTest()
{
connect (a,&A::signalA1,b,&B::slotB) ; // one connection to slot B::slotB
connect (this,&C::signalC,b,&B::slotB); // another connection to B::slotB
connect (a,&A::signalA1,this,&C::slotC) ; // another connection from A::signalA, slotC is private so only I can connect
connect (b,&B::signalB,this,&C::signalC); // connection from signal to signal
connect (a,&A::signalA2,this,&C::slotC); //connection from A::signalA2 to C::slotC
//with different argument count, but compatible as slotC doesn't need an argument
connect (a,&A::signalA1,b,&B::slotB) ; // duplicate connection to slot B::slotB (signalA1 will trigger slotB two times)
}
signals:
void signalC();
private slots:
void slotC();
private:
class A *a;
class B *b;
}

QT-How to utilize QWidget with QThread?

I'm making some GUI through QT.
I almost complete my work but I have a hard time dealing with Qthread.
My goal is to measure the position of the motor (it moves) and display it on the Qtextbrowser while working another function in the main thread. When I wrote codes like below, people said I can't use QTextBrowser(Qwidget) directly in the thread, so I'm searching how to return location value to the main thread. Can you do me a favor?
MDCE is a class in another header and the codes I attach are some parts of my first code.
void MotorPort::StartThread(MDCE* com, QTextBrowser* browser)
{
thread1 = QThread::create(std::bind(&MotorPort::MeasureLocation,this,com,browser));
thread1 -> start();
}
void MotorPort::MeasureLocation(MDCE* com, QTextBrowser* browser)
{
double location;
while(1)
{
location = CurrentLocation(com); \\return current position value
browser->setText(QString::number(location));
if (QThread::currentThread()->isInterruptionRequested()) return ;
}
}
void MotorPort::stopMeasure()
{
thread1->requestInterruption();
if (!thread1->wait(3000))
{
thread1->terminate();
thread1->wait();
}
thread1 = nullptr;
}
You should use the Qt signal/slot mechanism for iter-thread notification such as this. Firstly change your MotorPort class definition to declare a signal location_changed...
class MotorPort: public QObject {
Q_OBJECT;
signals:
void location_changed(QString location);
...
}
Now, rather than MotorPort::MeasureLocation invoking QTextBrowser::setText directly it should emit the location_changed signal...
void MotorPort::MeasureLocation (MDCE *com, QTextBrowser *browser)
{
while (true) {
double location = CurrentLocation(com);
/*
* Emit signal to notify of location update.
*/
emit location_changed(QString::number(location));
if (QThread::currentThread()->isInterruptionRequested())
return ;
}
}
Finally, update MotorPort::StartThread to connect the signal to the browser's setText slot...
void MotorPort::StartThread (MDCE *com, QTextBrowser *browser)
{
connect(this, &MotorPort::location_changed, browser, &QTextBrowser::setText);
thread1 = QThread::create(std::bind(&MotorPort::MeasureLocation, this, com, browser));
thread1->start();
}

QT QTcpServer::incomingConnection(qintptr handle) not firing?

I'm trying to create a multithreaded server using Qt for the first time. Normally one would use the socket pointer returned by the QTcpServer::nextPendingConnection() with the socket handle already baked in - but since I'm interfacing with the connecting client on a separate thread, I need to create the socket separately using the qintptr handle from QTcpServer::incomingConnection(qintptr handle). After a very dreary, error-packed debugging session I managed to track down the problem to the QTcpServer::incomingConnection() never being fired?
Has anyone had a similar problem - has something changed over recent versions Qt?
These are the ones I've tried:
QTcpServer::incomingConnection(qintptr handle)
QTcpServer::incomingConnection(qintptr socketDescriptor)
QTcpServer::incomingConnection(int handle)
EDIT:
Creating instance of server:
TestServer *myServer = new TestServer();
myServer->initializeServer(1234);
Which calls:
void TestServer::initializeServer(quint16 port)
{
mainServer = new QTcpServer(this);
mainServer->listen(QHostAddress::Any, port);
qDebug() << "Listening for connections on port: " << port;
}
Server is now listening. When a client connects incomingConnection(qintptr handle) is supposed to be called:
void TestServer::incomingConnection(qintptr socketDescriptor){
TestClient *client = new TestClient(this);
client->setSocket(socketDescriptor);
}
Which calls:
void TestClient::setSocket(quint16 socketDescr)
{
socket = new QTcpSocket(this);
socket->setSocketDescriptor(socketDescr);
connect(socket, SIGNAL(connected()),this,SLOT(connected()));
connect(socket, SIGNAL(disconnected()),this,SLOT(disconnected()));
connect(socket, SIGNAL(readyRead()),this,SLOT(readyRead()));
}
Called on connect() signal:
void TestClient::connected()
{
qDebug() << "Client connected..."; // This debug never appears in the console, since QTcpServer::incomingConnection isn't being fired.
}
You have some errors at your code:
At TestServer your QTcpServer probably aggregated, but you need to inherit it. At this case you try to override incomingConnection() method, but you haven't base class and you just create new incomingConnection(), not override.
You get qintptr descriptor variable from incomingConnection(), but set quint16 type at setSocket() method.
You probably mix client of server and client of your part, which just get incoming connection and handling socket data.
I write some little example below for your understanding tcp client-server communication.
Server part
Main part is server themselves:
#include <QTcpServer>
class TestServer: public QTcpServer
{
public:
TestServer(QObject *parent = 0);
void incomingConnection(qintptr handle) Q_DECL_OVERRIDE;
};
Just look: I don't aggragate QTcpServer, but inherited it. At this case you can override incomingConnection() method correctly.
At constructor we just start server for listening using listen() method:
TestServer::TestServer(QObject *parent):
QTcpServer(parent)
{
if (this->listen(QHostAddress::Any, 2323)) {
qDebug() << "Server start at port: " << this->serverPort();
} else {
qDebug() << "Start failure";
}
}
Then time for overriding incomingConnection():
void TestServer::incomingConnection(qintptr handle)
{
qDebug() << Q_FUNC_INFO << " new connection";
SocketThread *socket = new SocketThread(handle);
connect(socket, SIGNAL(finished()), socket, SLOT(deleteLater()));
socket->start();
}
I create SocketThread object which handle incoming data:
#include <QThread>
#include <QObject>
class QTcpSocket;
class SocketThread: public QThread
{
Q_OBJECT
public:
SocketThread(qintptr descriptor, QObject *parent = 0);
~SocketThread();
protected:
void run() Q_DECL_OVERRIDE;
private slots:
void onConnected();
void onReadyRead();
void onDisconnected();
private:
QTcpSocket *m_socket;
qintptr m_descriptor;
};
We inherits from QThread for making our server multithreading, so we have to override run() method:
SocketThread::SocketThread(qintptr descriptor, QObject *parent)
: QThread(parent), m_descriptor(descriptor)
{
}
void SocketThread::run()
{
qDebug() << Q_FUNC_INFO;
m_socket = new QTcpSocket;
m_socket->setSocketDescriptor(m_descriptor);
connect(m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()), Qt::DirectConnection);
connect(m_socket, SIGNAL(disconnected()), this, SLOT(onDisconnected()), Qt::DirectConnection);
exec();
}
This way we initialize our QTcpSocket, set socket descriptor, connect it with readyRead() and disconnected() signals and start event loop.
void SocketThread::onReadyRead()
{
QDataStream in(m_socket);
in.setVersion(QDataStream::Qt_5_5);
QString message;
in >> message;
qDebug() << message;
m_socket->disconnectFromHost();
}
void SocketThread::onDisconnected()
{
m_socket->close();
// Exit event loop
quit();
}
At onReadyRead() just read some QString from client, write it to console and disconnect from host. At onDisconnected() we close socket connection and exit event loop.
Client part
It is just example and bad-smells style, but i create connection to the server at MainWindow class on QPushButton::clicked signal:
void MainWindow::on_pushButton_clicked()
{
QTcpSocket *client = new QTcpSocket;
connect(client, SIGNAL(connected()), this, SLOT(connected()));
connect(client, SIGNAL(disconnected()), client, SLOT(deleteLater()));
client->connectToHost(QHostAddress::LocalHost, 2323);
client->waitForConnected();
if (client->state() != QAbstractSocket::ConnectedState ) {
qDebug() << Q_FUNC_INFO << " can't connect to host";
delete client;
return;
}
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_5);
out << QString("Hello");
out.device()->seek(0);
client->write(block);
}
void MainWindow::connected()
{
qDebug() << Q_FUNC_INFO << " client connected";
}
I create new QTcpSocket, connect it to the signals and try to connect to the host(in my case it is localhost). Wait for connected and check socket status. If all is right I write to socket QString - just example.
It is one of possible ways to organized multithreading client-server architecture, i hope it will be helpfull for you and you find yours bugs.

QTcpSocket does not send data sometimes

I have two QT apps. One app can be considered to hold a big data and it sends about 10 KB of data chunk every second to second application.
Earlier I tried using QUdpSocket to transmit the data but due to MTU limitation of about 2-5K and need to divide and reunite the data myself, I switched to QTcpSocket.
Sometimes data is sent correctly with QTcpSocket (especially if I write data very frequently ~every 100 ms) but sometimes data is not sent at all. Not even after 5 sec. And sometimes several data chunks are internally buffered for a long period (several seconds) and then sent together.
m_socket = new QTcpSocket(this);
m_socket->connectToHost(QHostAddress::LocalHost, 45454);
sendDataEverySec()
{
QByteArray datagram(10000, 'a');
qint64 len = m_socket->write(datagram);
if(len != datagram.size())
qDebug() << "Error"; //this NEVER occurs in MY case.
m_socket->flush();
}
On receiver side, I use readyRead signal to know when data has arrived.
How can I ensure that data is sent immediately? Are there any better alternatives for what I am trying to do?
Edit:: When I am writing after long gaps of 1 second, I receive "QAbstractSocket::SocketTimeoutError" on receiver side everytime sender sends data. This error is not received if sender writes data frequently.
Is it OKAY to use QTcpSocket to stream data the way I am doing????
Edit 2: On receiver side, when readyRead signal is emitted, I was again checking while(m_socket->waitForReadyRead(500)) and I was getting "QAbstractSocket::SocketTimeoutError" due to this. Also, this check was preventing delivering of single chunks.
After going through docs more, it seems readyRead will be continuously emitted when new data is available, so no need for waitForReadyRead.
I am receiving all data sent but still data does not come immediately. Sometimes two-three chunks are merged. This may be because of delay on receiver side in reading data etc.
On receiver side, when readyRead signal is emitted, I was again checking while(m_socket->waitForReadyRead(500)) and I was getting "QAbstractSocket::SocketTimeoutError" due to this. Also, this check was preventing delivering of single chunks.
After going through docs more, it seems readyRead will be continuously emitted when new data is available, so there is no need for waitForReadyRead.
It had solved my issue.
my tipical solution for client server app.
on server side :
class Server: public QTcpServer {
public:
Server(QObject *parent = 0);
~Server();
private slots:
void readyRead();
void disconnected();
protected:
void incomingConnection(int);
};
on cpp:
void Server::incomingConnection(int socketfd) {
QTcpSocket *client = new QTcpSocket(this);
client->setSocketDescriptor(socketfd);
connect(client, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(client, SIGNAL(disconnected()), this, SLOT(disconnected()));
}
void Server::disconnected() {
QTcpSocket *client = (QTcpSocket*) sender();
qDebug() << " INFO : " << QDateTime::currentDateTime()
<< " : CLIENT DISCONNECTED " << client->peerAddress().toString();
}
void Server::readyRead() {
QTcpSocket *client = (QTcpSocket*) sender();
while (client->canReadLine()) {
//here i needed a string..
QString line = QString::fromUtf8(client->readLine()).trimmed();
}
}
on client:
class Client: public QTcpSocket {
public:
Client(const QHostAddress&, int, QObject* = 0);
~Client();
void Client::sendMessage(const QString& );
private slots:
void readyRead();
void connected();
public slots:
void doConnect();
};
on cpp:
void Client::readyRead() {
// if you need to read the answer of server..
while (this->canReadLine()) {
}
}
void Client::doConnect() {
this->connectToHost(ip_, port_);
qDebug() << " INFO : " << QDateTime::currentDateTime()
<< " : CONNESSIONE...";
}
void Client::connected() {
qDebug() << " INFO : " << QDateTime::currentDateTime() << " : CONNESSO a "
<< ip_ << " e PORTA " << port_;
//do stuff if you need
}
void Client::sendMessage(const QString& message) {
this->write(message.toUtf8());
this->write("\n"); //every message ends with a new line
}

Qt QUdpSocket readyRead() signal is not triggering

I am trying to write a small UDP server application.
I have a client transmitting to this applications socket and I have verified this is sending ok using a small UDP echo program (which echoes the data received on a port to the screen) and also, I can see the packets received in wireshark.
I am using QUdpSocket and it would appear that this Binds ok on setup - but the readyRead() signal doesn't ever seem to get triggered.
I have included some of my code below - at the minute I am simply trying to emulate the little echo program.
Just to give some context to the below code - a button press on the UI calls 'setupNewSocket' on a port that is typed in on UI.
#include "sockethandler.h"
SocketHandler::SocketHandler(QObject *parent) :
QObject(parent)
{
udpSocket = new QUdpSocket(this);
connect( &w, SIGNAL(openNewUDPSocket(quint16)), this, SLOT(setupNewSocket(quint16)) );
connect( this, SIGNAL(printOnUI(QString,QString,QString)), &w, SLOT(updateUI(QString,QString,QString)) );
w.show();
}
void SocketHandler::readPendingDatagrams()
{
while (udpSocket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
QString data = QString( datagram.data() );
QString sender_address = sender.toString();
QString sender_port = QString("%1").arg(senderPort);
emit printOnUI(data, sender_address, sender_port);
}
}
void SocketHandler::setupNewSocket(quint16 port)
{
if( udpSocket->bind(QHostAddress::LocalHost, port) )
{
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
}
else
{
// bind has failed
}
}
QHostAddress::LocalHost binds to 127.0.0.1 .
Probably you need to use QHostAddress::Any which binds to 0.0.0.0.

Resources