C++ - QThread and SocketManager - c++

I subclassed QTcpServer to generate SocketManagers which are workers that should run and create sockets on their own thread. I would like, if possible, to keep the worker and the QThread in a single object instance. The overall goal is to have different threads for different collections of sockets. The purpose is purely academic.
How should I go about implementing the SocketManager class ? Can I make the QThread an attribute of the worker as such ?
class SocketManager : public QObject
{
Q_OBJECT
public:
SocketManager(); // Creates managerThread and moves this to it
~SocketManager(); // Calls close on all TcpSocket instances (subclass of QTcpSocket)
// Calls managerThread->quit() without waiting.
protected:
QSharedPointer<QThread> managerThread;
QHash<quint16, QSharedPointer<TcpSocket>> socketsHash;
public slots:
void onAddSocket(qintptr socketDescriptor);
void onRemoveSocket(qintptr socketDescriptor);
};
If this is an acceptable design how should I go about handling memory management:
Using &QObject::deleteLater the socket manager never gets deleted when resetting the shared pointer held by the TcpServer instance
Usind the delete operator on the SocketManager instance (The thread and sockets use deleteLater) works fine only when there are no sockets in the container socketsHash. The following message is given (which means I'm doing something wrong)
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
...in the following output:
Listening on 0.0.0.0:8080
Closing server
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
Removing socket QSharedPointer(QSslSocket(0xcf6410)) 1 connections left
QSharedPointer(QSslSocket(0xcf6410)) onDisconnected
SocketManager(0xd0f910) Destroyed QThread(0xc74dd0) QThread(0xc801b0)
QSslSocket(0xcf6410) Destroyed 0xc74dd0 0xc801b0
QThread(0xc801b0) destroyed
I also thought about making the SocketManager class a subclass of QThread but I read multiple times that sublassing QThread is a slippery slope. Please correct me if i'm wrong.
EDIT:
tcpserver.h
class TcpServer : public QTcpServer
{
Q_OBJECT
public:
TcpServer();
~TcpServer();
bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
void close();
private:
SharedPtr<SocketManager> manager;
protected:
void incomingConnection(qintptr handle) override;
signals:
void addSocket(qintptr handle);
};
tcpserver.cpp
#include <QApplication>
#include <QThread>
TcpServer::TcpServer(): QTcpServer()
, manager(nullptr)
{
}
TcpServer::~TcpServer()
{
close();
qDebug() << this << "Destroyed" << QApplication::instance()->thread() << thread();
}
bool TcpServer::listen(const QHostAddress &address, quint16 port)
{
if (isListening()) {
qDebug() << "Server is already listening";
return false;
}
if (!QTcpServer::listen(address, port)){
qDebug() << QString("Could not listen on %1:%2").arg(address.toString(),QString::number(port));
return false;
}
qDebug() << QString("Listening on %1:%2").arg(address.toString(),QString::number(port));
if (manager) manager.reset();
manager = SharedPtr<SocketManager>(new SocketManager());
connect(this, &TcpServer::addSocket, manager.data(), &SocketManager::onAddSocket, Qt::QueuedConnection);
return true;
}
void TcpServer::close()
{
qDebug() << "Closing server";
QTcpServer::close();
if (manager) manager.reset();
}
void TcpServer::incomingConnection(qintptr handle)
{
emit addSocket(handle);
}
socketmanager.h
class SocketManager : public QObject
{
Q_OBJECT
public:
explicit SocketManager();
~SocketManager();
protected:
SharedPtr<QThread> managerThread;
QHash<quint16, QSharedPointer<TcpSocket>> connections;
public slots:
void onAddSocket(qintptr handle);
void onRemoveSocket(qintptr handle);
};
socketmanager.cpp
#include "socketmanager.h"
#include <QApplication>
SocketManager::SocketManager(): QObject()
, managerThread(SharedPtr<QThread>(new QThread(), &QThread::deleteLater))
{
moveToThread(managerThread.data());
connect(managerThread.data(), &QThread::destroyed, [&](){ qDebug() << "Thread destroyed"; });
managerThread->start();
}
SocketManager::~SocketManager()
{
for (SharedPtr<TcpSocket> socket : connections.values()){
socket->close();
}
if (managerThread) managerThread->quit();
qDebug() << this << "Destroyed" << QApplication::instance()->thread() << thread();
}
void SocketManager::onAddSocket(qintptr handle)
{
if (!handle) return;
SharedPtr<TcpSocket> socket = SharedPtr<TcpSocket>(new TcpSocket(handle), &QObject::deleteLater);
connect(socket.data(), &TcpSocket::disconnected, [=](){onRemoveSocket(handle);});
connections.insert(handle, socket);
}
void SocketManager::onRemoveSocket(qintptr handle)
{
SharedPtr<TcpSocket> socket = connections.value(handle);
if(!socket.data()) return;
qDebug() << "Removing socket" << socket << connections.count() << "connections left";
connections.remove(handle);
socket->close();
}
tcpsocket.h
#include <QSslSocket>
class TcpSocket : public QSslSocket
{
public:
TcpSocket(qintptr handle);
~TcpSocket();
private slots:
void onConnected();
void onEncrypted();
void onDisconnected();
void onErrorOccured(QAbstractSocket::SocketError error);
void onSslErrors(const QList<QSslError> &errors);
void onStateChanged(QAbstractSocket::SocketState);
void onReadyRead();
};
tcpsocket.cpp
#include "tcpsocket.h"
#include <QApplication>
TcpSocket::TcpSocket(qintptr handle): QSslSocket()
{
setSocketDescriptor(handle);
connect(this, &QTcpSocket::connected, this, &TcpSocket::onConnected);
connect(this, &QSslSocket::encrypted, this, &TcpSocket::onEncrypted);
connect(this, &QTcpSocket::disconnected, this, &TcpSocket::onDisconnected);
connect(this, &QTcpSocket::errorOccurred, this, &TcpSocket::onErrorOccured);
connect(this, &QSslSocket::sslErrors, this, &TcpSocket::onSslErrors);
connect(this, &QAbstractSocket::stateChanged, this, &TcpSocket::onStateChanged);
connect(this, &QTcpSocket::readyRead, this, &TcpSocket::onReadyRead);
}
TcpSocket::~TcpSocket()
{
qDebug() << this << "Destroyed" << QApplication::instance()->thread() << thread();
}
void TcpSocket::onConnected()
{
qDebug() << socketDescriptor() << "onConnected";
}
void TcpSocket::onEncrypted()
{
qDebug() << socketDescriptor() << "onEncrypted";
}
void TcpSocket::onDisconnected()
{
qDebug() << socketDescriptor() << "onDisconnected";
}
void TcpSocket::onErrorOccured(QAbstractSocket::SocketError error)
{
qDebug() << socketDescriptor() << "onErrorOccured" << error;
}
void TcpSocket::onSslErrors(const QList<QSslError> &errors)
{
// qDebug() << socketDescriptor() << "onSslErrors" << errors;
}
void TcpSocket::onStateChanged(QAbstractSocket::SocketState state)
{
// qDebug() << socketDescriptor() << "onStateChanged" << state;
}
void TcpSocket::onReadyRead()
{
// qDebug() << socketDescriptor() << "onReadyRead" /*<< readAll()*/;
}

Related

How to exchange between each other value of variables in two(or more) classes in qt 5.6?

I work on for update STM mcu from qt program. I use qt for GUI but if I have a file that has specific format, I should begin update procedure. I send start char to STM and I must send update data according to the answer. I parsed my code to classes for usefull. So UART class send data to STM and read data incoming from STM mcu. PROTOCOL class should send update data to UART accordingly to incoming data.
My problem is here. I transfered data between classes but just one direction(from PROTOCOL to UART). I sent data from PROTOCOL class to UART class. I need incoming data from STM mcu in PROTOCOL class. UART class read data but it can not send to PROTOCOL class(or PROTOCOL class can not read incoming data). How can I accomplish this?
uart.h
#ifndef UART_H
#define UART_H
#include <QObject>
#include <QtSerialPort>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
class UART : public QObject
{
Q_OBJECT
public:
explicit UART(QObject *parent = 0);
void Start();
bool Updating = true;
QByteArray received_new_data;
signals:
public slots:
void ReadFromSerialPort();
void WriteToSerialPort(const QByteArray &update_data, const qint64 &message_size);
private:
const static QString DEBUG_IDENTIFIER;
QSerialPort serial;
QByteArray data;
qint64 messageSize;
void Initialize(const QString &portName);
void Stop();
static QString PORT_NAME;
};
#endif // UART_H
uart.cpp
#include "uart.h"
#include <QDebug>
QString const UART::DEBUG_IDENTIFIER = "[UART]"; QString
UART::PORT_NAME = "/dev/ttyS1";
UART::UART( QObject *parent) : QObject(parent) {
}
void UART::Initialize(const QString &portName) {
serial.setPortName(portName);
serial.setBaudRate(QSerialPort::Baud115200);
serial.setDataBits(QSerialPort::Data8);
serial.setParity(QSerialPort::NoParity);
serial.setStopBits(QSerialPort::OneStop);
serial.setFlowControl(QSerialPort::NoFlowControl);
serial.open(QIODevice::ReadWrite);
}
void UART::ReadFromSerialPort() {
quint64 available_byte_amount = serial.bytesAvailable();
if(available_byte_amount)
{
received_new_data = serial.readAll();
qDebug() << DEBUG_IDENTIFIER << "received_data: " << received_new_data;
}
}
void UART::WriteToSerialPort(const QByteArray &update_data, const
qint64 &message_size) {
data = update_data; //! max bytes lenght
messageSize = message_size;
//! When incoming hex "10" program continue send "U" char
serial.write(data,messageSize);
serial.flush();
qDebug() <<"send";
qDebug() << "data: " << received_new_data;
}
void UART::Start() {
Initialize(PORT_NAME);
if(serial.isOpen())
{
serial.clear();
serial.clearError();
connect(&serial,SIGNAL(readyRead()), this, SLOT(ReadFromSerialPort()));
}
else
{
qDebug() << DEBUG_IDENTIFIER << "(Start)"
<< "Serial device is not open";
}
if(serial.error() == QSerialPort::NoError)
{
qDebug() << DEBUG_IDENTIFIER << "No Error";
}
else
{
qDebug() << DEBUG_IDENTIFIER << "An error occured:" << serial.errorString();
} }
void UART::Stop() {
disconnect(&serial,SIGNAL(readyRead()), this, SLOT(ReadFromSerialPort())); }
protocol.h
#ifndef PROTOCOL_H
#define PROTOCOL_H
#include <QObject>
#include <QTimer>
#include "uart.h"
class UART;
class PROTOCOL : public QObject
{
Q_OBJECT
public:
explicit PROTOCOL(UART *target_STM, QObject *parent = 0);
static UART *ptr_uart;
void gentnuma();
signals:
public slots:
void readSTM(const QByteArray &readData);
void writeSTM(const QByteArray &stm_data, const qint64 &stm_message_size);
void read_data();
private:
const static QString DEBUG_IDENTIFIER;
QTimer *m_timer;
};
#endif // PROTOCOL_H
protocol.cpp
#include <QByteArray>
#include <QDebug>
#include "protocol.h"
#include "uart.h"
QString const PROTOCOL::DEBUG_IDENTIFIER = "[PROTOCOL]";
UART *PROTOCOL::ptr_uart = NULL;
PROTOCOL::PROTOCOL(UART *target_uart, QObject *parent) : QObject(parent)
{
ptr_uart = target_uart;
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(read_data()));
timer->start(1);
}
void PROTOCOL::gentnuma()
{
UART begin;
QByteArray a = begin.received_new_data;
qDebug() << DEBUG_IDENTIFIER << "TEST:";
qDebug() << DEBUG_IDENTIFIER << "COMING DATA:"<< a; // I expected read data here
}
void PROTOCOL::readSTM(const QByteArray &readData)
{
qDebug() << DEBUG_IDENTIFIER << "read_STM_request:" << readData;
}
void PROTOCOL::writeSTM(const QByteArray &stm_data, const qint64 &stm_message_size)
{
qDebug() << DEBUG_IDENTIFIER << "send STM_data: " << stm_data;
qDebug() << DEBUG_IDENTIFIER << "send STM_data size: " << stm_message_size;
ptr_uart->WriteToSerialPort(stm_data,stm_message_size); //send data to uart
}
void PROTOCOL::read_data()
{
gentnuma();
}
Sorry bad English and long code lines. Thanks.
Solution is just a few steps away: have a signal in your UART class:
signals:
void newData();
emit it when new data are read:
void UART::ReadFromSerialPort()
{
quint64 available_byte_amount = serial.bytesAvailable();
if(available_byte_amount)
{
received_new_data = serial.readAll();
qDebug() << DEBUG_IDENTIFIER << "received_data: " << received_new_data;
emit newData();
}
}
You then connect the UART signal to the PROTOCOL slot you happen to already have, so, in PROTOCOL constructor, instead of
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(read_data()));
timer->start(1);
do:
connect(ptr_uart, SIGNAL(newData()), this, SLOT(read_data()));
This way, instead of polling for new data every second, your PROTOCOL class will be able to read new data from 'UART' whenever there's any.
But, watch out, you're doing it wrong here:
void PROTOCOL::gentnuma()
{
UART begin;
QByteArray a = begin.received_new_data;
qDebug() << DEBUG_IDENTIFIER << "TEST:";
qDebug() << DEBUG_IDENTIFIER << "COMING DATA:"<< a; // I expected read data here
}
since you're instantiating a new UART object out of the blue, and it has not (and never will have) new data to show. You already have a pointer to UART as a private class member, so you must use that one:
void PROTOCOL::gentnuma()
{
QByteArray a = ptr_uart->received_new_data;
qDebug() << DEBUG_IDENTIFIER << "TEST:";
qDebug() << DEBUG_IDENTIFIER << "COMING DATA:"<< a; // I expected read data here
}
(just as a footnote: as far as I can see, that ptr_uart doesn't really need to be static ...)

Multi Thread server

I am writing multi thread server but i have problem in accept connection and start read function. i don't know where i should write them..
here is my code:
"mythread.cpp"
#include "mythread.h"
#include "myserver.h"
mythread::mythread(qintptr ID, QObject *parent) :
QThread(parent)
{
this->socketDescriptor = ID;
}
void mythread::run()
{
qDebug() << " Thread started";
}
void mythread::acceptConnection()
{
c_client = s_server.nextPendingConnection();
connect(c_client,SIGNAL(readyRead()),
this, SLOT(startRead()));
}
void mythread::startRead()
{
char buffer[1024] = {0};
c_client->read(buffer, c_client->bytesAvailable());
qDebug() << buffer;
}
void mythread::readyRead()
{
QByteArray Data = socket->readAll();
qDebug() << socketDescriptor << " Data in: " << Data;
socket->write(Data);
}
void mythread::disconnected()
{
qDebug() << socketDescriptor << " Disconnected";
socket->deleteLater();
exit(0);
}
"myserver.cpp"
#include "myserver.h"
#include "mythread.h"
myserver::myserver(QObject *parent) :
QObject(parent)
{
}
void myserver::startserver()
{
int port = 1234;
if(s_server.listen(QHostAddress::Any, port))
{
qDebug() << "Could not start server";
}
else
{
qDebug() << "Listening to port " << port ;
}
}
void myserver::incomingconnection(int socketDescriptor)
{
connect(&s_server, SIGNAL(newConnection()),
this, SLOT(acceptConnection()));
s_server.listen(QHostAddress::Any, 1234);
qDebug() << socketDescriptor << " Connecting...";
mythread *thread = new mythread(socketDescriptor,this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
i would be grateful if you help me.
You are not using QThread very well. you can use SIGNAL and SLOTS ,and MoveToThread() function. google it.
when you use QThread, the code in Run() function will be run in another thread. acceptConnection will run in main thread.
also search for nextPendingConnection();
void myserver::incomingconnection(int socketDescriptor)
{
connect(&s_server, SIGNAL(newConnection()),this, SLOT(acceptConnection()));
...
is not OK. this connect should be called once (maybe constructor). not for any incomming connection.

newConnection() signal does not get emitted in QTcpSever even when I am clearly connecting to the server

QTcpServer is supposed to emit the newConnection() signal whenever it receives a new client connection. I am able to connect to the server I created, even telnet to it. But in the server implementation, I have connected the newConnection() signal to a slot and it never gets invoked.
Can you please look at my code and tell me what is it that I am doing wrong ?
First the header :
#include <data_model.pb.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <QtNetwork>
#include <QVector>
#include <iostream>
#include <QAbstractSocket>
#include <QDebug>
class QTcpServer;
class QNetworkSession;
class superServer : public QObject
{
Q_OBJECT
public:
explicit superServer(QObject *parent = 0);
data_model::terminal_data fetch_next_record();
void route_data();
private slots:
void sessionOpened();
public slots:
void newTerminalConnection();
void errorconnecting(QAbstractSocket::SocketError socketError);
private:
QTcpServer *tcpServer;
QNetworkSession *networkSession;
QVector<QTcpSocket*> liveConnections;
};
#endif // SUPERSERVER_HPP
Now the implementation:
#include <superServer.hpp>
void superServer::newTerminalConnection()
{
qDebug() << "someone connected ...";
std::cout << "someone connected ..." << std::endl;
QTcpSocket *socket = tcpServer->nextPendingConnection();
liveConnections.push_back(socket);
}
superServer::superServer(QObject *parent) :
QObject(parent)
{
tcpServer = new QTcpServer();
if(!tcpServer->listen(QHostAddress::Any, 9889))
qDebug() << "Server could not start";
else
qDebug() << "Server started!";
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newTerminalConnection()));
connect(tcpServer, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(errorconnecting(QAbstractSocket::SocketError)));
}
void superServer::errorconnecting(QAbstractSocket::SocketError socketError)
{
qDebug() << "error connecting to client";
}
Also, I am connecting like this:
void tradeView::doConnect()
{
socket = new QTcpSocket(this);
connect(socket, SIGNAL(connected()),this, SLOT(connected()));
connect(socket, SIGNAL(disconnected()),this, SLOT(disconnected()));
connect(socket, SIGNAL(bytesWritten(qint64)),this, SLOT(bytesWritten(qint64)));
connect(socket, SIGNAL(readyRead()),this, SLOT(readyRead()));
qDebug() << "connecting...";
// this is not a blocking call
socket->connectToHost("localhost", 9889);
// we need to wait...
if(!socket->waitForConnected(5000))
{
qDebug() << "Error: " << socket->errorString();
}
}
void tradeView::connected()
{
qDebug() << "connected...";
}
void tradeView::disconnected()
{
qDebug() << "disconnected...";
}
void tradeView::bytesWritten(qint64 bytes)
{
qDebug() << bytes << " bytes written...";
}
The client connects !!! Even Telnet works !!!
But that signal does not get emitted. Can you please tell me what is wrong ?
Trust me I have looked at more than a dozen related threads here on SO and qt-center etc. None give a solution.
Please help.

Why is my thread which is waiting on a QWaitCondition not returning

I have data structure where I want to wait until it recives data. When I call setInterrupt I want my thread to stop but it never returns.
This is my queue:
BufferedIpQqueue::BufferedIpQqueue()
{
}
BufferedIpQqueue::~BufferedIpQqueue()
{
bufferWaitCondition.wakeAll();
}
QString BufferedIpQqueue::get()
{
QMutexLocker locker(&mutex);
while(queue.isEmpty())
{
qDebug() << "waiting at mutex " << &mutex << "calling threadid: " << QThread::currentThreadId();
bufferWaitCondition.wait(&mutex);
}
return queue.first();
}
void BufferedIpQqueue::put(QString &data)
{
QMutexLocker locker(&mutex);
queue.append(data);
bufferWaitCondition.wakeAll();
}
void BufferedIpQqueue::wakAllThreads()
{
qDebug() << "wake all waiting threads at mutex: " << &mutex << "calling threadid: " << QThread::currentThreadId();
bufferWaitCondition.wakeAll();
}
This is my consumer thread:
IpCheckWorker::IpCheckWorker(BufferedIpQqueue *queue)
{
this->queue = queue;
interrupt = false;
}
void IpCheckWorker::setInterrupt(bool value)
{
QMutexLocker lock(&mutex);
qDebug() << "wake all threads";
interrupt = value;
queue->wakAllThreads();
}
bool IpCheckWorker::getInterruptFlag()
{
QMutexLocker lock(&mutex);
return interrupt;
}
void IpCheckWorker::process()
{
while(getInterruptFlag() == false)
{
qDebug() << "enter process loop ThreadID:" << QThread::currentThreadId();
QString check_ip = queue->get();
qDebug() << "proccess ThreadID:" << QThread::currentThreadId();
}
qDebug() << "leave process event ThreadID:" << QThread::currentThreadId();
emit finished();
}
I get the following output:
enter process loop ThreadID: 0xf94
waiting at mutex 0x51ab5f0 calling threadid: 0xf94
wake all threads
wake all waiting threads at mutex: 0x51ab5f0 calling threadid: 0x10dc
waiting at mutex 0x51ab5f0 calling threadid: 0xf94
It seems like I am stock at while(queue.isEmpty()) in my BufferedIpQqueue::get() method. Why is my mthod not returning to IpCheckWorker::process()?
Any help how to do it right would be very kind.
What you are doing is reimplementing a slightly less flexible QThreadPool. If you wish, you can look in its implementation for how it uses the wait conditions. I'd simply advise to use QThreadPool and call it a day, since it's a perfect fit for your requirements.
The thread pool maintains a queue of QRunnable instances. The instances are picked up by idle threads. The thread management is automatic. QRunnable is a lightweight class similar to QEvent - it is not a QObject.
QThreadPool is not a singleton. There is a global instance, but you don't have to use it. You can have your own instance.
This example is compatible with Qt 4.4 & up.
#include <QCoreApplication>
#include <QThreadPool>
#include <QRunnable>
#include <QBasicTimer>
#include <QDebug>
#include <cstdlib>
class Thread : public QThread {
public:
using QThread::msleep;
};
class IpData {
QString m_value;
public:
IpData(const QString & str) : m_value(str) {}
const QString & value() const { return m_value; }
};
class IpRunnable : public QRunnable, IpData {
void run() {
qDebug() << "[" << QThread::currentThreadId()
<< "] processing ip" << value();
Thread::msleep(qrand() < (RAND_MAX/4.0) ? 0 : 100);
}
public:
IpRunnable(const IpData & data) : IpData(data) {}
IpRunnable(const QString & str) : IpData(str) {}
};
class Test : public QObject {
Q_OBJECT
int i;
QBasicTimer timer;
void timerEvent(QTimerEvent * t) {
if (t->timerId() != timer.timerId()) return;
QThreadPool::globalInstance()->start(new IpRunnable(QString::number(i)));
if (++i > 100) qApp->quit();
}
public:
Test() : i(0) { timer.start(20, this); }
};
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QThreadPool::globalInstance()->setMaxThreadCount(5);
Test test;
return app.exec();
}
#include "main.moc"

Why client dont connect to server? Qt

Programm works, but client can't connect to the server. (i run 2 examples of programm: client and server). I can't find where is my mistake.
I wrote the codes below.You will see what i want to do if you look at main function.
//main.cpp
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//выбор клиента или сервера
cout << "1.Client\n2.Server\n";
switch (_getch())
{
case 49:
{
cout<<"client init\n";
Client* cli = new Client("localhost",1922);
string line;
while(line!="exit") {
cout << "Message : ";
cin >> line;
cli->SendData(QString(line.c_str()));
}
break;
}
case 50:
{
cout<<"server init\n";
Server* srv = new Server(0, 1922);
break;
}
}
return a.exec();
}
//server.h
class Server : public QTcpServer {
Q_OBJECT public : Server(QObject *parent = 0, quint16 port = 1922);
virtual ~Server();
private
slots:
void acceptConnection();
void startRead();
void disconnected();
private:
QTcpServer *tcpServer;
QTcpSocket *client;
};
//server.cpp
Server::Server(QObject *parent, quint16 port) : QTcpServer(parent) {
//tcpServer = new QTcpServer(this);
connect(this, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
if (!this->listen(QHostAddress::Any, port))
std::cout << "unable to start server\n"
<< this->errorString().toUtf8().constData() << endl;
else
std::cout << "server started\n";
}
Server::~Server() {
//delete client;
close();
}
void Server::acceptConnection() {
std::cout << "new connection!\n";
client = nextPendingConnection();
connect(client, SIGNAL(readyRead()), this, SLOT(startRead()));
connect(client, SIGNAL(disconnected()), this, SLOT(disconnected()));
qDebug() << "New client from:" << client->peerAddress().toString();
}
void Server::startRead() {
client = (QTcpSocket *)sender();
while (client->canReadLine()) {
QString line = QString::fromUtf8(client->readLine()).trimmed();
qDebug() << "Client :" << line;
client->write(QString("Server : I've taken your message (:\n").toUtf8());
}
}
void Server::disconnected() {
qDebug() << "Client disconnected:" << client->peerAddress().toString();
client->write(QString("Server : I wish you didn't leave ):\n").toUtf8());
}
//} <-- EDIT: THIS IS PROBABLY AN EXTRA
//***************************************************************
//client.h
class Client : public QObject {
Q_OBJECT public : Client(const QString &add, int port, QObject *obj = 0);
void SendData(QString data);
virtual ~Client();
int status();
QString err;
private
slots:
void ReadData();
void slotConnected();
void slotError(QAbstractSocket::SocketError);
private:
QTcpSocket *socket;
};
//client.cpp
Client::Client(const QString &add, int port, QObject *obj) : QObject(obj) {
//create socket
socket = new QTcpSocket(this);
//connect
socket ->connectToHost(add, port);
connect(socket, SIGNAL(readyRead()), SLOT(ReadData()));
connect(socket, SIGNAL(connected()), SLOT(slotConnected()));
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this,
SLOT(slotError(QAbstractSocket::SocketError)));
}
Client::~Client() {
socket->close();
delete socket;
}
void Client::SendData(QString data) {
if (!data.isEmpty()) {
QByteArray arrBlock;
QDataStream out(&arrBlock, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_1);
out << quint16(0) << QTime::currentTime() << data;
out.device()->seek(0);
out << quint16(arrBlock.size() - sizeof(quint16));
socket->write(arrBlock);
socket->flush();
}
}
void Client::ReadData() {
QDataStream in(socket);
in.setVersion(QDataStream::Qt_5_1);
while (socket->canReadLine()) {
QString line = QString::fromUtf8(socket->readLine()).trimmed();
qDebug() << line;
}
}
void Client::slotConnected() {
socket->write(QString("Client : Server connection has been made (: \n")
.toUtf8());
}
void Client::slotError(QAbstractSocket::SocketError err) {
QString strError =
"Error: " + (err == QAbstractSocket::HostNotFoundError
? "The host was not found."
: err == QAbstractSocket::RemoteHostClosedError
? "The remote host is closed."
: err == QAbstractSocket::ConnectionRefusedError
? "The connection was refused."
: QString(socket->errorString()));
std::cout << strError.toUtf8().constData() << endl;
}
int Client::status() { return socket->state(); }
help me pls!
It's probably becouse of the while loop in main.cpp, it blocks client's event loop, and will return to event loop right after 'exit' will be typed. I mean this lines:
while (line != "exit") {
cout << "Message : ";
cin >> line;
cli.SendData(QString(line.c_str()));
}
How this can be avoided: main.cpp MUST reach return a.exec(); line to start event loop (i am excluding some ugly processEvent solutions right away ).
To send commands to cmd and NOT block event loop i used class i saw somewhere here on stackoverflow:
example of main.cpp:
QCoreApplication a(argc, argv);
qDebug()<<"Press 'q' to quit";
QTcpServer server;
qDebug()<<"Server is started -"<<server.isListening();
// Console reader to filter console input
ConsoleReader reader;
QObject::connect(&reader,SIGNAL(shutdown()),&a,SLOT(quit()));
return a.exec();
aaand behold, ConsoleReader class, header:
#ifndef CONSOLEREADER_H
#define CONSOLEREADER_H
#pragma once
#include <QObject>
#include <QSocketNotifier>
class ConsoleReader : public QObject
{
Q_OBJECT
public:
explicit ConsoleReader(QObject *parent = 0);
~ConsoleReader();
signals:
void shutdown();
public slots:
void text();
private:
QSocketNotifier* notifier;
};
#endif // CONSOLEREADER_H
source:
#include "consolereader.h"
#include <QTextStream>
#include <QDebug>
#include <unistd.h> //Provides STDIN_FILENO
ConsoleReader::ConsoleReader(QObject *parent) :
QObject(parent)
{
notifier = new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read);
connect(notifier, SIGNAL(activated(int)), this, SLOT(text()));
}
void ConsoleReader::text()
{
QTextStream qin(stdin);
QString line = qin.readLine();
if (line==QString("q")){
qDebug()<<"Shutting down the server..";
emit shutdown();
}
else qDebug()<<"Unknown command: "<<line;
}
ConsoleReader::~ConsoleReader(){
delete notifier;
}
In your main function, you create both the client and the server on the stack, which will then be deleted when they go out of scope of the switch statement.
You need to dynamically allocate the objects on the heap: -
Server* pServer = new Server(0, 1922);
Client* pClient = new Client("localhost" 1922);
Although the client will remain, due to the while loop after its creation, the server will be created, start listening, then be deleted, along with the QTcpSocket, as it is has the server as its parent.
As I mentioned in my comment there is no need to create a separate QTCpServer as the server isa QTcpServer. So where you have:
Server::Server(QObject *parent, quint16 port) : QTcpServer(parent) {
tcpServer = new QTcpServer(this);
connect(this, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
if (!tcpServer->listen(QHostAddress::Any, port))
std::cout << "unable to start server\n"
<< tcpServer->errorString().toUtf8().constData() << endl;
else
std::cout << "server started\n";
}
Change to:
Server::Server(QObject *parent, quint16 port) : QTcpServer(parent) {
connect(this, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
if (!this->listen(QHostAddress::Any, port))
std::cout << "unable to start server\n"
<< this->errorString().toUtf8().constData() << endl;
else
std::cout << "server started\n";
}
I think the problem could be that its trying to do stuff with a different object rather than the 'this' object.