Writing a chat using Qt. Got a problem. My client's QTcpSocket remains in connecting state, but the server emits newConnection() signal. Network session is not required. Why is that? Here is some code:
ChatClient::ChatClient(QObject *parent)
: QObject(parent) {
tcpSocket = new QTcpSocket(this);
QNetworkConfigurationManager manager;
if (QNetworkConfigurationManager::NetworkSessionRequired
& manager.capabilities()) {
qDebug() << "Network session required";
}
connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(error(QAbstractSocket::SocketError)));
connect(tcpSocket, SIGNAL(connected()),
this, SLOT(requestForID()));
connect(tcpSocket, SIGNAL(readyRead()),
this, SLOT(receiveMessage()));
tcpSocket->connectToHost("192.168.0.100", PORT);
}
void ChatClient::requestForID() {
qDebug() << "Connected, requesting for ID";
QByteArray segment;
QDataStream out(&segment, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_7);
out << (quint16)0 << ID;
out.device()->seek(0);
out << (quint16)(segment.size() - sizeof(quint16));
tcpSocket->write(segment);
}
requestForID() is never being executed
ChatServer::ChatServer(QObject *parent)
: QObject(parent) {
tcpServer = new QTcpServer(this);
if (!tcpServer->listen(QHostAddress::Any, PORT)) {
qDebug() << "Unable to start the server"
<< tcpServer->errorString();
}
qDebug() << "Server port" << tcpServer->serverPort();
connect(tcpServer, SIGNAL(newConnection()),
this, SLOT(processConnection()));
}
void ChatServer::processConnection() {
qDebug() << "Incoming connection";
QTcpSocket *clientSocket = tcpServer->nextPendingConnection();
/*connect(clientSocket, SIGNAL(readyRead()),
this, SLOT(readData()));
readData(clientSocket);
connect(clientSocket, SIGNAL(disconnected()),
clientSocket, SLOT(deleteLater()));*/
QByteArray segment;
QDataStream out(&segment, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_7);
out << (quint16)0 << (quint16)Message
<< "Successfully connected";
out.device()->seek(0);
out << (quint16)(segment.size() - sizeof(quint16));
clientSocket->write(segment);
clientSocket->disconnectFromHost();
}
server displays incoming connection and the client does not emit connected remaining in connecting state, doesnt receive server message as well...
Any ideas?
I had the same problem and I found the reason and a solution.
connectToHost may not be called in the contstructor of the main window directly. Why?
The reason is, that the main message loop is not running yet at this time. Internally QAbstractSocketPrivate::fetchConnectionParameters() gets never called and Qt Socket Timeout Timer thinks connection is never established.
The solution is either to call it "delayed" like in
QMetaObject::invokeMethod(this, "OnDelayedConnect", Qt::QueuedConnection);
Or to call waitForConnected() after connectToHost
Related
I want to make simple communication example on Qt Remote Objects. I want to make the communication peer-to-peer, therefore I'm trying to merge both Source and Replica of the same remote object functionality in one application (REPC_MERGED tool used to generate Source and Replica base classes).
#include <QtCore/QCoreApplication>
#include "MyPeerHost.h"
#include "Client.h"
#include <QDebug>
static QString peer_node_name(int number)
{
QString ret = QString("peer_%1").arg(number);
return ret;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyPeerHost peerHost; // just inherits auto-generated MyPeerSimpleSource
QUrl thisAddress = "local:" + peer_node_name(0);
QRemoteObjectHost sourceNode(thisAddress);
if(sourceNode.enableRemoting(&peerHost))
{
qInfo() << "Source remoting enabled successfully" << thisAddress;
QUrl remoteAddress = "local:" + peer_node_name(1);
QSharedPointer<MyPeerReplica> replica;
QRemoteObjectNode replicaNode;
if(replicaNode.connectToNode(remoteAddress))
{
qInfo() << "Replica connected to the address" << remoteAddress << "successfully";
replica.reset(replicaNode.acquire<MyPeerReplica>());
QString sourceClassName = peerHost.staticMetaObject.className();
qDebug() << "Replica wait for Source" << sourceClassName << "...";
if(replica->waitForSource(1000))
{
qInfo() << "Replica object completely initialized";
Client client;
client.setReplicaObject(replica);
client.sendMessage("AAA");
}
else
{
qCritical() << "Replica wait for Source" << sourceClassName << "FAILED" << replicaNode.lastError();
}
}
else
{
qCritical() << "Replica connect to the address" << remoteAddress << "FAILED" << replicaNode.lastError();
}
}
else
{
qCritical() << "Source remoting enable FAILED" << sourceNode.lastError();
}
return a.exec();
}
Application output:
Source remoting enabled successfully QUrl("local:peer_0")
Replica connected to the address QUrl("local:peer_1") successfully
Replica wait for Source "MyPeerHost" ...
Replica wait for Source "MyPeerHost" FAILED QRemoteObjectNode::NoError
As you see, replicaNode successfully connected to the non-existent node QUrl("local:peer_1").
What I am doing wrong?
You don't have valid Qt code.
Qt relies on an event loop to handle asynchronous behavior, which is started by the a.exec() at the end of your main() routine. Qt Remote Objects, in turn, relies on the event loop for all of its communication.
In your code, you create your objects on the stack, but in code blocks that go out of scope before you start the event loop. They will therefore be destructed before the event loop is kicked off.
I'd recommend starting with some of the examples, make sure they work, then grow what you are trying to do from there.
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.
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.
The following code is from an example shows how to use QNetworkAccessManager to download things.
void Downloader::replyFinished (QNetworkReply *reply)
{
if(reply->error())
{
qDebug() << "ERROR!";
qDebug() << reply->errorString();
}
else
{
qDebug() << reply->header(QNetworkRequest::ContentTypeHeader).toString();
qDebug() << reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().toString();
qDebug() << reply->header(QNetworkRequest::ContentLengthHeader).toULongLong();
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
QFile *file = new QFile("C:/Qt/Dummy/downloaded.txt");
if(file->open(QFile::Append))
{
file->write(reply->readAll());
file->flush();
file->close();
}
delete file;
}
reply->deleteLater();
}
My question is do we have to call reply->deleteLater(); here? If we don't call it, when we perform QNetworkAccessManager::get() call the second time, will the QNetworkReply* in the slot be the same QNetworkReply* ?
If you don't call deleteLater(), the QNetworkReply object will be leaked and its memory not freed. A second get() call will create a new QNetworkReply object.
I am struggling with qnetworkaccessmanager for quite sometime. I googled a lot, but I donot find a solution for this.
I am creating a client using qaccessmanager to talk with a rest server. QNetworkReply is not returning any results. The server is working properly but the client is not returning results. On top of that the server gets called 3 times and sometimes the server is crashing. Hope some one can figure out what is going wrong. I am attaching the client code.
I tried different approches like connecting finished signal of networkaccessmanager, qnetworkreply e.t.c. But all of them ends up in giving the same error "Connection Closed" or the readAll bytearray being empty.
void RestClientCore::ConnectToServer()
{
m_NetworkManager = new QNetworkAccessManager(this);
QUrl url("http://localhost");
url.setPort(5432);
QByteArray postData;
postData.append("/?userid=user");
postData.append("&site=site");
QNetworkReply *reply = m_NetworkManager->post(request,postData);
connect(reply, SIGNAL(readyRead()),this, SLOT(slotReadyRead()));
connect(reply, SIGNAL(finished()), this, SLOT(onRequestCompleted()));
}
void RestClientCore::onRequestCompleted() {
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
if(reply->error())
{
qDebug() <<reply->bytesAvailable() << reply->errorString();
}
else
{
qDebug() << reply->readAll();
}
reply->deleteLater();
}
void RestClientCore::slotReadyRead()
{
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
qDebug() << reply->readAll();
}
Thanks in advance
Regards
Rejo