从服务器端关闭连接时的 QAbstractSocket 奇怪行为
Posted
技术标签:
【中文标题】从服务器端关闭连接时的 QAbstractSocket 奇怪行为【英文标题】:QAbstractSocket strange behavior when connection is closed from server side 【发布时间】:2017-08-23 04:48:40 【问题描述】:我正在实现一个从 QAbstractSocket 继承的简单 TCP 套接字类。我正在测试我的代码,然后我注意到 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;
我的代码可能有什么问题?
问候
【问题讨论】:
@WindyFields 我为我的班级添加了代码。这是一个非常简单的 QAbsracSocket 实现。 如果我是你,我会用 QTcpSocket 替换你的自定义套接字(出于调试目的),看看它是否有效。您明确地说:断开信号输出断开连接。所以如果它有效,那么你搞砸了你的 SimpleSocket 类,如果它没有,那么你的连接实际上并没有“断开”或者从套接字读取有一些问题......对不起,我没有看到任何其他原因如此奇怪的行为。此外,您可能应该尝试从 QDataStream like in the example 读取数据。 @WindyFields 我用 QTcpSocket 替换了我的自定义套接字,它工作正常。可能是我的实现有问题。 @WindyFields 感谢您对如何读取数据的提示。 【参考方案1】:我在 Qt 论坛上发布了这个问题,然后我得到了这个answer。 raven-worx 写道:“你的实现没有任何问题。不幸的是,这是相当正常的行为。”。这样,问题就“解决了”。
【讨论】:
以上是关于从服务器端关闭连接时的 QAbstractSocket 奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章