I am using QTcpSocket to send data between a client and a server.
The server works perfectly fine on Linux, yet on Windows it will only receive one message and not more after that. readyRead() is just never emitted again.
Does anyone know what could be the problem?
Some of the code which I deemed to be important for this question:
server sending:
void Server::sendData(Client *client, QString data)
{
if (client->socketDescriptor == socketDescriptor) {
data = data + CRLF;
// Have also tried to use socket->flush(), same problem.
socket->write(data.toUtf8());
socket->waitForBytesWritten();
}
}
server receiving:
// this slot is connected to the readyRead() signal
void Server::readData()
{
// on Windows this is called exactly once for each client
// every additional messages just don't seem to arrive at the server
QString msg = QString(socket->readAll());
emit receivedData(this, &msg);
}
I just don't get why it works on Linux, yet not on windows...
Related
I am trying to achieve a signal when the internet is disconnected for an already connected SSL socket. Here is the way I have derived the QSslSocket:
struct CloudSSL::Socket : public QSslSocket
{
Q_OBJECT public:
void ConnectSlots ()
{
connect(this, SIGNAL(readyRead()), this, SLOT(ReceiveData()));
connect(this, SIGNAL(disconnected()), this, SLOT(Disconnected()));
// *** None of the above or below is invoking when internet disconnects ***
connect(this, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(Error(QAbstractSocket::SocketError)));
}
virtual ~Socket ()
{
QObject::disconnect();
QSslSocket::abort();
}
public slots:
void ReceiveData ()
{
LOG("Socket received data...");
}
void Disconnected ()
{
LOG("Socket got disconnected...");
}
void Error (QAbstractSocket::SocketError error)
{
LOG("Socket error ", error);
}
}
Here is how it's initialized:
m_pSSLSocket = new Socket;
m_pSSLSocket->setProtocol(QSsl::TlsV1_2);
m_pSSLSocket->setLocalCertificateChain(QSslCertificate::fromPath(":/Certificate.pem", QSsl::Pem));
m_pSSLSocket->setPrivateKey(QSslKey(privateKeyFile.readAll(), QSsl::Rsa));
m_pSSLSocket->setSocketOption(QAbstractSocket::LowDelayOption, true); // <---
m_pSSLSocket->setSocketOption(QAbstractSocket::KeepAliveOption, true); // <---
m_pSSLSocket->connectToHostEncrypted(SAARATHY_URL, SAARATHY_PORT);
m_pSSLSocket->ignoreSslErrors();
Things work fine in general. However, if I disable wifi in my Ubuntu PC, then I don't get any network error as expected from the QAbstractSocket::SocketError:
QAbstractSocket::NetworkError -- 7 -- An error occurred with the network
(e.g., the network cable was accidentally plugged out).
Referred following posts before this Qn:
QTcpSocket state always connected, even unplugging ethernet wire
Qt TCP/IP socket connection check
Question: What is the Qt exclusive way of receiving a signal when the internet is disconnected?
Unless the protocols you use have some sort of keep-alive, if you don't send anything nothing will be sent and no attempt of error checking will be done.
If you want to see if there's problem with a connection you actually have to send something. If a cable is unplugged or there is any other problem between you and the remote host, then (after a suitable timeout and retries) you will get an error.
To see if the remote host has closed the connection in a nice way, you have to attempt to read something, in which case the receive call will return that it has read zero bytes.
I have been stuck on this for the past 5 days, I have no idea how to proceed.
Overview:
I have a client UI which interacts with a data handler library, and the data handler library utilizes a network manager library, which is where my problem lies.
More Info
Firstly, QT provides a basic example for interactions between a QTcpServer (Fortune Server)and a QTcpSocket (Fortune Client).
I thus implemented this code into an extremely basic example of my own, which works like a charm and has no issues.
My own adaption of fortune client and server for the record (basic)
Quick Explaination:
Server application runs, click on start server, then on the client side, enter text in field and click connect to server and text is displayed, easy!
Problem:
Implementing the code above into my network manager library, does not fire the QTcpSocket::readyRead() in the server application above.
It connects to the server, where the QTcpServer::newConnection() is fired, as expected, straight after which the client writes to the socket but the readyRead() on the server socket does not fire, however in the example given it does.
Note:
The same port and ip address is used in this server-client application example and my current application, and the server is also running.
Further Information:
From the above code, I copied over directly from the client. Only 2 things were changed/modified:
String that is sent to server
return types for method
This was copied into my network mannager ::write() method. When running my application, and instance of QMainWindow is passed via data handler class and creates an instance of my network manager class which inherits QObject and implements the Q_OBJECT macro.
Code Examples:
//client_UI Class (snippet):
data_mananger *dman = new data_mananger(this); //this -> QMainWindow
ReturnObject r = dman->NET_AuthenticateUser_GetToken(Query);
//data_manager library (snippet)
data_mananger::data_mananger(QObject *_parent) :
parent(_parent)
{}
ReturnObject data_mananger::NET_AuthenticateUser_GetToken(QString Query){
//Query like "AUTH;U=xyz#a;P=1234"
//convert query string to char
QByteArray ba = Query.toLatin1();
//send query and get QList return
ReturnCode rCode = networkManager.write(ba);
//...
}
//netman library (snippet)
//.h
class NETMANSHARED_EXPORT netman : public QObject
{
Q_OBJECT
public
netman();
netman(QObject *_parent);
//...
private:
QTcpSocket *tcp_con;
//...
};
//cpp
netman::netman(QObject *_parent) :
parent(_parent)
{
tcp_con = new QTcpSocket(parent);
}
return;
}
serverIP.setAddress(serverInfo.addresses().first().toIPv4Address());
}
ReturnCode netman::write(QByteArray message, int portNumber){
tcp_con->connectToHost(QHostAddress("127.0.0.1"), 5000);
if (!tcp_con->waitForConnected())
{
qDebug(log_lib_netman_err) << "Unable to connect to server";
return ReturnCode::FailedConnecting;
}
if (!tcp_con->isValid()) {
qDebug(log_lib_netman_err) << "tcp socket invalid";
return ReturnCode::SocketError;
}
if (!tcp_con->isOpen()) {
qDebug(log_lib_netman_err) << "tcp socket not open";
return ReturnCode::SocketError;
}
// QByteArray block(message);
QByteArray block;
QDataStream out(&block,QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << QString("Hello world");
if (!tcp_con->write(block)){
qDebug(log_lib_netman_err) << "Unable to send data to server";
return ReturnCode::WriteFailed;
}
else{
qDebug(log_lib_netman_info) << "Data block sent";
return ReturnCode::SentSuccess;
}
}
Conclusion:
The core code of the client side has been fully implemented, yet I cannot see why this error occurs.
I would very much appreciate help/advice!
Add a tcp_con->flush() statement to the end of your write function.
Why/how this works
You weren't getting a readyRead signal in your receiver because the written data was being buffered into the socket but not actually transmitted 'over the wire'. The flush() command causes the buffer to be transmitted. From the docs
This function writes as much as possible from the internal write
buffer to the underlying network socket, without blocking. If any data
was written, this function returns true; otherwise false is returned.
How are you supposed to know
In my case a lot of experience/frustration with serial ports and flushing. It's the equivalent of "have you rebooted it?" in the socket debugging toolbox.
If everything else is working fine, you may not have to flush, but it's kind of application specific and depends on the lifetime of the socket, the TCP window size, socket option settings, and various other factors. That said, I always flush because I like having complete control over my sockets, and I want to make sure data is transmitted when I want it to be. I don't think it's a hack, but in some cases it could be indicative of some other problem. Again, application specific.
Why might the buffer not be flushing itself?
I'm pretty sure no flush is needed in the fortune server example because they disconnectFromHost at the end of the sendFortune() function, and from the Qt documentation:
Attempts to close the socket. If there is pending data waiting to be
written, QAbstractSocket will enter ClosingState and wait until all
data has been written.
The socket would disconnect if it were destroyed as well, but from what I can see of your code you aren't doing that either, and the buffer isn't full, so probably nothing is actually stimulating the buffer to flush itself.
Other causes can be:
flow control isn't returned to the event loop (blocking calls, etc), so the buffer flush is never performed.
Transmit is occuring inside of a loop, which seems like it will exit (e.g. while(dataToTransmit)), but in fact the condition never becomes false, which leads to the event loop being blocked.
Nagles algorithm: the buffer may be waiting for more data before it flushes itself to keep network throughput high. You can disable this by setting the QAbstractSocket::LowDelayOption, but it may adversely affect your throughput... it's normally used for latency-sensative applications.
I have a need for a very simple server program which executes one of five different activities, based on client connections.
Since this is for a demo, I don't need any complex network handling, my intention was just to open up five server sockets (say 10001 thru 10005 inclusive) and simply await incoming connections.
Upon the server receiving an incoming connection on (for example) the first socket 10001, it would immediately accept and close the connection, then execute the first action. Ditto for the other sockets. That way, I could demo the actions from another window simply by executing:
telnet 127.0.0.1 10001
Normally, I would use select() with a very short timeout value (i.e., not too onerous on the event processing thread) to await and detect which port was being connected to but, since this is a Qt application, I'm not sure that will work so well with the Qt event model.
What would be the best way of doing this with Qt (5.5, if it matters)? Is the use of a small-timeout select() actually going to work or do I need to go heavyweight with five separate QTcpServer objects, each with their own infrastructure (callbacks and such)?
If I properly understand, you want handle all requests in one place. In Qt you can use signal/slot for it. Connect signals from all QTcpServer objects to one slot, something like:
// Method to fully start a server on specific port.
QTcpServer *MyClass::StartServer(int port) {
QTcpServer *server = new QTcpServer();
server->listen(QHostAddress::Any, port);
connect(server, SIGNAL(newConnection()), this, SLOT(HandleConn()));
return server;
}
// Method to start all servers, serverArr is an array of QTcpServer
// items (in the object, obviously).
void MyClass::StartAllServers() {
for (size_t i = 0; i < sizeof(serverArr) / sizeof(*serverArr); i++)
serverArr[i] = StartServer(10000 + i);
}
// Callback to handle connections.
void MyClass::HandleConn() {
// This will call doAction with parameter based on which QTcpServer
// has caused the callback.
QTcpServer *source = static_cast<QTcpServer*>(sender());
for (size_t i = 0; i < sizeof(serverArr) / sizeof(*serverArr); i++)
if (source == serverArr[i])
doAction(i);
// Action done, so just accept and close connection.
QTcpSocket *socket = source->nextPendingConnection();
socket->close();
}
I have compiled Qt's Trip Planner example that uses QTcpSocket and QTcpServer to create a client and server.
The problem is, the server's incomingConnection() slot is not being called. Even though the client connects to the server successfully. Therefore, the socket's readyRead() signal is never emitted and the client gets no data back.
tripserver.cpp
TripServer::TripServer(QObject *parent)
: QTcpServer(parent)
{
}
void TripServer::incomingConnection(int socketId)
{
qDebug() << "Incoming Connection";
ClientSocket *socket = new ClientSocket(this);
socket->setSocketDescriptor(socketId);
}
If I add a newConnection() slot, it gets called. So what is going on?
Found my answer.
The parameter list has changed since Qt 4.8.1
http://qt-project.org/doc/qt-5.0/qtnetwork/qtcpserver.html#incomingConnection
void TripServer::incomingConnection(qintptr socketId){}
I'm using Qt and trying to create a Client - Server connection. Whenever I click a button in my client application, a socket connects to the server and sends some data. The problem is I don't know how to receive the data. These are the slots for my buttons.
void MainWindow::func_button_one(){
socket->connectToHost("127.0.0.1", 1324);
if(socket->waitForConnected(1000)) {
socket->write("button one has been pressed");
socket->waitForBytesWritten(1000);
}
else {
qDebug() << "Something terrible seems to have happened.";
}
}
Now, in my server application, I tried something like this.
void MainWindow::newConnection(){
QTcpSocket *socket = server->nextPendingConnection();
socket->waitForReadyRead(1000);
qDebug() << "connection received";
qDebug() << socket->readAll();
socket->close();
}
The connection is all right, because the "connection received" message shows up. So, how am I supposed to receive the data from the client? QTcpServer doesn't have any read() function.
The connection is all right, because waitForReadyRead returned 'true', or the connection is not all right, because waitForReadyRead returned false after 1000ms. How would you know the difference?
Better work asynchronously with signals. Connect your socket to 'readyRead()'. Or at least test the return value of waitForReadyRead.