how to set SO_REUSEADDR on the socket used by QTcpServer? - qt

I have been using a QTcpServer subclass as a http server, but now I need to reuse the server port.
I have tried to set ShareAddress | ReuseAddressHint in a QTcpSocket, it seems worked, because the same port can be bound twice. But I did not find a way to get a QTcpSocket object from an existing QTcpServer object.
I also used the socketDescriptor() to get the native socket, because I want to use the linux C way to setsockopt, but I don't know how to use linux C code with Qt code together to set socket options.(I followed the Qt style until now.)
I am on ubuntu and Qt5.4. And I am stuck...
Any help would be appreciated. Thanks in advance.

Because SO_REUSEPORT needs to be set before bind/listen is called you need to create descriptor first, set all needed flags, bind, listen and forward your descriptor to QTcpServer for future usage with it.
Here is an example which will listen on port 9999 on any interface
mytcpserver.h:
#ifndef MYTCPSERVER_H
#define MYTCPSERVER_H
#include <QObject>
#include <QTcpSocket>
#include <QTcpServer>
class MyTcpServer : public QObject
{
Q_OBJECT
public:
explicit MyTcpServer(QObject *parent = 0);
public slots:
void newConnection();
private:
QTcpServer *server;
};
#endif // MYTCPSERVER_H
mytcpserver.cpp:
#include "mytcpserver.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
MyTcpServer::MyTcpServer(QObject *parent):QObject(parent)
{
this->server = new QTcpServer(this);
connect(server, &QTcpServer::newConnection, this, &MyTcpServer::newConnection);
// open server and listen on given port
int sockfd = 0;
struct sockaddr_in serv_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
qDebug() << "ERROR: IoT Omega Daemon can't open socket";
exit(EXIT_FAILURE);
}
int flag = 1;
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) == -1)
{
qDebug() << "ERROR: Can't set SO_REUSEADDR";
exit(EXIT_FAILURE);
}
//set Address,IFace, Port...
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(9999);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(sockaddr_in)) < 0)
{
qDebug() << "ERROR: can't bind socket";
exit(EXIT_FAILURE);
}
if(listen(sockfd,SOMAXCONN) < 0)
{
qDebug() << "ERROR: can't listen on port";
exit(EXIT_FAILURE);
}
//forward our descriptor with SO_REUSEPORT to QTcpServer member
server->setSocketDescriptor(sockfd);
}
void MyTcpServer::newConnection()
{
qDebug() << "NEW CONNECTION " << __LINE__;
QTcpSocket * socket = server->nextPendingConnection();
socket->write("Hello client\r\n");
socket->flush();
socket->waitForBytesWritten(3000);
socket->close();
}
Maybe you would like to optimize your socket usage even more? For example setting SO_LINGER timeout of zero to avoid large numbers of connections sitting in the TIME_WAIT state? Maybe you don't ned waiting for ACKs(TCP_NODELAY)?
Then your newConnection function can look like this:
void MyTcpServer::newConnection()
{
qDebug() << "NEW CONNECTION " << __LINE__;
QTcpSocket * socket = server->nextPendingConnection();
int flag = 1;
struct linger l = {1,0};
if(setsockopt(socket->socketDescriptor(), SOL_SOCKET, SO_REUSEADDR, (const char *)&flag, sizeof(int)) < 0)
{
qDebug() << "ERROR: Can't set SO_REUSEADDR";
}
if(setsockopt(socket->socketDescriptor(), IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) < 0)
{
qDebug() << "ERROR: can't fork set TCP_NODELAY";
}
socket->write("Hello client\r\n");
socket->flush();
socket->waitForBytesWritten(3000);
if(setsockopt(socket->socketDescriptor(), SOL_SOCKET, SO_LINGER, (const char *)&l,sizeof(l)) < 0)
{
qDebug() << "ERROR: Can't set SO_LINGER";
}
socket->close();
}
This will just do the job and free any used socket resource so they can be reused just right after that. Usefull for heavyload microservers, etc..

Related

QSerial without QThreads

I create a "server" lib with a Qt GUI.
I don't and can't use QThreads because this is supposed to be as independent from Qt as possible, and because I have other threads already working like a Ethernet part.
The thread ExternalRs232Thread() is lauched by the public function RunExternalRs232()
RunExternalRs232() opens the serial port, returns -1 if can't open serial, and if is ok runs the function ExternalRs232Thread() in a detached thread.
Initially, I have tried to run this with Serialib, but this never works properly with the Qt project. So I decided to give QSerialPort a try like this:
Server.h
#ifndef SERVER_H
#define SERVER_H
#include <iostream>
#include <winsock2.h>
#include <windows.h>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <sstream>
#include <time.h>
#include <chrono>
#include <mutex>
#include <QObject>
#include "libs/json.hpp"
#include <QtSerialPort>
#include "hdlccsvparser.h"
using namespace std;
using json = nlohmann::json;
using std::chrono::milliseconds;
using std::chrono::duration_cast;
using std::chrono::seconds;
using std::chrono::system_clock;
struct Contact {
int port; //udp port of te contact
time_t time; //last communication date ( is still active ? )
};
class Server : public QObject
{
Q_OBJECT
public:
Server();
virtual ~Server();
[...]
//return 0 if sucess , 1 port is not usabe, 2 port already in use
int RunExternalRs232(string PortCom, unsigned int baudrate);
int StopExternalRs232(string PortCom);
[...]
//clean exit
void ExitServer();
signals:
[...]
private:
[...]
// External ThreadS and exiting loop of threadS in set (if key d'ont exist anymore, exit loop)
mutex mtxExternalRs232;
unordered_set<string> ExternalRs232PortActiveList;
void ExternalRs232Thread(QSerialPort* Rs232Connection, string port);
[...]
};
#endif // SERVER_H
Server.cpp
#include "Server/Server.h"
#include <winsock2.h>
/* Public Part*/
[...]
int Server::RunExternalRs232(string PortCom, unsigned int baudrate){
//PortCom = (char *)"COM1";
//baudrate = 9600;
QSerialPort Rs232Connection;
// Connection to serial port
// COMxx
Rs232Connection.setPortName(QString::fromStdString(PortCom));
Rs232Connection.setBaudRate(baudrate);
Rs232Connection.setDataBits(QSerialPort::Data8);
Rs232Connection.setParity(QSerialPort::NoParity);
Rs232Connection.setStopBits(QSerialPort::OneStop);
Rs232Connection.setFlowControl(QSerialPort::NoFlowControl);
int i = Rs232Connection.open(QIODevice::ReadWrite);
// If connection fails, errorOpening != 1
if(i != 1){
return i;
}
mtxExternalRs232.lock();
ExternalRs232PortActiveList.insert(PortCom);
mtxExternalRs232.unlock();
cout << "External Rs232 starting on " << PortCom << endl;
stringstream sstmp;
sstmp << PortCom;
emit LogMessage(QString::fromStdString("External Rs232 starting on " + sstmp.str()),0);
std::thread thServer(&Server::ExternalRs232Thread,this,&Rs232Connection,PortCom);
thServer.detach();
return 0;
}
int Server::StopExternalRs232(string PortCom){
PortCom = "COM1";
mtxExternalRs232.lock();
/*
if(ExternalRs232PortActiveList.count(PortCom) != 1){ //???
//port not in use
cerr << "close Failed Rs232 " << PortCom << endl;
mtxExternalRs232.unlock();
return 1;
}*/
cout << "close External Rs232 " << PortCom << endl;
ExternalRs232PortActiveList.erase(PortCom);
mtxExternalRs232.unlock();
return 0;
mtxExternalRs232.lock();
cout << ExternalRs232PortActiveList.count(PortCom) << " here " << endl;
mtxExternalRs232.unlock();
}
[...]
/* Private Part */
/* Thread part */
[...]
void Server::ExternalRs232Thread(QSerialPort * Rs232Connection, string port){
//char/binary read on rs232
char tmp;
//if rs232 has data
bool res = false;
stringstream message;
Rs232Connection->write("H");
mtxExternalRs232.lock();
int iscount = ExternalRs232PortActiveList.count(port);
mtxExternalRs232.unlock();
while(iscount == 1){
res = false;
if(!Rs232Connection->waitForReadyRead(300)){
//no data skipp
} else {
QByteArray datas = Rs232Connection->readAll();
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
string tmp = codec->toUnicode(datas).toStdString();
cout << tmp << endl;
}
mtxExternalRs232.lock();
iscount = ExternalRs232PortActiveList.count(port);
mtxExternalRs232.unlock();
}
Rs232Connection->flush();
Rs232Connection->close();
}
[...]
/* functions part */
[...]
Server::Server(){
//init winsock
if(WSAStartup(MAKEWORD(2,2),&wsa) != 0){
cerr << "Could not init winsock2 : " << WSAGetLastError();
}
//creating a socket
if((s = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET){
cerr << "Could not create a socket : " << WSAGetLastError();
}
}
Server::~Server(){
//dtor
}
at the end, I get
QObject::startTimer: Timers can only be used with threads started with QThreads
So how is possible to run Serial Rs232 without using QThreads?

Qt Wrapper class on libWebSockets - not emitting a signal from the callback method

I am writing a WebSocket client application, based on the example, where the client application needs to pass the WebSocket Protocol while establishing connection with the server. Since QtWebSockets does not support the Websocket protocols, I am writing a C++ wrapper to use libWebSockets library and emit
connected, disconnected, textReceived kind of signals similar to QWebSocket.
My client application is able to connect to the server and is receiving the text message from the server, and I have copied minimum example code below, here I am facing an issue that when I emit a signal from the callback function the signal is not actually published and the slot I have connected to this signal never executed. I have verified that the object address passed in the session data is correct (copied the Logs of my program). What am I doing wrong here.
WebSocket.h
class WebSocket : public QObject
{
Q_OBJECT
public:
WebSocket(QObject *parent = Q_NULLPTR);
~WebSocket();
signals:
void connected();
void disconnected();
void textMessageReceived(const QString &message);
private:
static int callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len);
struct lws_context *context;
struct lws *client_wsi;
static const struct lws_protocols protocols[];
};
WebSocket.cpp
const struct lws_protocols WebSocket::protocols[] = {
{
"dumb_protocol",
callback_dumb_increment,
sizeof(per_session_data),
0,
},
{ NULL, NULL, 0, 0 }
};
WebSocket::WebSocket(QObject *parent) : QObject(parent)
{
struct lws_context_creation_info info;
struct lws_client_connect_info i;
int n = 0;
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
info.protocols = protocols;
qDebug() << "[parent] address: " << this;
info.user = this;
/*
* since we know this lws context is only ever going to be used with
* one client wsis / fds / sockets at a time, let lws know it doesn't
* have to use the default allocations for fd tables up to ulimit -n.
* It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we
* will use.
*/
info.fd_limit_per_thread = 1 + 1 + 1;
context = lws_create_context(&info);
if (!context) {
qDebug() << "lws init failed";
}
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
i.context = context;
i.port = 7070;
i.address = "localhost";
i.path = "/";
i.host = i.address;
i.origin = i.address;
i.pwsi = &client_wsi;
lws_client_connect_via_info(&i);
while (n >= 0 && client_wsi)
n = lws_service(context, 0);
}
int WebSocket::callback_dumb_increment( struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len )
{
/* This will be same for every connected peer */
void *userdata = lws_context_user(lws_get_context(wsi));
qDebug() << "userData address: " << userdata;
QString message = "";
switch (reason) {
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
if (in)
qDebug() << "CLIENT_CONNECTION_ERROR: " << (char *)in;
else
qDebug() << "CLIENT_CONNECTION_ERROR: (null)";
wsi = NULL;
break;
case LWS_CALLBACK_CLIENT_ESTABLISHED:
qDebug() << __func__ << " established";
emit static_cast<WebSocket*>(userdata)->connected();
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
message = QString::fromUtf8((const char *)in);
qDebug() << "RX: " << message;
emit static_cast<WebSocket*>(userdata)->textMessageReceived(message);
break;
case LWS_CALLBACK_CLIENT_CLOSED:
wsi = NULL;
emit static_cast<WebSocket*>(userdata)->disconnected();
break;
default:
break;
}
return lws_callback_http_dummy(wsi, reason, user, in, len);
}
SocketClient.h
class SocketClient : public QObject
{
Q_OBJECT
public:
explicit SocketClient(QObject *parent = Q_NULLPTR);
~SocketClient();
public slots:
void onConnected();
void onTextMessageReceived(QString message);
private:
std::shared_ptr<WebSocket> webSock = nullptr;
};
SocketClient.cpp
SocketClient::SocketClient(QObject *parent) :QObject(parent)
{
webSock = std::make_shared<WebSocket>(this);
connect(webSock .get(), &WebSocket::connected, this, &SocketClient::onConnected);
connect(webSock .get(), &WebSocket::textMessageReceived,
this, &SocketClient::onTextMessageReceived);
}
Logs:
[parent] address: WebSocket(0x1b2cae32140)
userData address: 0x1b2cae32140
WebSocket::callback_dumb_increment established
userData address: 0x1b2cae32140
RX: "Hello World"

Not able to get poco 1.6.1 or 1.7.6 to bind to ipv6 with Net::DatagramSocket

Using the following code I can bind to an ipv4 address but not to a scope global ipv6 address that is also bound to this same machine. I am compiling the code like this:
g++ -lPocoFoundation -lPocoXML -lPocoUtil -lPocoNet -lcrypto -lssl -I/usr/include/Poco -o pocoudpipv6 pocoudpipv6.cpp
When I execute ./pocoudpipv6 10.X.X.X, it holds open the socket and cycles on "Waiting..." until I hit ctrl-c, which is expected. ss reports the socket:
# ss -nelup |grep 20000
UNCONN 0 0 10.X.X.X:20000 *:* users:(("pocoudpipv6",pid=2444,fd=3)) uid:1000 ino:14526705 sk:2a <->
But when I execute with ./pocoudpipv6 2001:X:X:X::X:X, this occurs:
We're resetting the ipaddress from ::1 to 2001:X:X:X::X:X
Address family is ipv6
Failure launching. Error was Net Exception: Address family not supported
This problem occurs with 1.7.6 on Slackware64 14.2 as well as with 1.6.1 on Debian 8 jessie amd64. As far as I've read, ipv6 being enabled in Poco is supposed to be the default. Is there something else I need to do in order to get this test-case to work with ipv6?
And I do have at least one daemon that is binding to an ipv6 socket on this machine:
udp UNCONN 0 0 2001:X:X:X::X:X:123 :::* users:(("ntpd",pid=2133,fd=22)) ino:5247 sk:19 v6only:1 <->
Thanks in advance!
Code as follows:
#include <iostream>
#include <Net/DatagramSocket.h>
#include <Net/SocketAddress.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
struct sigaction sigact = { 0 };
struct io_handling {
uint8_t exit_value;
};
struct io_handling io_handler = { 0 };
static void sigint_signal_handler(int sig)
{
if(sig == SIGINT) {
std::cout << std::endl << "Commencing shutdown in 5... 4... 3... 2... 1..." << std::endl;
io_handler.exit_value = 1;
}
}
static void cleanup(void)
{
sigemptyset(&sigact.sa_mask);
}
int main(int argc, char **argv)
{
Poco::Net::DatagramSocket *pSocket = new Poco::Net::DatagramSocket();
Poco::UInt16 port = 20000;
Poco::Net::IPAddress *ipAddress = new Poco::Net::IPAddress("::1");
if(argc == 2) {
delete ipAddress;
ipAddress = new Poco::Net::IPAddress(argv[1]);
std::cout << std::endl << "We're resetting the ipaddress from ::1 to " << argv[1];
}
std::cout << std::endl << "Address family is ";
if(ipAddress->family() == static_cast<Poco::Net::IPAddress::Family>(Poco::Net::Impl::IPAddressImpl::IPv6)) {
std::cout << "ipv6 ";
} else if(ipAddress->family() == static_cast<Poco::Net::IPAddress::Family>(Poco::Net::Impl::IPAddressImpl::IPv4)) {
std::cout << "ipv4 ";
} else {
std::cout << "something else, something very wrong.";
}
try {
sigact.sa_handler = sigint_signal_handler;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigaction(SIGINT, &sigact, (struct sigaction *)NULL);
pSocket->bind(Poco::Net::SocketAddress(*ipAddress, port));
while(!io_handler.exit_value) {
sleep(1);
std::cout << std::endl << "Waiting...";
}
} catch(Poco::Exception& ex) {
std::cout << std::endl << "Failure launching. Error was " << ex.displayText() << std::endl;
}
delete pSocket;
delete ipAddress;
return 0;
}
Self-resolved. The lib I was working with was only calling up Poco::Net::DatagramSocket's default ctor, which then gives an ipv4-only socket. Calls to ::bind() will not reset that based upon the input ip address.
The solution is to not new pSocket until handling ipAddress in the example above, and then passing that and the port casted as Poco::Net::SocketAddress to the ctor, and it will create the proper socket type and automatically bind it.

Ssl Server using QSslSocket in Qt

I have implemented a ssl server using QSslSocket and run it correctly. But I have some problem with it that I couldn't solve them immediately.
I thought that just connecting readyRead() signal to a slot for reading buffer is sufficient to do that but I have recognized that the readyRead() does not emit at all in this situation and I must also use waitForReadyRead() function in my code. But the problem is using this function cause blocking read the buffer. Actually I want to know how I can read buffer when data has arrived without blocking?
Bellow is my implemented ssl server:
#include "sslserver.h"
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QFile>
#include <QtNetwork/QSslKey>
#include <QtNetwork/QSslConfiguration>
#include <QtNetwork/QSslError>
SslServer::SslServer(QObject *parent) : QTcpServer(parent)
{
server = new QTcpServer(this);
if(!server->listen(QHostAddress::Any, 9996))
{
qDebug() << "Server could not start";
}
else
{
qDebug() << "Server started!";
}
connect(server, SIGNAL(newConnection()), this, SLOT(newConnectionRecognized()));
}
void SslServer::showErrors()
{
this-> err = socket->sslErrors();
for(int i=0;i<err.size();i++)
qDebug() << err[i];
}
SslServer::~SslServer()
{
}
void SslServer::newConnectionRecognized()
{
incomingConnection(server->nextPendingConnection()->socketDescriptor());
}
void SslServer::incomingConnection(qintptr socketDescriptor)
{
socket = new QSslSocket(this);
socket->setProtocol(QSsl::SslV3);
connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(showErrors()));
connect(socket, SIGNAL(encrypted()), this, SLOT(ready()));
connect(socket, SIGNAL(readyRead()), this, SLOT(readChannel()));
// Read Key from file
QByteArray key;
QFile KeyFile("server.key");
if(KeyFile.open(QIODevice::ReadOnly))
{
key = KeyFile.readAll();
KeyFile.close();
}
else
{
qDebug() << KeyFile.errorString();
}
QSslKey sslKey(key, QSsl::Rsa);
socket->setPrivateKey(sslKey);
// Load server ssl certificate from file
QByteArray cert;
QFile CertFile("server.csr");
if(CertFile.open(QIODevice::ReadOnly))
{
cert = CertFile.readAll();
CertFile.close();
}
else
{
qDebug() << CertFile.errorString();
}
QSslCertificate sslCert(cert);
socket->setLocalCertificate(sslCert);
QSslConfiguration cfg = socket->sslConfiguration();
cfg.caCertificates();
if (!socket->setSocketDescriptor(socketDescriptor))ee
{
qDebug() << ("! Couldn't set socket descriptor");
delete socket;
return;
}
socket->startServerEncryption();
if (socket->isEncrypted())
emit socket->encrypted();
if(!socket->waitForEncrypted(3000)) {
qDebug("Wait for encrypted!!!!");
return;
}
while (true) {
socket->waitForReadyRead();
}
}
void SslServer::readChannel()
{
QByteArray qstrbytes = socket->readLine();
qDebug() << qstrbytes;
}
void SslServer::ready()
{
qDebug() << "Encrypted";
}
I have found the problem when I implement another client/server but this time with QTcpSocket. I dont know exactly why but I guess the problem is because of using socketDescriptor for creating a QSslSocket. When I created client and server with QTcpSocket they works perfectly without any event loop and only by connecting readyRead() signal to an slot. After that in order to testing some situation I have create QTcpSocket using socketDescriptor. Then I found the problem is from creating socket using socketDescriptor because this time the readyRead() signal doesn't work as before.

Qt Threading code different behavior in MAC,Linux and Windows

I have written code for a server which accepts connections from different clients. Each client is serviced in different threads. Each thread accesses a database to get data and then updates this data to all the clients connected to server.
1) For the first time when UI asks data from server, it responds properly, but after that server does not read the socket i.e. Server's readyread() doesn't get invoked. Funnily enough, this works fine in mac and linux, this issue is seen only on windows
2) I was able to verify that when the DB module emits a signal which is caught by the threads, the hang occurs, Because everything worked fine when I removed the emit.
Here, I am attaching all the needed .h and .cpp codes
Defn.h
#ifndef DEFN_H
#define DEFN_H
struct PresetData{
QString ID;
QString name;
QString value;
QString source;
};
#endif // DEFN_H
main.cpp
#include <QCoreApplication>
#include "myserver.h"
#include "mydb.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyDB db;
MyServer server(&db);
server.startServer();
return a.exec();
}
mydb.h
#ifndef MYDB_H
#define MYDB_H
#include <QObject>
#include <QtSql>
#include "Defn.h"
class MyDB : public QObject
{
Q_OBJECT
public:
explicit MyDB(QObject *parent = 0);
signals:
void dataAvailable(QString ID, QString name, QString value, QString source);
public slots:
void onUpdateData(QString ID, QString name, QString value, QString source);
void onGetData(QString ID, QString name, QString value, QString source);
private:
QSqlDatabase m_db;
};
#endif // MYDB_H
mydb.cpp
#include "mydb.h"
MyDB::MyDB(QObject *parent) :
QObject(parent)
{
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setConnectOptions();
m_db.setDatabaseName("D:/MySimulator/New Folder/TCPServer1/DB.db");
if (m_db.open()){
qDebug() << "DB opened succesfully" ;
}else{
qDebug() << "DB Opening failed" ;
}
QStringList tables = m_db.tables();
if (tables.contains("Presets", Qt::CaseInsensitive)){
qDebug() << "DB Contains Data" ;
return;
}
}
void MyDB::onGetData(QString ID, QString name, QString value, QString source)
{
qDebug() << "onGetData" ;
QString queryString = "SELECT Value from 'Presets' where ID = \'" + ID + "\'";
QSqlQuery q;
bool result = q.exec(queryString);
if (result){
if (q.next()){
value = q.value(q.record().indexOf("Value")).toString();
qDebug() << " Retrieved Value = " << value ;
emit dataAvailable(ID, name, value, source);
}else{
qDebug("Empty Result");
}
}else{
qDebug("NO Result");
}
}
void MyDB::onUpdateData(QString ID, QString name, QString value, QString source)
{
qDebug() << "onUpdateData" ;
QString queryString = "UPDATE 'Presets' SET Value = \'" + value + "'\ WHERE ID = \'" + ID + "\'";
QSqlQuery q;
QSqlDatabase::database().transaction();
bool result = q.exec(queryString);
if (result){
QSqlDatabase::database().commit();
onGetData(ID, name, "", "000");
}else{
qDebug("NO Result");
}
}
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QTcpSocket>
#include <QAbstractSocket>
#include <QDebug>
#include "Defn.h"
#include "mydb.h"
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(int ID, MyDB* db, QObject * parent = 0);
void run();
void parseInput(QString string);
signals:
void error(QTcpSocket::SocketError socketError);
void updateData(QString ID, QString name, QString value, QString source);
void getData(QString ID, QString name, QString value, QString source);
public slots:
void readyRead();
void disconnected();
void onDataAvailable(QString ID, QString name, QString value, QString source);
private:
QTcpSocket* socket;
int socketDescriptor;
MyDB* db;
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include "qtcpserver.h"
#include "qabstractsocket.h"
MyThread::MyThread(int ID, MyDB* db, QObject * parent ):
QThread(parent)
{
this->socketDescriptor = ID ;
this->db = db;
}
void MyThread::run()
{
// thread starts here.
qDebug() << socketDescriptor << "Starting Thread" ;
socket = new QTcpSocket();
if (!socket->setSocketDescriptor(this->socketDescriptor)){
emit error(socket->error());
return;
}
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection);
connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()), Qt::DirectConnection);
connect(this, SIGNAL(getData(QString, QString , QString , QString )), this->db, SLOT(onGetData(QString , QString , QString , QString )));
connect(this, SIGNAL(updateData(QString , QString , QString , QString )), this->db, SLOT(onUpdateData(QString , QString , QString , QString )));
connect(this->db, SIGNAL(dataAvailable(QString , QString , QString , QString )), this, SLOT(onDataAvailable(QString , QString , QString , QString )));
qDebug() << socketDescriptor << "Client Connected" ;
exec();
}
void MyThread::readyRead()
{
QByteArray data = socket->readAll();
qDebug() << socketDescriptor << "Data in: " << data;
parseInput(data);
}
void MyThread::disconnected()
{
qDebug() << socketDescriptor << "Disconnected" ;
socket->deleteLater();
exit(0);
}
void MyThread::parseInput(QString dataFromTCP)
{
qDebug() << socketDescriptor << ":" <<"parseInput " << dataFromTCP;
if (dataFromTCP.isEmpty())
return;
QStringList list1 = dataFromTCP.split("\n", QString::SkipEmptyParts);
qDebug() << socketDescriptor << ":" << "list1 BEGIN";
for (int i = 0 ; i < list1.count(); i++)
{
qDebug() << i<< ":" << list1.at(i);
}
qDebug() << socketDescriptor << ":" << "list1 END";
if (list1.count() < 1){
return;
}
QString strMessage = "";
for (int i = 0 ; i < list1.count() ; i++)
{
strMessage = list1[i];
QStringList list2 = strMessage.split(" ", QString::SkipEmptyParts);
qDebug() << socketDescriptor << ":" << "list2 BEGIN";
for (int i = 0 ; i < list2.count(); i++)
{
qDebug() << i<< ":" << list2.at(i);
}
qDebug() << socketDescriptor << ":" << "list2 END";
if (list2.count() < 1){
break;
}
QString ID = list2[1];
QString source = QString::number(socketDescriptor) ;
if (list2[0] == "GET"){
emit getData(ID, "", "", source);
}
else if (list2[0] == "UPD"){
QString value = list2[2];
emit updateData(ID, "", value, source);
}
}
}
void MyThread::onDataAvailable(QString ID, QString name, QString value, QString source)
{
if( (QString::number(socketDescriptor) == source) || ("000" == source ) ) {
qDebug() << socketDescriptor << " : On Data Available " << ID << name << value ;
QString data = "DATA " + ID + " " + value + " " + "\n" ;
QByteArray ba;
ba.append(data);
socket->write(ba);
}
}
myserver.h
#ifndef MYSERVER_H
#define MYSERVER_H
#include <QDebug>
#include <QObject>
#include <QTCPServer>
#include <QTCPSocket>
#include "mythread.h"
#include "mydb.h"
class MyServer: public QTcpServer
{
Q_OBJECT
public:
explicit MyServer(MyDB* pdb, QObject* parent = 0);
void startServer();
signals:
public slots:
protected:
void incomingConnection(qintptr socketDescriptor);
private:
MyDB* pdb ;
};
#endif // MYSERVER_H
myserver.cpp
#include "myserver.h"
MyServer::MyServer(MyDB* pdb, QObject* parent ):
QTcpServer(parent)
{
this->pdb = pdb;
}
void MyServer::startServer()
{
if (!this->listen(QHostAddress::Any, 1234)){
qDebug() << "Could not Start Server " << this->errorString();
}
else{
qDebug() << " Server Running... ";
}
}
void MyServer::incomingConnection(qintptr socketDescriptor)
{
qDebug() << socketDescriptor << " Connecting... ";
MyThread *thread = new MyThread(socketDescriptor, pdb, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
Here the signal about which I mentioned above is dataAvailable from "mydb.cpp". If I comment out that line then server responds to client messages. But if that signal is emitted then after the initial response, the server seems to hang and no longer reacts to incoming messages from the client.
The same code is working perfectly fine in mac and linux. But it is having this problem in Windows only.
Could someone let me know what is it that I am doing wrong that it is failing only in Windows?
Thanks in advance for helping me out.
EDIT:
The objective of this code is that whenever a thread causes an update call to the database, EVERY thread including the one that called the update gets informed about the change. So it is EXPECTED that other thread that runs at that time also receives a signal.
This is what is expected of the server:
Be able to allow TCP connections from multiple clients simultaneously.
If any client requests info, it gets the required data over the TCP connection.
If any client updates info, all clients including the updating client, gets a notifications over the TCP connection.
Well, for starters, your code is completely not thread-safe. You create a single instance of MyDB in your main() function, then call it from threads without protecting its data member. Also, signals get emitted, updating data without any protection. What if two threads happen to be running at the same time?
Secondly, and this is more important: whenever you emit dataAvailable() you call functions in other thread objects in your own thread. This is the code path when data arrives:
MyThread::parseInput() emits
MyThread::getData(), which is connected to
MyDB::onGetData(), which emits
MyDb::dataAvailable, which is connected to (drumroll....)
MyThread::onDataAvailable, which eventually calls
socket->write()
So if data arrives in thread #1, you're going to send data from MyThread object #2, #3, #4, etc from .... thread #1. Depending on the OS, this is bad news. I don't know enough about Windows threads but I do know this code is terminally broken.
If all you want to do is update a database and relay the data you can dispense with the threads and use a sequential program that handles sockets using the regular Qt signals and slots just fine.

Resources