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

时间:2017-08-23 04:48:40

标签: c++ qt sockets

我正在实现一个继承自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.

我得到的上述输出就像&#34;永远&#34;。无论在服务器端关闭连接多长时间后,我的SimpleTCPSocket类都不会发出disconnected()信号。 这是我的socket类的头文件:

#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;
}

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

此致

1 个答案:

答案 0 :(得分:0)

我在Qt论坛上发布了这个问题然后我得到了answer。 raven-worx写道:“你的实施没有任何问题。不幸的是,这是相当正常的行为。”所以,问题就“解决了”。