I faced a problem with QUdpSocket. Signal readyRead() seems to be never emitted. So, I decided to create QTimer and check state of socket reading queue. That way I ensured that socket working properly (bytesAvailable() shows number of bytes) and signal/slot mechanism is working too (timeout() signal occurred). But why readyRead() doesn't emit? Thanks.
Qt 5.1
QString EthernetListener::listen()
{
udp_socket = new QUdpSocket(this);
connect(udp_socket, SIGNAL(readyRead()), this, SLOT(process_messages()));
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(dummy_slot()));
timer->start(1000);
bool res = udp_socket->bind(QHostAddress::Any, 1947, QUdpSocket::ShareAddress);
if (!res)
return QString("Не удалось подключиться к хосту").toUtf8();
return QString("Идет прослушка сети. Хост: ");
}
void EthernetListener::dummy_slot()
{
int test = udp_socket->bytesAvailable();
}
void EthernetListener::process_messages()
{
bool bp = true;
}
This problem can occur if more data becomes available while still processing the first datagram. Add this to the slot and it should work fine:
int readCount;
while (udpSock->hasPendingDatagrams())
{
readCount = udpSock->readDatagram(buffer, 4096);
cout << "readCount = " << readCount << endl;
}
You can try implementing basic receiver using this one: Udp Receiver
Probably you are not reading correctly from the slot so it looks like the signal is not emitted. Or you are calling bind after you connected the slot: probably you should call bind before connecting the slot.
Related
I have problem with synchronize(start at the same time QTCPSocket) in my application I have 10 sockets. I have to read data at the similar time to all sockets. At this moment I have something that:
///...///
if(!socket->waitForConnected(-1))
{
qDebug() << "Server not found";
emit serverNotFound();
}else if(socket->state()==QAbstractSocket::ConnectedState){
qDebug() << "Connected"
connect(timer, SIGNAL(timeout()),this,SLOT(connected()));
timer->start(1000);
}
}
On connected signal:
void SocketsClass::connected()
{
sendRequest(socket, messageToServer);
}
The problem is that when the first socket get connected the timer starts for the one.
You can invert your approach. Don't wait for the sockets to get connected. Instead, check if the socket is connected in the slot activated by the timer. In that slot, you can iterate over all sockets and send the message to each of them.
Finally, you should never use Qt's waitForXxx methods, they lead to a horrible pseudo-synchronous code that is very error prone and hard to extend and maintain. Use the signal-slot mechanism instead.
Example:
SocketManager : public QObject {
Q_OBJECT
QTcpSocket m_sockets[10];
QTimer m_timer;
public:
SocketManager(QObject * parent = 0) : QObject(parent) {
... // connect all sockets here
m_timer.start(1000);
connect(&m_timer, &QTimer::timeout, [this]{
for (auto & socket : m_sockets)
if (socket.state() == QAbstractSocket::ConnectedState)
sendRequest(socket, messageToServer);
});
}
};
I work with QTcpSocket. I need any write/read calls to the socket to be synchronous (blocking).
I know there is waitForReadyRead() and waitForBytesWritten(), but those two methods are marked in Qt documentation as they can fail randomly under Windows. I cannot affort this.
The blocking read is the most important (as reading comes always after writting a command to the other peer, so I know that if data reaches the other peer, it will answer).
I have tried 2 approaches.
First:
QByteArray readBytes(qint64 count)
{
int sleepIterations = 0;
QByteArray resultBytes;
while (resultBytes.size() < count && sleepIterations < 100)
{
if (socket->bytesAvailable() == 0)
{
sleepIterations++;
QThread::msleep(100);
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
continue;
}
resultBytes += socket->read(qMin(count, socket->bytesAvailable()));
}
return resultBytes;
}
This should wait for bytes to be available for reading on the socket, processing the event loop in the mean time, so the socket is doing it's necessary internal stuff.
Unfortunately - for unknown to me reason - the bytesAvailable() sometimes returns correct number of bytes, but sometimes it never returns anything greater than 0.
I know in fact that there was data to be read, because it used to work with the second approach (but it has it's own problems).
Second:
I have a kind of signal "blocker", which blocks current context and processes event loop, until certain signal is emitted. This is the "blocker":
SignalWait.h:
class SignalWait : public QObject
{
Q_OBJECT
public:
SignalWait(QObject *object, const char *signal);
bool wait(int msTimeout);
private:
bool called = false;
private slots:
void handleSignal();
};
SignalWait.cpp:
SignalWait::SignalWait(QObject* object, const char* signal) :
QObject()
{
connect(object, signal, this, SLOT(handleSignal()));
}
bool SignalWait::wait(int msTimeout)
{
QTime timer(0, 0, 0, msTimeout);
timer.start();
while (!called && timer.elapsed() < msTimeout)
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
return called;
}
void SignalWait::handleSignal()
{
called = true;
}
and then I used it like this:
SignalWait signalWait(socket, SIGNAL(readyRead()));
// ...
// socket->write(...);
// ...
if (!signalWait.wait(30000))
{
// error
return;
}
bytes = socket->read(size);
This approach seems to be working better, but it also fails from time to time. I don't know why. It's like the readyRead() signal was never emitted and the SignalWait keeps waiting, until it times out.
I'm out of ideas. What is the proper way to deal with it?
I would suggest to use the asynchronous approach but if you really want to go with the synchronous way, then a better way is to use a local event loop:
QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
loop.connect(socket, SIGNAL(readyRead()), SLOT(quit()));
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
while (resultBytes.size() < count)
{
timer.start(msTimeout);
loop.exec();
if(timer.isActive())
resultBytes += socket->read(qMin(count, socket->bytesAvailable()));
else
break;
}
Here it waits until count bytes are read or the the timeout reaches.
I'm writing a threaded TcpServer (each client in its own thread) using QTcpServer and QTcpSocket. The client application works correctly and sends data every 3 seconds but the readReady() signal never fires, meaning my receive_data() function is never called. When using socket->waitForReadyRead() and calling receive_data() by myself everything works fine. Please have a look at the code below, maybe I made some mistake with the moveToThread / connect functionality Qt offers.
Client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <QThread>
#include <QTcpSocket>
#include <QHostAddress>
#include "PacketDefinitions.h"
#include "tcpserver.h"
class Client : public QObject
{
Q_OBJECT
public:
explicit Client(int socket,TcpServer *parent,bool auto_disconnect = true);
~Client();
bool isGameServer(){return is_gameserver;}
GameServerPacket getGameServerData(){return gameserver;}
void run();
private:
QTcpSocket* client;
TcpServer *parent_server;
int socket;
GameServerPacket gameserver;
ClientPacket clientdata;
bool is_gameserver;
bool auto_disconnect;
QHostAddress client_ip;
quint16 client_port;
signals:
void disconnected(Client *);
private slots:
void remove_from_clientlist();
void receive_data();
void display_error(QAbstractSocket::SocketError error);
};
#endif // CLIENT_H
Client.cpp
#include "client.h"
#include "PacketDefinitions.h"
#include "time.h"
#include <iostream>
Client::Client(int _socket, TcpServer *parent,bool _auto_disconnect)
{
auto_disconnect = _auto_disconnect;
parent_server = parent;
is_gameserver = false;
socket = _socket;
}
void Client::run(){
client = new QTcpSocket();
if(client->setSocketDescriptor(socket) == false){
std::cout << client->errorString().toStdString() << std::endl;
remove_from_clientlist();
return;
}
connect(client,SIGNAL(disconnected()),this,SLOT(remove_from_clientlist()));
if(connect(client,SIGNAL(readyRead()),this,SLOT(receive_data()),Qt::DirectConnection) == false) return;
connect(client,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(display_error(QAbstractSocket::SocketError)));
client_ip = client->peerAddress();
client_port = client->peerPort();
std::cout << "New incomming connection " << client->peerAddress().toString().toStdString() << ":" << client->peerPort() << std::endl;
//this works fine
// while(client->waitForReadyRead()){
// receive_data();
// }
}
void Client::receive_data(){
QDataStream stream(client);
stream.setVersion(QDataStream::Qt_5_2);
quint32 magic; stream >> magic;
//interpret data
if(magic == GAMESERVER_MAGIC){
is_gameserver = true;
gameserver.Read(stream);
gameserver.port = client_port;
gameserver.ip = client_ip;
time(&(gameserver.last_update));
parent_server->add_server(gameserver.ip.toString(),gameserver);
std::cout << "GameServer " << gameserver.name << " registerd" << std::endl;
}else if(magic == CLIENT_MAGIC){
is_gameserver = false;
clientdata.Read(stream);
//get nearby servers
GameServerListPacket server_list = parent_server->getServerList(clientdata);
QDataStream outstream(client);
server_list.Write(outstream);
std::cout << "Sending ServerList(" << server_list.server_count << ") to " << client->peerAddress().toString().toStdString() << std::endl;
if(auto_disconnect){
//client->flush();
client->waitForBytesWritten();
}
}else{
std::cout << "Unknown package " << magic << std::endl;
}
//not enough data read, somthing is wrong, just for debugging
if(client->bytesAvailable()> 0) std::cout << "BytesAvailable " << client->bytesAvailable() << std::endl;
if(auto_disconnect) remove_from_clientlist();//close the connection once the serverlist was deployed
}
In the TcpServer.cpp add_client() is called when newConnection() was emitted by the QTcpServer:
void TcpServer::add_client(){
while(server->hasPendingConnections()){
QTcpSocket *socket = 0;
if(thread_pool.size() < max_connections && (socket = server->nextPendingConnection())){
QThread *thread = new QThread();
Client * client = new Client(socket->socketDescriptor(),this,auto_disconnect);
client->moveToThread(thread);
client->run();
thread->start();
connect(client,SIGNAL(disconnected(Client*)),this,SLOT(remove_client(Client*)));
WRITELOCK(thread_pool.insert(client,thread));
}
}
}
the order calling client->run() and thread->start() doesn't seem to matter. Some time ago the code (not this exact code) worked fine but I can't remember what I changed that made it fail. Any help is appreciated!
Thanks in advance
Fabian
Edit 1:
I derived from QTcpServer and reimplemented void incomingConnection(qintptr socketDescriptor) which works fine. I dont use QThreadPool, its just a QMap and remove_client(Client*) closes the QTcpSocket and stops the thread and removes it from the map. Everything works fine on linux, on windows I get the following error: QSocketNotifier: socket notifiers cannot be disabled from another thread ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread....
Caused by this remove_client(Client*)
void TcpServer::remove_client(Client *client){
//disconnect(client,SIGNAL(disconnected(Client*)),this,SLOT(remove_client(Client*)));
lock.lockForWrite();
QMap<Client*,QThread*>::iterator itr = thread_pool.find(client);
if(itr != thread_pool.end()){
//delete itr.key(); causes the problem on windows
itr.value()->quit();
itr.value()->wait();
delete itr.value();
thread_pool.erase(itr);
}
lock.unlock();
}
Where and how should I free the Client object? If i'd use QThreadPool theres no way to iterate through the clients in case i want to send a message to more than one client. I could use a list/map holding only the Client* but then QThreadPool might delete them for me right before i want to access it. Any suggestions?
There is a problem with how you move your client object to a new thread. Actually, Client::run executes in the same thread as TcpServer::add_client.
Also QTcpSocket client remains in the default thread, while its container (Client class) is moved to a new thread. That's why the connection with Qt::DirectConnection type doesn't work.
Try this:
class Client : public QObject
{
Q_OBJECT
...
public slots:
void run();
...
}
Client::Client(int _socket, TcpServer *parent,bool _auto_disconnect)
{
...
client = new QTcpSocket(this);
}
void Client::run()
{
...
connect(client, SIGNAL(readyRead()), this, SLOT(receive_data()));
...
}
And here's how you should move your client to a new thread:
void TcpServer::add_client()
{
...
QThread *thread = new QThread();
Client * client = new Client(socket->socketDescriptor(),this,auto_disconnect);
client->moveToThread(thread);
connect(thread, SIGNAL(started()), client, SLOT(run()));
thread->start();
...
}
There are a number of things wrong with your code.
1.You have two QTcpSocket object trying to collect data from the same underlying socket descriptor. You appear to use the first one only to get access to the socket descriptor value which you then pass to your Client class. You might end up losing data because you won't be able to tell which socket will be getting what data from the operating system.
If you are creating a derived class of QTcpServer, rather reimplement QTcpServer::incomingConnection(qintptr socketDescriptor) instead of your existing TcpServer::add_client() function. Since this protected function is called once for every new connection, you don't need to make any connections to the newConnection() signal, nor do you have to loop while new connections are pending. You will also then only have one QTcpSocket connected to each socket descriptor so you won't lose data.
2.You seem to be using QThreadPool to manage threads. If you make Client a derived class of QRunnable (take not that with multiple inheritance of QObject, QObject must always be first), you don't need to check the maximum connections and you can eliminate all the QThread boiler-plating.
Taking 1. and 2. into account, your TcpServer::add_client() function will be replaced with:
void TcpServer::incomingConnection(qintptr socketDescriptor){
Client * client = new Client(socketDescriptor,this,auto_disconnect);
connect(client,SIGNAL(disconnected(Client*)),this,SLOT(remove_client(Client*)));
QThreadPool::globalInstance()->start(client);
}
With QThreadPool, there's no need to check whether the max number of threads has been reached or not. If the maximum has been reached, any new calls to start() will queue the next connection until a thread becomes available.
3.The reason your socket is not reading any data unless you call waitForReadyRead() is because you're executing the run() function in the main thread, creating the local socket in the main thread, you make a DirectConnection with the instance of Client and then move client to a different thread. You cannot have direct connections between threads.
You will need to add a local QEventLoop to your run() function to handle all events and signals of your new thread but remember to connect signals to your loop's quit() slot so the run() function will exit, otherwise your thread will continue to run forever.
I am writing a server as a Qt console application. I have the server set up to wait for a socket connection, but I also need to allow a user to input commands into the server for managing it. Both are working independently. However, the problem I ran into is that when I'm in a while loop accepting and processing input commands, the server doesn't accept connections.
I have a Socket class, and in its constructor, I have:
connect(server,SIGNAL(newConnection()),this, SLOT(newConnection()));
Right under that in the constructor, I call a function that has a more in-depth version of this for getting commands from the user:
QTextStream qin(stdin, QIODevice::ReadOnly);
QString usrCmd;
while(usrCmd != "exit" && usrCmd != "EXIT") {
//Get command input and process here
}
Inside newConnection(), I just accept the next connection and then use the socket.
QTcpSocket *serverSocket = server->nextPendingConnection();
How can I make it so the socket can wait for connections and wait for user-inputed commands at the same time?
Problem with your code is because you are blocking event loop with your while loop. So, the solution to your problem is to read from stdin asynchronously. On Linux (and on Mac, I guess), you can use QSocketNotifier to notify when the data is arrived on stdin, and to read it manually), as per various internet sources.
As I am using Windows, I would suggest you to do it in this way (which should work on all platforms):
Open the thread for reading data from stdin
Once you get some data (perhaps line?) you can use Qt signal-slot mechanism to pass the data to main thread for processing without blocking the event loop.
So, this is the pseudocode. MainAppClass should your existing server class, just edit the constructor to create new thread, and add new slot for processing the data.
class Reader: public QThread
{
Q_OBJECT
public:
Reader(QObject * parent = 0 ): QThread(parent){}
void run(void)
{
forever{
std::string data;
std::getline (std::cin, data);
if(data == "exit")
{
emit exitServer();
return;
}
emit dataReady(QString::fromStdString(data));
}
}
signals:
void dataReady(QString data);
void exitServer();
};
class MainAppClass: public QObject
{
Q_OBJECT
public:
MainAppClass()
{
Reader * tr = new Reader(this);
connect(tr, SIGNAL(dataReady(QString)), this, SLOT(processData(QString)));
connect(tr, SIGNAL(exitServer()), this, SLOT(exitServer()));
tr->start();
}
public slots:
void processData(QString data)
{
std::cout << "Command: " << data.toStdString() << std::endl;
}
void exitServer()
{
std::cout << "Exiting..." << std::endl;
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainAppClass myapp; //your server
app.exec();
return 0;
}
Since I wrote simple guidelines how to use QTcpSocket, here is the brief
When you get client QTcpSocket, connect readyRead() signal to some slot, and read data from sender() object. You don't need to read anything in the constructor.
For reading you can use standard QIODevice functions.
Note: this is pseudo code, and you may need to change few things (check the state of the stream on reading, save pointer to sockets in some list, subscribe to disconnected() signal, call listen() in constructor, check if QTcpServer is listening, etc).
So, you need to have slot onReadyRead() in your class which will have the following code:
void Server::readyReadSlot()
{
QTcpSocket *client = (QTcpSocket*)sender(); // get socket which emited the signal
while(client->canReadLine()) // read all lines!
// If there is not any lines received (you may not always receive
// whole line as TCP is stream based protocol),
// you will not leave data in the buffer for later processing.
{
QString line = client->readLine();
processLine(line); // or emit new signal if you like
}
}
Inside newConnection() you need to connect readyRead() signal with your slot.
void Server::newConnection()
{
QTcpSocket *clientSocket = server->nextPendingConnection();
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
}
I've this code here in a program I'm making but I've a problem, How can I keep the program waiting for data on http2 before returning to the tcpserver class? And how can I get the data in the tcpserver class?
This is like a checkpoint were I need to get the data from the server and then keep running the tcpserver and use that data there.
tcpserver.cpp
#include "tcpserver.h"
#include "protocol.h"
#include "http2.h"
QTextStream in(stdin);
tcpserver::tcpserver(QObject *parent) :
QObject(parent)
{
server = new QTcpServer(this);
[ ... Other Server Stuff ... ]
http2 *h = new http2(this);
}
I've tried this with no luck:
http2.cpp
#include "http2.h"
bool httpdonne = false;
QByteArray finaldata;
http2::http2(QObject *parent, QByteArray url, QByteArray data) :
QObject(parent)
{
url.append(data);
m_manager = new QNetworkAccessManager(this);
connect(m_manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(httpdown(QNetworkReply*)));
QNetworkRequest request;
request.setUrl(QUrl(url));
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
m_manager->get(request);
while ( httpdonne == false ) {
}
finaldata.append("HTTP: ");
qDebug() << finaldata;
}
QByteArray http2::httpdown(QNetworkReply* result)
{
QByteArray data = result->readAll();
finaldata = data;
httpdonne = true;
return data;
}
Thanks a lot! ;)
Qt makes use of signals and slots and the event loop. If you have no QEventLoop running which is normally provided by QApplication your events will not be handled.
Take a peek at this question to see how to create an event loop to simulate a blocking (synchronous) using the asynchronous programming model.
In addition I normally add a QTimer too:
QEventLoop loop;
..
QTimer timer;
timer.setInterval(2000);
timer.setSingleShot(true);
loop.connect(&timer, SIGNAL(timeout()), SLOT(handleTimeout()));
timer.start();
..
//Setup your objects / connections here...
..
loop.exec(); //Your signals and slots will be triggered / handled now!
Qt network classes are all signal-driven. That is, you create functions which handle various events (data ready etc) and connect them to the appropriate slots. Blocking isn't really the way they are to be used.
Of course I would never recommend what you are trying to achieve but if you still wanna "shoot yourself in the foot" :) then here's what you can try,
run the manager as a separate thread and make the http2 or tcpserver thread wait on
a condition variable. And when the manager is done invoke the threads
waiting the the condition variable.
You can use my WaitForSignalHelper class posted here , it does what you want: wait for a timeout OR signal to be emitted...