Unsolved Impossible to bind QTcpSocket again after connection error
-
Hi,
in one our application we encountered a problem with reconnecting to the server over a TCP connection after accidentally disconnecting network cable. After some examination I found that the problem is related to binding QTcpSocket. After a fixed network cable QTcpSocket::bind() got stuck on QAbstract::SocketError = 7 (Connection time out) or QAbstract::SocketError = 7 (Host unreachable).
I created a test class with one QTcpSocket instance for manually call QTcpSocket methods connectToHost(), bind(), disconnectFromHost() etc. The source code is below.
SocketConnector.h
#ifndef SOCKETCONNECTOR_H #define SOCKETCONNECTOR_H #include <QTcpSocket> #include <QObject> #include <QPushButton> #include <QLineEdit> class SocketConnector: public QObject { Q_OBJECT public: SocketConnector(); void setUi(QLineEdit * leBindAddressW, QLineEdit * leHostAddressW, QLineEdit * leHostPortW); private: QTcpSocket socket; QLineEdit *leBindAddress = nullptr; QLineEdit *leHostAddress = nullptr; QLineEdit *leHostPort = nullptr; QString getSocketState() const; public slots: // Slots for QPushButtons void onPbState() const; void onPbBind(); void onPbConnect(); void onPbDisconnect(); void onPbClose(); void onPbAbort(); private slots: // Slots for QTcpSocket signals void onConnect(); void onDisconnected(); void onError(QAbstractSocket::SocketError error); void onStateChanged(QAbstractSocket::SocketState state); }; #endif // SOCKETCONNECTOR_H
SocketConnector.cpp
#include "SocketConnector.h" #include <QTcpSocket> #include <QPushButton> #include <QHostAddress> #include <QDebug> #include <QString> SocketConnector::SocketConnector(): QObject(nullptr) { connect(&socket, SIGNAL(connected()), this, SLOT(onConnect())); connect(&socket, SIGNAL(disconnected()), this, SLOT(onDisconnected())); connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError))); connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onStateChanged(QAbstractSocket::SocketState))); } void SocketConnector::setUi(QLineEdit *leBindAddressW, QLineEdit *leHostAddressW, QLineEdit *leHostPortW) { leBindAddress = leBindAddressW; leHostAddress = leHostAddressW; leHostPort = leHostPortW; } QString SocketConnector::getSocketState() const { return QString("SocketState: %1, SocketError: %2 - %3") .arg(socket.state()) .arg(socket.error()) .arg(socket.errorString()); } // ----------------------------------------------------------------------------- // Slots for QTcpSocket signals void SocketConnector::onConnect() { qDebug() << "Slot onConnected():" << getSocketState(); } void SocketConnector::onDisconnected() { qDebug() << "Slot onDisconnected():" << getSocketState(); } void SocketConnector::onError(QAbstractSocket::SocketError error) { qDebug() << "Slot onError(): error=" << error << " - " << getSocketState(); } void SocketConnector::onStateChanged(QAbstractSocket::SocketState state) { qDebug() << "Slot onStateChanged(): state=" << state << getSocketState(); } // ----------------------------------------------------------------------------- // Slots for QPushButton activating actions bind, connection etc. void SocketConnector::onPbState() const { qDebug() << "onPbState():" << getSocketState(); } void SocketConnector::onPbBind() { QHostAddress bindAddress = QHostAddress(leBindAddress->text()); qDebug() << "onPbBind(): Pressed"; bool ok = socket.bind(bindAddress, 0, QAbstractSocket::ReuseAddressHint); qDebug() << "onPbBind(): ok =" << ok << getSocketState(); } void SocketConnector::onPbConnect() { QString hostAddress = leHostAddress->text(); int hostPort = leHostPort->text().toInt(); qDebug() << "onPbConnect(): Pressed"; socket.connectToHost(hostAddress, hostPort); qDebug() << "onPbConnect():" << getSocketState(); } void SocketConnector::onPbDisconnect() { qDebug() << "onPbDisconnect(): Pressed"; socket.disconnectFromHost(); qDebug() << "onPbDisconnect():" << getSocketState(); } void SocketConnector::onPbClose() { qDebug() << "onPbClose(): Pressed"; socket.close(); qDebug() << "onPbClose():" << getSocketState(); } void SocketConnector::onPbAbort() { qDebug() << "onPbAbort(): Pressed"; socket.abort(); qDebug() << "onPbAbort():" << getSocketState(); }
The methods begins with prefix onPb are slots for signals from QPushButtons which are connected outside of class SocketConnector. This QPushButtons activates actions of QTcpSocket: bind(), connectToHost() etc.
The scenario what I have been tried was:
I disconnected the network cable and called bind() and after that I called connectToHost(). The debug output was:
onPbBind(): Pressed
Slot onStateChanged(): state= QAbstractSocket::BoundState "SocketState: 4, SocketError: -1 - Unknown error"
onPbBind(): ok = true "SocketState: 4, SocketError: -1 - Unknown error"
onPbConnect(): Pressed
Slot onStateChanged(): state= QAbstractSocket::HostLookupState "SocketState: 1, SocketError: -1 - Unknown error"
Slot onStateChanged(): state= QAbstractSocket::ConnectingState "SocketState: 2, SocketError: -1 - Unknown error"
Slot onStateChanged(): state= QAbstractSocket::UnconnectedState "SocketState: 0, SocketError: 7 - Host unreachable"
Slot onError(): error= QAbstractSocket::NetworkError - "SocketState: 0, SocketError: 7 - Host unreachable"
onPbConnect(): "SocketState: 0, SocketError: 7 - Host unreachable"I connect the network cable again and called bind() with same parameters as before.
onPbBind(): Pressed
Slot onError(): error= QAbstractSocket::NetworkError - "SocketState: 0, SocketError: 7 - Host unreachable"
onPbBind(): ok = false "SocketState: 0, SocketError: 7 - Host unreachable"After this step QTcpSocket::bind() is always failing and QTcpSocket::error() signal have been emitted with SocketError: 7 - Host unreachable. I have been tried to call QTcpSocket::disconnectFromHost(), QTcpSocket::close(), QTcpSocket::abort() but nothing helps. The binding keeps failing with the same error emitted.
But strange thing occurred in this error state when connectToHost() was called.
onPbConnect(): Pressed
Slot onStateChanged(): state= QAbstractSocket::HostLookupState "SocketState: 1, SocketError: 7 - Unknown error"
Slot onStateChanged(): state= QAbstractSocket::ConnectingState "SocketState: 2, SocketError: 7 - Unknown error"
onPbConnect(): "SocketState: 2, SocketError: 7 - Unknown error"
Slot onStateChanged(): state= QAbstractSocket::ConnectedState "SocketState: 3, SocketError: 7 - Unknown error"
Slot onConnected(): "SocketState: 3, SocketError: 7 - Unknown error"
onPbDisconnect(): Pressed
Slot onStateChanged(): state= QAbstractSocket::ClosingState "SocketState: 6, SocketError: 7 - Unknown error"
Slot onStateChanged(): state= QAbstractSocket::UnconnectedState "SocketState: 0, SocketError: 7 - Unknown error"
Slot onDisconnected(): "SocketState: 0, SocketError: 7 - Unknown error"
onPbDisconnect(): "SocketState: 0, SocketError: 7 - Unknown error"The connection was successful and after successful disconnection the binding started to work again.
onPbBind(): Pressed
Slot onStateChanged(): state= QAbstractSocket::BoundState "SocketState: 4, SocketError: 7 - Unknown error"
onPbBind(): ok = true "SocketState: 4, SocketError: 7 - Unknown error"
onPbConnect(): Pressed
Slot onStateChanged(): state= QAbstractSocket::HostLookupState "SocketState: 1, SocketError: 7 - Unknown error"
Slot onStateChanged(): state= QAbstractSocket::ConnectingState "SocketState: 2, SocketError: 7 - Unknown error"
onPbConnect(): "SocketState: 2, SocketError: 7 - Unknown error"
Slot onStateChanged(): state= QAbstractSocket::ConnectedState "SocketState: 3, SocketError: 7 - Unknown error"
Slot onConnected(): "SocketState: 3, SocketError: 7 - Unknown error"onPbDisconnect(): Pressed
Slot onStateChanged(): state= QAbstractSocket::ClosingState "SocketState: 6, SocketError: 7 - Unknown error"
Slot onStateChanged(): state= QAbstractSocket::UnconnectedState "SocketState: 0, SocketError: 7 - Unknown error"
Slot onDisconnected(): "SocketState: 0, SocketError: 7 - Unknown error"
onPbDisconnect(): "SocketState: 0, SocketError: 7 - Unknown error"Only one solution I have been found is to create QTcpSocket dynamically by new operator and after error occurs delete QTcpSocket and create new one for new connection. But IMHO this is rather workaround than solution.
Why binding is failing after a fixed network cable? Is there any solution to fix binding?
-
@Karlor said in Impossible to bind QTcpSocket again after connection error:
Why binding is failing after a fixed network cable? Is there any solution to fix binding?
Use
QUdpSocket
. You bind UDP sockets (as UDP is a conectionless protocol) and you connect TCP sockets to your known host (as the TCP sockets are connection-oriented), so unless I'm missing something you're just doing it wrong.Either use
bind()
withQUdpSocket
orconnectToHost
withQTcpSocket
. -
@Karlor
Like @kshegunov says, for TCP connections you will usebind()
,listen()
&accept()
only at the server side, andconnect()
only at the client side. I don't know about his UDP suggestion, that's a different matter. -
Do you mean the OS' functions in that last post, because I meant Qt's API.
-
@kshegunov
Is that addressed to me?In my reply I was attempting to back up your previous reply. Perhaps I gave too much (non-Qt) information. I haven't even looked up what Qt names its functions, sounds like with
QTcpSocket
you usebind()
only at server side andconnectToHost()
only at client side. -
@JonB said in Impossible to bind QTcpSocket again after connection error:
Is that addressed to me?
Yep, says to whom I replied rightwards of the post's date.
In my reply I was attempting to back up your previous reply. Perhaps I gave too much (non-Qt) information.
Mhm, I got that. I Just wondered whether the functions you mentioned you meant as the C functions one'd usually use for networking.
-
@kshegunov
Yes, indeed they are the C functions I used years ago, and still recall fondly :) They were native under UNIX, and provided under same names by WinSock. IMHO, if you want to get anything done and still understand what's going on underneath all the obfuscation layers C++ attempts to pile on, you need to know what it's ultimately calling at the OS level, because that's all the C++ still has available to it ;-) -
@JonB said in Impossible to bind QTcpSocket again after connection error:
all the obfuscation layers C++
We call them abstraction layers, you see. ;)
-
Thank you for your answers.
I have realized I have missed maybe something important. On client side computers are installed multiple network interfaces.
I have been informed that the binding in the client side part of our software is implemented for purpose to connect with appropriate network interface to connect to the right server and must use
QTcpSocket::bind()
withQAbstractSocket::ReuseAddressHint
.But even with multiple network interfaces I think @kshegunov's answer is still right. I have tested connections to servers without binding and everything works fine.
I would like to ask to some subquestions to this topic.
- I am confused by Qt documentation for QAbstractSocket::bind() where is written
For TCP sockets, this function may be used to specify which interface to use for an outgoing connection, which is useful in case of multiple network interfaces.
Even with multiple network interfaces the binding is not mandatory and only
connectToHost()
to IP server address should be sufficient, isn't it?- Is it true that binding in mode
QAbstractSocket::ReuseAddressHint
in calling
socket.bind(bindAddress, 0, QAbstractSocket::ReuseAddressHint)
is useless, because it will use any available port (the second argument is 0) and reusing has no effect in this case?
-
@Karlor said in Impossible to bind QTcpSocket again after connection error:
Even with multiple network interfaces the binding is not mandatory and only connectToHost() to IP server address should be sufficient, isn't it?
If it connects then it should be sufficient. I haven't had this particular case, however I imagine the
bind()
is required to specify your outgoing address (a.k.a. interface), so you're using the correct network. If there isn't ambiguity then I supposeconnectToHost
would do on its own.Is it true that binding in mode QAbstractSocket::ReuseAddressHint in calling
[snippet]That line looks correct, yes.
-
Fortunately in our case the connection via
QTcpSocket::connectToHost()
without needQTcpSocket::bind()
is sufficient. However, this may change in the future, and it may need to specify the outgoing network interface, so the problem of stacked binding ofQTcpSocket
will be there again.I found hypothetic scenario in which things might go worse if an error occurred. Imagine having two network interfaces labeled as N1 and N2. N1 is disconnected from the network. If the user accidentally decides to bind via
QTcpSocket::bind()
to N1, the connection fails. Changing the connection to N2 is now impossible. I guess it is impossible becauseQTcpSocket
is still bounded to N1, so rebind to N2 fails and the connection to N2 is not possible.Do you know how to reset
QTcpSocket
to useQTcpSocket::bind()
again?