QAbstractSocket 从服务器端关闭连接时的奇怪行为

QAbstractSocket strange behavior when connection is closed from server side

本文关键字:连接 服务器端 QAbstractSocket      更新时间:2023-10-16

我正在实现一个简单的TCP套接字类,它继承自QAbstractSocket。我正在测试我的代码,然后我注意到QAbstractSocket呈现出一种奇怪的行为。当连接从服务器端结束时,QAbstractSocket 永远不会发出 disconnected(( 信号,但它会不断重复发出 readyRead(( 信号。

我正在将 SimpleTCPSocket 类信号连接到我的 Widget 类构造函数中的 Widget 插槽:

connect(simpleSocket, SIGNAL(readyRead()), this, SLOT(readDataFromSimpleSocket()));
connect(simpleSocket, SIGNAL(disconnected()), this, SLOT(onSimpleSocketDisconnect()));

这是我的插槽的实现:

void Widget::readDataFromSimpleSocket()
{
QString aux;
size_t sizeOfDataAvilable;
sizeOfDataAvilable = simpleSocket->bytesAvailable();
aux = "Message received from readDataFromSimpleSocket slot: bytes available ";
aux += QString::number(sizeOfDataAvilable);
qDebug() << aux;
dataFromServer = (char*) malloc (sizeof(char) * sizeOfDataAvilable);
simpleSocket->read(dataFromServer, sizeOfDataAvilable);
qDebug() << "All data has been read.";
}
void Widget::onSimpleSocketDisconnect()
{
qDebug() << "Disconnected!!";
}

当服务器应用程序关闭时,我收到 readReady(( 信号,但没有字节可供读取。这是我的输出。

"Message received from readDataFromSimpleSocket slot: bytes available 0"
All data has been read.

我得到的输出就像"永远"一样。无论在服务器端关闭连接后经过多少时间,我的 SimpleTCPSocket 类都不会发出 disconnected(( 信号。 这是我的套接字类的头文件:

#include "simpletcpsocket_global.h"
#include <QTcpSocket>
#include <QAuthenticator>
#include <QNetworkProxy>
class SIMPLETCPSOCKETSHARED_EXPORT SimpleTCPSocket : public QAbstractSocket
{
Q_OBJECT
public:
SimpleTCPSocket(QObject *parent = Q_NULLPTR);
void setHostInfo(const QString &hostName, quint16 port, QIODevice::OpenMode openMode = QIODevice::ReadWrite, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::AnyIPProtocol);
void setProxyInfo(QNetworkProxy::ProxyType proxyType, QString proxyHostName, quint16 proxyPort, QString username, QString password);
void connectSimpleSocketToHost();
private:
QNetworkProxy simpleSocketProxy;
QString proxyUser, proxyPassword;
QString hostName;
int hostPort;
QAbstractSocket::NetworkLayerProtocol protocol;
QIODevice::OpenMode openMode;
bool isHostSet, isConnectionOK, isProxySet;
private slots:
void onHostFound(); // # slot 1
void onSocketConnected(); // # slot 2
void onSocketError(QAbstractSocket::SocketError error); // # slot 3
void onUpdateSocketState(QAbstractSocket::SocketState state); // # slot 4
void onProxyAuthRequired(const QNetworkProxy &simpleSocketProxy, QAuthenticator *authenticator); // # slot 5
void onSocketAboutToClose(); // # slot 6
void onSocketClose(); // # slot 7
signals:
void remoteHostName(QString remoteHostName); // emmited whenever the host is found
void attemptToConnectFinished(QString resultMessage); // emitted when the method connect is executed, attempt to connect to host
void errorMessage(QString errorMessage); // emitted whenever an error occurs
void socketStateMessage(QString socketState); // emitted whenever the socket state is changed
void connectionInfo(QString connectionInformation); // emmited whenever the
};

这是我类的实现:

#include "simpletcpsocket.h"
#include <QDebug>
#include <QTime>
SimpleTCPSocket::SimpleTCPSocket(QObject *parent)
:QAbstractSocket(QAbstractSocket::TcpSocket, parent)
{
connect(this, SIGNAL(hostFound()), this, SLOT(onHostFound()));
connect(this, SIGNAL(connected()), this, SLOT(onSocketConnected()));
connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError)));
connect(this, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onUpdateSocketState(QAbstractSocket::SocketState)));
connect(this, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), this, SLOT(onProxyAuthRequired(const QNetworkProxy &, QAuthenticator *)));
connect(this, SIGNAL(aboutToClose()), this, SLOT(onSocketAboutToClose()));
connect(this, SIGNAL(disconnected()), this, SLOT(onSocketClose()));
isHostSet = false;
isConnectionOK = false;
isProxySet = false;
}
void SimpleTCPSocket::connectSimpleSocketToHost()
{
if (isProxySet)
{
this->setProxy(simpleSocketProxy);
}
else
{
this->setProxy(QNetworkProxy::NoProxy);
}
if (isHostSet)
{
QTime t;
t.start();
static_cast<QAbstractSocket*>(this)->connectToHost(hostName, hostPort, openMode, protocol);
emit attemptToConnectFinished("Attempt to connect to host ended. Time elapsed: " + QString::number(t.elapsed()) + " ms");
}
else
{
emit errorMessage("Host has not been set. You must set host name, port, open mode, and IP protocol first.");
}
}
void SimpleTCPSocket::onHostFound()
{
emit remoteHostName("Remote host name: " + static_cast<QAbstractSocket*>(this)->peerName());
}
void SimpleTCPSocket::onSocketConnected()
{
isConnectionOK = true;
QString connectionInfoString;
connectionInfoString = "Peer address: " + this->peerAddress().toString();
connectionInfoString += "; Peer port: " + QString::number(this->peerPort());
connectionInfoString += "; Local address: " + this->localAddress().toString();
connectionInfoString += "; Local port: " + QString::number(this->localPort());
connectionInfoString += ";";
emit connectionInfo(connectionInfoString);
}
void SimpleTCPSocket::onSocketError(QAbstractSocket::SocketError error)
{
switch(error)
{
case QAbstractSocket::ConnectionRefusedError:
{
emit errorMessage("The connection was refused by the peer (or timed out).");
break;
}
case QAbstractSocket::RemoteHostClosedError:
{
emit errorMessage("The remote host closed the connection. Note that the client socket (i.e., this socket) will be closed after the remote close notification has been sent.");
break;
}
case QAbstractSocket::HostNotFoundError:
{
emit errorMessage("The host address was not found.");
break;
}
case QAbstractSocket::SocketAccessError:
{
emit errorMessage("The socket operation failed because the application lacked the required privileges.");
break;
}
case QAbstractSocket::SocketResourceError:
{
emit errorMessage("The local system ran out of resources (e.g., too many sockets).");
break;
}
case QAbstractSocket::SocketTimeoutError:
{
emit errorMessage("The socket operation timed out.");
break;
}
case QAbstractSocket::DatagramTooLargeError:
{
emit errorMessage("The datagram was larger than the operating system's limit (which can be as low as 8192 bytes).");
break;
}
case QAbstractSocket::NetworkError:
{
emit errorMessage("An error occurred with the network (e.g., the network cable was accidentally plugged out).");
break;
}
case QAbstractSocket::AddressInUseError:
{
emit errorMessage("The address specified to QAbstractSocket::bind() is already in use and was set to be exclusive.");
break;
}
case QAbstractSocket::SocketAddressNotAvailableError:
{
emit errorMessage("The address specified to QAbstractSocket::bind() does not belong to the host.");
break;
}
case QAbstractSocket::UnsupportedSocketOperationError:
{
emit errorMessage("The requested socket operation is not supported by the local operating system (e.g., lack of IPv6 support).");
break;
}
case QAbstractSocket::ProxyAuthenticationRequiredError:
{
emit errorMessage("The socket is using a proxy, and the proxy requires authentication.");
break;
}
case QAbstractSocket::SslHandshakeFailedError:
{
emit errorMessage("The SSL/TLS handshake failed, so the connection was closed (only used in QSslSocket)");
break;
}
case QAbstractSocket::UnfinishedSocketOperationError:
{
emit errorMessage("Used by QAbstractSocketEngine only, The last operation attempted has not finished yet (still in progress in the background).");
break;
}
case QAbstractSocket::ProxyConnectionRefusedError:
{
emit errorMessage("Could not contact the proxy server because the connection to that server was denied");
break;
}
case QAbstractSocket::ProxyConnectionClosedError:
{
emit errorMessage("The connection to the proxy server was closed unexpectedly (before the connection to the final peer was established)");
break;
}
case QAbstractSocket::ProxyConnectionTimeoutError:
{
emit errorMessage("The connection to the proxy server timed out or the proxy server stopped responding in the authentication phase.");
break;
}
case QAbstractSocket::ProxyNotFoundError:
{
emit errorMessage("The proxy address set with setProxy() (or the application proxy) was not found.");
break;
}
case QAbstractSocket::ProxyProtocolError:
{
emit errorMessage("The connection negotiation with the proxy server failed, because the response from the proxy server could not be understood.");
break;
}
case QAbstractSocket::OperationError:
{
emit errorMessage("An operation was attempted while the socket was in a state that did not permit it.");
break;
}
case QAbstractSocket::SslInternalError:
{
emit errorMessage("The SSL library being used reported an internal error. This is probably the result of a bad installation or misconfiguration of the library.");
break;
}
case QAbstractSocket::SslInvalidUserDataError:
{
emit errorMessage("Invalid data (certificate, key, cypher, etc.) was provided and its use resulted in an error in the SSL library.");
break;
}
case QAbstractSocket::TemporaryError:
{
emit errorMessage("A temporary error occurred (e.g., operation would block and socket is non-blocking).");
break;
}
case QAbstractSocket::UnknownSocketError:
{
emit errorMessage("An unknown socket error occurred.");
break;
}
}
}
void SimpleTCPSocket::onUpdateSocketState(QAbstractSocket::SocketState state)
{
switch(state)
{
case QAbstractSocket::UnconnectedState:
{
isConnectionOK = false;
emit socketStateMessage("The socket is not connected.");
break;
}
case QAbstractSocket::HostLookupState:
{
emit socketStateMessage("The socket is performing a host name lookup.");
break;
}
case QAbstractSocket::ConnectingState:
{
emit socketStateMessage("The socket has started establishing a connection.");
break;
}
case QAbstractSocket::ConnectedState:
{
isConnectionOK = true;
emit socketStateMessage("A connection is established.");
break;
}
case QAbstractSocket::BoundState:
{
emit socketStateMessage("The socket is bound to an address and port.");
break;
}
case QAbstractSocket::ClosingState:
{
emit socketStateMessage("The socket is about to close (data may still be waiting to be written).");
break;
}
case QAbstractSocket::ListeningState:
{
emit socketStateMessage("For internal use only.");
break;
}
default:
{
emit socketStateMessage("Unkown state.");
}
}
}
void SimpleTCPSocket::onProxyAuthRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
{
qDebug() << "Proxy authentication required.";
this->setProxy(proxy);
this->proxy().setUser(proxyUser);
this->proxy().setPassword(proxyPassword);
authenticator->setUser(proxyUser);
authenticator->setPassword(proxyPassword);
}
void SimpleTCPSocket::onSocketAboutToClose()
{
qDebug() << "Connection about to close. Saving pending data to log (not implemented yet).";
}
void SimpleTCPSocket::onSocketClose()
{
qDebug() << "Connection closed. Saving and closing log (not implemented yet).";
isConnectionOK = false;
}
void SimpleTCPSocket::setHostInfo(const QString &hostName, quint16 port, QIODevice::OpenMode openMode, QAbstractSocket::NetworkLayerProtocol protocol)
{
this->hostName = hostName;
this->hostPort = port;
this->openMode = openMode;
this->protocol = protocol;
isHostSet = true;
}
void SimpleTCPSocket::setProxyInfo(QNetworkProxy::ProxyType proxyType, QString proxyHostName, quint16 proxyPort, QString username, QString password)
{
simpleSocketProxy.setType(proxyType);
simpleSocketProxy.setHostName(proxyHostName);
simpleSocketProxy.setPort(proxyPort);
simpleSocketProxy.setUser(username);
simpleSocketProxy.setPassword(password);
isProxySet = true;
}

我的代码可能有什么问题?

问候

我在Qt论坛上发布了这个问题,然后我得到了这个答案。 Raven-Worx写道:"你的实现没有任何问题。不幸的是,这是相当正常的行为(。所以,问题就"解决了"。