From: AAH Date: Wed Dec 22 17:32:50 2004 +0100 Update QAbstractSocket's synchronous API to mimic the behavior of the event loop better. Remove the closing() signal. Allow data to be read from the socket after disconnection. Add disconnectFromHost(). It's now possible to connect, write, then wait for finished(), then read all data that was received on the connection. Done with: P [git-p4: depot-paths = "//depot/qt/main/": change = 163487] diff --git a/examples/network/fortuneclient/client.cpp b/examples/network/fortuneclient/client.cpp --- a/examples/network/fortuneclient/client.cpp +++ b/examples/network/fortuneclient/client.cpp @@ -36,6 +36,7 @@ Client::Client(QWidget *parent) connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readFortune())); connect(tcpSocket, SIGNAL(error(int)), this, SLOT(displayError(int))); + connect(tcpSocket, SIGNAL(connected()), this, SLOT(booga())); QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addStretch(1); @@ -56,13 +57,17 @@ Client::Client(QWidget *parent) void Client::requestNewFortune() { + qDebug("Client::requestNewFortune()"); + getFortuneButton->setEnabled(false); blockSize = 0; + tcpSocket->abort(); tcpSocket->connectToHost(hostLineEdit->text(), portLineEdit->text().toInt()); } void Client::readFortune() { + qDebug("Client::readFortune()"); QDataStream in(tcpSocket); in.setVersion(QDataStream::Qt_4_0); @@ -78,13 +83,18 @@ void Client::readFortune() QString nextFortune; in >> nextFortune; + if (nextFortune == currentFortune) { - requestNewFortune(); + QTimer::singleShot(0, this, SLOT(requestNewFortune())); + qDebug("Client::readFortune() restarting requestNewFortune"); return; } + qDebug("Client::readFortune() got new fortune"); + currentFortune = nextFortune; statusLabel->setText(currentFortune); + getFortuneButton->setEnabled(true); } void Client::displayError(int socketError) @@ -107,6 +117,8 @@ void Client::displayError(int socketError) tr("The following error occurred: %1.") .arg(tcpSocket->errorString())); } + + getFortuneButton->setEnabled(true); } void Client::enableGetFortuneButton() diff --git a/examples/network/fortuneclient/client.h b/examples/network/fortuneclient/client.h --- a/examples/network/fortuneclient/client.h +++ b/examples/network/fortuneclient/client.h @@ -20,6 +20,7 @@ private slots: void readFortune(); void displayError(int socketError); void enableGetFortuneButton(); + void booga() { qDebug("connected"); } private: QLabel *hostLabel; diff --git a/examples/network/fortuneserver/server.cpp b/examples/network/fortuneserver/server.cpp --- a/examples/network/fortuneserver/server.cpp +++ b/examples/network/fortuneserver/server.cpp @@ -60,7 +60,7 @@ void Server::sendFortune() out << (Q_UINT16)(block.size() - sizeof(Q_UINT16)); QTcpSocket *clientConnection = tcpServer->nextPendingConnection(); - connect(clientConnection, SIGNAL(closed()), + connect(clientConnection, SIGNAL(disconnected()), clientConnection, SLOT(deleteLater())); clientConnection->write(block); diff --git a/src/network/qabstractsocket.cpp b/src/network/qabstractsocket.cpp --- a/src/network/qabstractsocket.cpp +++ b/src/network/qabstractsocket.cpp @@ -343,9 +343,6 @@ void QAbstractSocketPrivate::resetSocketLayer() qDebug("QAbstractSocketPrivate::resetSocketLayer()"); #endif - readBuffer.clear(); - writeBuffer.clear(); - if (readSocketNotifier) readSocketNotifier->setEnabled(false); delete readSocketNotifier; @@ -430,7 +427,7 @@ bool QAbstractSocketPrivate::initSocketLayer(Qt::SocketType type, when new data is available for reading, or when the socket has been closed. Handles recursive calls. */ -void QAbstractSocketPrivate::canReadNotification(int) +bool QAbstractSocketPrivate::canReadNotification(int) { #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::canReadNotification()"); @@ -444,7 +441,7 @@ void QAbstractSocketPrivate::canReadNotification(int) #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::canReadNotification() recursive call detected."); #endif - return; + return false; } readSocketNotifierCalled = true; @@ -456,7 +453,7 @@ void QAbstractSocketPrivate::canReadNotification(int) qDebug("QAbstractSocketPrivate::canReadNotification() buffer is full"); #endif readSocketNotifierCalled = false; - return; + return false; } // If reading from the socket fails after getting a read @@ -464,22 +461,22 @@ void QAbstractSocketPrivate::canReadNotification(int) int oldBufferSize = d->readBuffer.size(); if (!d->readFromSocket()) { #if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocketPrivate::canReadNotification() closing socket"); + qDebug("QAbstractSocketPrivate::canReadNotification() disconnecting socket"); #endif - q->close(); + q->disconnectFromHost(); readSocketNotifierCalled = false; - return; + return false; } // If the buffer size is unchanged after reading from the // socket, close the socket. if (oldBufferSize == d->readBuffer.size()) { #if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocketPrivate::canReadNotification() unchanged buffer: closing socket"); + qDebug("QAbstractSocketPrivate::canReadNotification() unchanged buffer: disconnecting socket"); #endif - q->close(); + q->disconnectFromHost(); readSocketNotifierCalled = false; - return; + return false; } } @@ -492,7 +489,7 @@ void QAbstractSocketPrivate::canReadNotification(int) #endif emit q->readyRead(); if (!that) - return; + return true; // If we were closed as a result of the readyRead() signal, // return. @@ -501,7 +498,7 @@ void QAbstractSocketPrivate::canReadNotification(int) qDebug("QAbstractSocketPrivate::canReadNotification() socket is closing - returning"); #endif readSocketNotifierCalled = false; - return; + return true; } // If there is still space in the buffer, reenable the read socket @@ -515,6 +512,7 @@ void QAbstractSocketPrivate::canReadNotification(int) } readSocketNotifierCalled = false; + return true; } /*! \internal @@ -522,7 +520,7 @@ void QAbstractSocketPrivate::canReadNotification(int) Slot connected to the write socket notifier. It's called during a delayed connect or when the socket is ready for writing. */ -void QAbstractSocketPrivate::canWriteNotification(int) +bool QAbstractSocketPrivate::canWriteNotification(int) { // Prevent the write socket notifier from being called more times writeSocketNotifier->setEnabled(false); @@ -534,12 +532,15 @@ void QAbstractSocketPrivate::canWriteNotification(int) qDebug("QAbstractSocketPrivate::canWriteNotification() testing connection"); #endif testConnection(); - } else { + return false; + } + #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::canWriteNotification() flushing"); #endif - flush(); - } + int tmp = writeBuffer.size(); + flush(); + return (writeBuffer.size() < tmp); } /*! \internal @@ -711,6 +712,8 @@ void QAbstractSocketPrivate::connectToNextAddress() emit q->stateChanged(state); if (d->readSocketNotifier) readSocketNotifier->setEnabled(true); + if (d->writeSocketNotifier && !d->writeBuffer.isEmpty()) + writeSocketNotifier->setEnabled(true); emit q->connected(); return; } @@ -754,6 +757,8 @@ void QAbstractSocketPrivate::testConnection() if (d->readSocketNotifier) readSocketNotifier->setEnabled(true); + if (d->writeSocketNotifier && !d->writeBuffer.isEmpty()) + writeSocketNotifier->setEnabled(true); emit q->connected(); #if defined(QABSTRACTSOCKET_DEBUG) @@ -936,12 +941,17 @@ void QAbstractSocket::connectToHost(const QString &hostName, Q_UINT16 port, (int) openMode); #endif - if (d->state == Qt::ConnectingState || d->state == Qt::ConnectedState) - abort(); + if (d->state == Qt::ConnectedState || d->state == Qt::ConnectingState) { + qWarning("QAbstractSocket::connectToHost() called when already connecting/connected"); + return; + } d->hostName = hostName; d->port = port; d->state = Qt::HostLookupState; + d->readBuffer.clear(); + d->writeBuffer.clear(); + d->closeCalled = false; setOpenMode(openMode); emit stateChanged(d->state); @@ -1138,70 +1148,6 @@ bool QAbstractSocket::setSocketDescriptor(int socketDescriptor, Qt::SocketState return true; } -/*! \reimp -*/ -bool QAbstractSocket::waitForReadyRead(int msecs) -{ -#if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::waitForReadyRead(%i)", msecs); -#endif - - if (socketState() == Qt::UnconnectedState) { - qWarning("QAbstractSocket::waitForReadyRead() is not allowed in UnconnectedState"); - return false; - } - - bool timedOut = false; - if (!d->socketLayer.waitForRead(msecs, &timedOut) || (d->isBuffered && !d->readFromSocket())) { - d->socketError = d->socketLayer.socketError(); - setErrorString(d->socketLayer.errorString()); -#if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)", - msecs, d->socketError, errorString().latin1()); -#endif - emit error(d->socketError); - close(); - return false; - } - -#if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::waitForReadyRead(%i) emitting readyRead()", msecs); -#endif - if (!d->readSocketNotifierCalled) - emit readyRead(); - return true; -} - -/*! \reimp - */ -bool QAbstractSocket::waitForBytesWritten(int msecs) -{ -#if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::waitForBytesWritten(%i)", msecs); -#endif - - if (socketState() == Qt::UnconnectedState) { - qWarning("QAbstractSocket::waitForReadyRead() is not allowed in UnconnectedState"); - return false; - } - - bool timedOut = false; - if (!d->socketLayer.waitForWrite(msecs, &timedOut)) { - d->socketError = d->socketLayer.socketError(); - setErrorString(d->socketLayer.errorString()); -#if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::waitForBytesWritten(%i) failed (%s)", - msecs, errorString().latin1()); -#endif - emit error(d->socketError); - close(); - return false; - } - - d->flush(); - return true; -} - /*! Waits until the socket is connected, up to \a msecs milliseconds. If the connection has been established, this @@ -1274,40 +1220,169 @@ bool QAbstractSocket::waitForConnected(int msecs) return socketState() == Qt::ConnectedState; } +/*! \reimp +*/ +bool QAbstractSocket::waitForReadyRead(int msecs) +{ +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::waitForReadyRead(%i)", msecs); +#endif + + // require calling connectToHost() before waitForReadyRead() + if (socketState() == Qt::UnconnectedState) { + qWarning("QAbstractSocket::waitForReadyRead() is not allowed in UnconnectedState"); + return false; + } + + QTime stopWatch; + stopWatch.start(); + + // handle a socket in connecting state + if (socketState() == Qt::HostLookupState || socketState() == Qt::ConnectingState) { + if (!waitForConnected(msecs)) + return false; + } + + forever { + bool readyToRead = false; + if (!d->socketLayer.waitForReadOrWrite(&readyToRead, true, !d->writeBuffer.isEmpty(), + msecs - stopWatch.elapsed())) { + d->socketError = d->socketLayer.socketError(); + setErrorString(d->socketLayer.errorString()); +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)", + msecs, d->socketError, errorString().latin1()); +#endif + emit error(d->socketError); + close(); + return false; + } + + if (readyToRead) { + if (d->canReadNotification(0)) + return true; + } else { + d->canWriteNotification(0); + } + + if (socketState() != Qt::ConnectedState) + return false; + } +} + +/*! \reimp + */ +bool QAbstractSocket::waitForBytesWritten(int msecs) +{ +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::waitForBytesWritten(%i)", msecs); +#endif + + // require calling connectToHost() before waitForBytesWritten() + if (socketState() == Qt::UnconnectedState) { + qWarning("QAbstractSocket::waitForBytesWritten() is not allowed in UnconnectedState"); + return false; + } + + if (d->writeBuffer.isEmpty()) + return false; + + QTime stopWatch; + stopWatch.start(); + + // handle a socket in connecting state + if (socketState() == Qt::HostLookupState || socketState() == Qt::ConnectingState) { + if (!waitForConnected(msecs)) + return false; + } + + forever { + bool readyToRead = false; + if (!d->socketLayer.waitForReadOrWrite(&readyToRead, true, !d->writeBuffer.isEmpty(), + msecs - stopWatch.elapsed())) { + d->socketError = d->socketLayer.socketError(); + setErrorString(d->socketLayer.errorString()); +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::waitForBytesWritten(%i) failed (%i, %s)", + msecs, d->socketError, errorString().latin1()); +#endif + emit error(d->socketError); + close(); + return false; + } + + if (readyToRead) { + d->canReadNotification(0); + } else { + if (d->canWriteNotification(0)) + return true; + } + + if (socketState() != Qt::ConnectedState) + return false; + } +} + /*! - Waits until the socket is closed, up to \a msecs milliseconds. If - the connection has been closed, this function returns true; - otherwise it returns false. In the case where it returns false, - you can call socketError() to determine the cause of the error. + Waits until the socket has disconnected, up to \a msecs + milliseconds. If the connection has been disconnected, this + function returns true; otherwise it returns false. In the case + where it returns false, you can call socketError() to determine + the cause of the error. The following example waits up to one second for a connection to be closed: \code - socket->close(); - if (socket->waitForClosed(1000)) - qDebug("Closed!"); + socket->disconnect(); + if (socket->waitForDisconnected(1000)) + qDebug("Disconnected!"); \endcode - \sa close(), closed() + \sa disconnect(), close() */ -bool QAbstractSocket::waitForClosed(int msecs) +bool QAbstractSocket::waitForDisconnected(int msecs) { - if (d->state == Qt::UnconnectedState) - return true; - if (d->state != Qt::ClosingState) { - qWarning("QAbstractSocket::waitForClosed() called when not in Qt::ClosingState"); + // require calling connectToHost() before waitForDisconnected() + if (socketState() == Qt::UnconnectedState) { + qWarning("QAbstractSocket::waitForDisconnected() is not allowed in UnconnectedState"); return false; } - int tmp = d->blockingTimeout; - d->blockingTimeout = msecs; - bool flushed = flush(); - d->blockingTimeout = tmp; - if (!flushed) - return false; - close(); - return true; + QTime stopWatch; + stopWatch.start(); + + // handle a socket in connecting state + if (socketState() == Qt::HostLookupState || socketState() == Qt::ConnectingState) { + if (!waitForConnected(msecs)) + return false; + } + + forever { + bool readyToRead = false; + if (!d->socketLayer.waitForReadOrWrite(&readyToRead, socketState() == Qt::ConnectedState, + !d->writeBuffer.isEmpty(), + msecs - stopWatch.elapsed())) { + d->socketError = d->socketLayer.socketError(); + setErrorString(d->socketLayer.errorString()); +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)", + msecs, d->socketError, errorString().latin1()); +#endif + emit error(d->socketError); + close(); + return false; + } + + if (readyToRead) { + d->canReadNotification(0); + } else { + d->canWriteNotification(0); + } + + if (socketState() == Qt::UnconnectedState) + return true; + } } /*! @@ -1329,31 +1404,6 @@ void QAbstractSocket::abort() close(); } -/*! - Writes any pending outgoing data to the socket. Returns true if - all data was successfully written; otherwise returns false. If it - returns false, you can call socketError() to determine the cause - of the error. - - This function blocks until all data has been written or until the - default timeout has expired. - - \sa setDefaultTimeout() -*/ -bool QAbstractSocket::flush() -{ -#if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::flush() writeBuffer.size() = %d", - d->writeBuffer.size()); -#endif - while (d->writeBuffer.size() > 0) { - if (!d->socketLayer.waitForWrite(QT_TRANSFER_TIMEOUT) || !d->flush()) - return false; - } - - return d->writeBuffer.isEmpty(); -} - /*! \reimp */ bool QAbstractSocket::isSequential() const @@ -1365,19 +1415,14 @@ bool QAbstractSocket::isSequential() const */ Q_LONGLONG QAbstractSocket::readData(char *data, Q_LONGLONG maxSize) { - if (!isValid()) { - qWarning("QAbstractSocket::readData: Invalid socket"); -#if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::readData(%p, %lli) == -1 (%s)", - data, maxSize, errorString().latin1()); -#endif - return -1; - } - if (!d->isBuffered) { Q_LONGLONG readBytes = d->socketLayer.read(data, maxSize); - if (d->readSocketNotifier) + if (readBytes < 0) { + d->socketError = d->socketLayer.socketError(); + q->setErrorString(d->socketLayer.errorString()); + } else if (d->readSocketNotifier) { d->readSocketNotifier->setEnabled(true); + } #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld", data, qt_prettyDebug(data, 32, readBytes).data(), maxSize, @@ -1386,52 +1431,13 @@ Q_LONGLONG QAbstractSocket::readData(char *data, Q_LONGLONG maxSize) return readBytes; } - // If readFromSocket() read data, copy it to its destination. - if (d->readBuffer.size() > 0) { - // getch optimization - if (maxSize == 1) { - *data = d->readBuffer.getChar(); -#if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::readData(%p '%c (0x%.2x)', 1) == 1", - data, isprint(*data) ? *data : '?', *data); -#endif - return 1; - } - - if (d->readSocketNotifier) - d->readSocketNotifier->setEnabled(true); - Q_LONGLONG bytesToRead = qMin(Q_LONGLONG(d->readBuffer.size()), maxSize); - Q_LONGLONG readSoFar = 0; - while (readSoFar < bytesToRead) { - char *ptr = d->readBuffer.readPointer(); - int bytesToReadFromThisBlock = qMin(int(bytesToRead - readSoFar), - d->readBuffer.nextDataBlockSize()); - memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock); - readSoFar += bytesToReadFromThisBlock; - d->readBuffer.free(bytesToReadFromThisBlock); - } - -#if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld", - data, qt_prettyDebug(data, qMin(32, readSoFar), readSoFar).data(), - maxSize, readSoFar); -#endif - return readSoFar; - } - - // Wait for more data to read. - if (!waitForReadyRead(d->blockingTimeout)) { -#if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::readData(%p, %lli) == -1 (%s)", - data, maxSize, errorString().latin1()); -#endif - return -1; - } + if (d->readBuffer.isEmpty()) + return Q_LONGLONG(0); if (d->readSocketNotifier) d->readSocketNotifier->setEnabled(true); - // getch optimization + // If readFromSocket() read data, copy it to its destination. if (maxSize == 1) { *data = d->readBuffer.getChar(); #if defined (QABSTRACTSOCKET_DEBUG) @@ -1454,7 +1460,7 @@ Q_LONGLONG QAbstractSocket::readData(char *data, Q_LONGLONG maxSize) #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld", - data, qt_prettyDebug(data, qMin(readSoFar, 32), readSoFar).data(), + data, qt_prettyDebug(data, qMin(32, readSoFar), readSoFar).data(), maxSize, readSoFar); #endif return readSoFar; @@ -1464,25 +1470,22 @@ Q_LONGLONG QAbstractSocket::readData(char *data, Q_LONGLONG maxSize) */ Q_LONGLONG QAbstractSocket::writeData(const char *data, Q_LONGLONG size) { - if (!isValid()) { - qWarning("QAbstractSocket::writeData: Invalid socket"); -#if defined (QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::writeData(%p \"%s\", %lli) == -1 (%s)", data, - qt_prettyDebug(data, qMin((int)size, 32), size).data(), - size, errorString().latin1()); -#endif - return -1; - } - if (!d->isBuffered) { Q_LONGLONG written = d->socketLayer.write(data, size); - emit bytesWritten(written); + if (written < 0) { + d->socketError = d->socketLayer.socketError(); + q->setErrorString(d->socketLayer.errorString()); + } else if (!d->writeBuffer.isEmpty() && d->writeSocketNotifier) { + d->writeSocketNotifier->setEnabled(true); + } #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocket::writeData(%p \"%s\", %lli) == %lli", data, qt_prettyDebug(data, qMin((int)size, 32), size).data(), size, written); #endif + if (written >= 0) + emit bytesWritten(written); return written; } @@ -1494,7 +1497,7 @@ Q_LONGLONG QAbstractSocket::writeData(const char *data, Q_LONGLONG size) Q_LONGLONG written = size; - if (d->writeSocketNotifier) + if (!d->writeBuffer.isEmpty() && d->writeSocketNotifier) d->writeSocketNotifier->setEnabled(true); #if defined (QABSTRACTSOCKET_DEBUG) @@ -1509,19 +1512,40 @@ Q_LONGLONG QAbstractSocket::writeData(const char *data, Q_LONGLONG size) Attempts to close the socket. If there is pending data waiting to be written, QAbstractSocket will enter Qt::ClosingState and wait until all data has been written. Eventually, it will enter - Qt::ClosedState and emit the closed() signal. + Qt::UnconnectedState and emit the disconnected() signal. \sa abort() */ void QAbstractSocket::close() { +#if defined(QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::close()"); +#endif + if (d->state != Qt::UnconnectedState) { + d->closeCalled = true; + disconnectFromHost(); + } +} + +/* +*/ +void QAbstractSocket::disconnectFromHost() +{ +#if defined(QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::disconnectFromHost()"); +#endif + if (d->state == Qt::UnconnectedState) { #if defined(QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::close() on an unconnected socket"); + qDebug("QAbstractSocket::disconnectFromHost() was called on an unconnected socket"); #endif return; } +#ifdef QT_COMPAT + emit connectionClosed(); // compat signal +#endif + // Disable and delete read notification if (d->readSocketNotifier) { d->readSocketNotifier->setEnabled(false); @@ -1533,16 +1557,12 @@ void QAbstractSocket::close() if (d->state != Qt::ClosingState) { d->state = Qt::ClosingState; #if defined(QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::close() emits stateChanged(), then closing()"); + qDebug("QAbstractSocket::disconnectFromHost() emits stateChanged()(Qt::ClosingState)"); #endif emit stateChanged(d->state); -#ifdef QT_COMPAT - emit connectionClosed(); // compat signal -#endif - emit closing(); } else { #if defined(QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::close() return from delayed close"); + qDebug("QAbstractSocket::disconnectFromHost() return from delayed close"); #endif } @@ -1552,12 +1572,12 @@ void QAbstractSocket::close() d->writeSocketNotifier->setEnabled(true); #if defined(QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::close() delaying close"); + qDebug("QAbstractSocket::disconnectFromHost() delaying disconnect"); #endif return; } else { #if defined(QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::close() closing immediately"); + qDebug("QAbstractSocket::disconnectFromHost() disconnecting immediately"); #endif } @@ -1570,16 +1590,25 @@ void QAbstractSocket::close() d->resetSocketLayer(); d->state = Qt::UnconnectedState; - setOpenMode(NotOpen); - -#if defined(QABSTRACTSOCKET_DEBUG) - qDebug("QAbstractSocket::close() emits stateChanged(), then closed()"); -#endif emit stateChanged(d->state); + #ifdef QT_COMPAT emit delayedCloseFinished(); // compat signal #endif - emit closed(); + emit disconnected(); + +#if defined(QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::disconnectFromHost() disconnected!"); +#endif + + if (d->closeCalled) { +#if defined(QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::disconnectFromHost() closed!"); +#endif + d->readBuffer.clear(); + d->writeBuffer.clear(); + QIODevice::close(); + } } /*! diff --git a/src/network/qabstractsocket.h b/src/network/qabstractsocket.h --- a/src/network/qabstractsocket.h +++ b/src/network/qabstractsocket.h @@ -65,6 +65,7 @@ public: void connectToHost(const QString &hostName, Q_UINT16 port, OpenMode mode = ReadWrite); void connectToHost(const QHostAddress &address, Q_UINT16 port, OpenMode mode = ReadWrite); + void disconnectFromHost(); bool isValid() const; @@ -94,20 +95,18 @@ public: // from QIODevice void close(); - bool flush(); bool isSequential() const; // for synchronous access bool waitForConnected(int msecs = 30000); bool waitForReadyRead(int msecs = 30000); bool waitForBytesWritten(int msecs = 30000); - bool waitForClosed(int msecs = 30000); + bool waitForDisconnected(int msecs = 30000); signals: void hostFound(); void connected(); - void closing(); - void closed(); + void disconnected(); void stateChanged(int); void error(int); @@ -129,8 +128,8 @@ private: Q_PRIVATE_SLOT(d, void startConnecting(const QDnsHostInfo &)) Q_PRIVATE_SLOT(d, void abortConnectionAttempt()) Q_PRIVATE_SLOT(d, void testConnection()) - Q_PRIVATE_SLOT(d, void canReadNotification(int)) - Q_PRIVATE_SLOT(d, void canWriteNotification(int)) + Q_PRIVATE_SLOT(d, bool canReadNotification(int)) + Q_PRIVATE_SLOT(d, bool canWriteNotification(int)) #ifdef QT_COMPAT public: @@ -160,11 +159,11 @@ public: return 0; } signals: - QT_MOC_COMPAT void connectionClosed(); // same as closing() - QT_MOC_COMPAT void delayedCloseFinished(); // same as closed() -#endif + QT_MOC_COMPAT void connectionClosed(); // same as disconnected() + QT_MOC_COMPAT void delayedCloseFinished(); // same as disconnected() +#endif }; #endif diff --git a/src/network/qabstractsocket_p.h b/src/network/qabstractsocket_p.h --- a/src/network/qabstractsocket_p.h +++ b/src/network/qabstractsocket_p.h @@ -35,11 +35,12 @@ public: void connectToNextAddress(); void startConnecting(const QDnsHostInfo &hostInfo); void testConnection(); - void canReadNotification(int); - void canWriteNotification(int); + bool canReadNotification(int); + bool canWriteNotification(int); void abortConnectionAttempt(); bool readSocketNotifierCalled; + bool closeCalled; QString hostName; Q_UINT16 port; diff --git a/src/network/qftp.cpp b/src/network/qftp.cpp --- a/src/network/qftp.cpp +++ b/src/network/qftp.cpp @@ -86,7 +86,6 @@ private slots: void socketConnectionClosed(); void socketBytesWritten(Q_LONGLONG); void setupSocket(); - void readAllFromSocket(); private: void clearData(); @@ -187,6 +186,8 @@ private: bool waitForDtpToConnect; bool waitForDtpToClose; + + QByteArray bytesFromSocket; }; /********************************************************************** @@ -296,9 +297,8 @@ void QFtpDTP::connectToHost(const QString & host, Q_UINT16 port) connect(socket, SIGNAL(connected()), SLOT(socketConnected())); connect(socket, SIGNAL(readyRead()), SLOT(socketReadyRead())); connect(socket, SIGNAL(error(int)), SLOT(socketError(int))); - connect(socket, SIGNAL(closed()), SLOT(socketConnectionClosed())); + connect(socket, SIGNAL(disconnected()), SLOT(socketConnectionClosed())); connect(socket, SIGNAL(bytesWritten(Q_LONGLONG)), SLOT(socketBytesWritten(Q_LONGLONG))); - connect(socket, SIGNAL(closing()), SLOT(readAllFromSocket())); socket->connectToHost(host, port); } @@ -637,6 +637,8 @@ void QFtpDTP::socketConnectionClosed() if (!is_ba && data.dev) { clearData(); } + + bytesFromSocket = socket->readAll(); #if defined(QFTPDTP_DEBUG) qDebug("QFtpDTP::connectState(CsClosed)"); #endif @@ -661,18 +663,12 @@ void QFtpDTP::setupSocket() connect(socket, SIGNAL(connected()), SLOT(socketConnected())); connect(socket, SIGNAL(readyRead()), SLOT(socketReadyRead())); connect(socket, SIGNAL(error(int)), SLOT(socketError(int))); - connect(socket, SIGNAL(closed()), SLOT(socketConnectionClosed())); + connect(socket, SIGNAL(disconnected()), SLOT(socketConnectionClosed())); connect(socket, SIGNAL(bytesWritten(Q_LONGLONG)), SLOT(socketBytesWritten(Q_LONGLONG))); listener.close(); } -void QFtpDTP::readAllFromSocket() -{ - if (socket->bytesAvailable() > 0) - bytesFromSocket = socket->readAll(); -} - void QFtpDTP::clearData() { is_ba = false; @@ -700,7 +696,7 @@ QFtpPI::QFtpPI(QObject *parent) : SLOT(hostFound())); connect(&commandSocket, SIGNAL(connected()), SLOT(connected())); - connect(&commandSocket, SIGNAL(closed()), + connect(&commandSocket, SIGNAL(disconnected()), SLOT(connectionClosed())); connect(&commandSocket, SIGNAL(readyRead()), SLOT(readyRead())); diff --git a/src/network/qhttp.cpp b/src/network/qhttp.cpp --- a/src/network/qhttp.cpp +++ b/src/network/qhttp.cpp @@ -2118,6 +2118,8 @@ void QHttpPrivate::sendRequest() // existing one? if (socket->peerName() != hostName || socket->peerPort() != port || socket->socketState() != Qt::ConnectedState) { + socket->abort(); + setState(QHttp::Connecting); if (proxyHost.isEmpty()) socket->connectToHost(hostName, port); @@ -2559,7 +2561,7 @@ void QHttpPrivate::setSock(QTcpSocket *sock) // connect all signals QObject::connect(socket, SIGNAL(connected()), q, SLOT(slotConnected())); - QObject::connect(socket, SIGNAL(closed()), q, SLOT(slotClosed())); + QObject::connect(socket, SIGNAL(disconnected()), q, SLOT(slotClosed())); QObject::connect(socket, SIGNAL(readyRead()), q, SLOT(slotReadyRead())); QObject::connect(socket, SIGNAL(error(int)), q, SLOT(slotError(int))); QObject::connect(socket, SIGNAL(bytesWritten(Q_LONGLONG)), diff --git a/src/network/qsocketlayer.cpp b/src/network/qsocketlayer.cpp --- a/src/network/qsocketlayer.cpp +++ b/src/network/qsocketlayer.cpp @@ -721,6 +721,27 @@ bool QSocketLayer::waitForWrite(int msecs, bool *timedOut) const return ret > 0; } +bool QSocketLayer::waitForReadOrWrite(bool *readyToRead, bool checkRead, bool checkWrite, + int msecs, bool *timedOut) const +{ + Q_CHECK_VALID_SOCKETLAYER(QSocketLayer::waitForWrite(), false); + Q_CHECK_NOT_STATE(QSocketLayer::waitForReadOrWrite(), + Qt::UnconnectedState, false); + + bool selectedForRead = false; + int ret = d->nativeSelect(msecs, checkRead, checkWrite, &selectedForRead); + if (ret == 0) { + if (timedOut) + *timedOut = true; + d->setError(Qt::SocketTimeoutError, "Network operation timed out"); + return false; + } + + *readyToRead = selectedForRead; + + return ret > 0; +} + /*! Returns the size of the operating system's socket receive buffer. Depending on the operating system, this size may be diff --git a/src/network/qsocketlayer_p.h b/src/network/qsocketlayer_p.h --- a/src/network/qsocketlayer_p.h +++ b/src/network/qsocketlayer_p.h @@ -66,6 +66,7 @@ public: bool waitForRead(int msecs = 30000, bool *timedOut = 0) const; bool waitForWrite(int msecs = 30000, bool *timedOut = 0) const; + bool waitForReadOrWrite(bool *readyToRead, bool checkRead, bool checkWrite, int msecs = 30000, bool *timedOut = 0) const; Qt::SocketError socketError() const; QString errorString() const; @@ -141,6 +142,7 @@ public: Q_LONGLONG nativeRead(char *data, Q_LONGLONG maxLength); Q_LONGLONG nativeWrite(const char *data, Q_LONGLONG length); int nativeSelect(int timeout, bool selectForRead) const; + int nativeSelect(int timeout, bool checkRead, bool checkWrite, bool *selectForRead) const; void nativeClose(); diff --git a/src/network/qsocketlayer_unix.cpp b/src/network/qsocketlayer_unix.cpp --- a/src/network/qsocketlayer_unix.cpp +++ b/src/network/qsocketlayer_unix.cpp @@ -788,3 +788,30 @@ int QSocketLayerPrivate::nativeSelect(int timeout, bool selectForRead) const else return select(socketDescriptor + 1, 0, &fds, 0, timeout < 0 ? 0 : &tv); } + +int QSocketLayerPrivate::nativeSelect(int timeout, bool checkRead, bool checkWrite, bool *selectForRead) const +{ + fd_set fdread; + FD_ZERO(&fdread); + if (checkRead) + FD_SET(socketDescriptor, &fdread); + + fd_set fdwrite; + FD_ZERO(&fdwrite); + if (checkWrite) + FD_SET(socketDescriptor, &fdwrite); + + struct timeval tv; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + int ret = select(socketDescriptor + 1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv); + if (ret <= 0) + return ret; + + if (FD_ISSET(socketDescriptor, &fdread)) + *selectForRead = true; + if (FD_ISSET(socketDescriptor, &fdwrite)) + *selectForRead = false; + return ret; +} diff --git a/src/network/qsocketlayer_win.cpp b/src/network/qsocketlayer_win.cpp --- a/src/network/qsocketlayer_win.cpp +++ b/src/network/qsocketlayer_win.cpp @@ -657,7 +657,7 @@ bool QSocketLayerPrivate::nativeHasPendingDatagrams() const storagePtrIPv4->sin_port = 0; QT_SOCKLEN_T storageSize = sizeof(storage); - + bool result = false; // Peek 0 bytes into the next message. The size of the message may @@ -911,6 +911,36 @@ int QSocketLayerPrivate::nativeSelect(int timeout, bool selectForRead) const return select(0, 0, &fds, 0, timeout < 0 ? 0 : &tv); } +int QSocketLayerPrivate::nativeSelect(int timeout, bool checkRead, bool checkWrite, bool *selectForRead) const +{ + fd_set fdread; + memset(&fdread, 0, sizeof(fd_set)); + if (checkRead) { + fdread.fd_count = 1; + fdread.fd_array[0] = socketDescriptor; + } + + fd_set fdwrite; + memset(&fdwrite, 0, sizeof(fd_set)); + if (checkWrite) { + fdwrite.fd_count = 1; + fdwrite.fd_array[0] = socketDescriptor; + } + + struct timeval tv; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + int ret = select(socketDescriptor + 1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv); + if (ret <= 0) + return ret; + + if (FD_ISSET(socketDescriptor, &fdread)) + *selectForRead = true; + if (FD_ISSET(socketDescriptor, &fdwrite)) + *selectForRead = false; + return ret; +} void QSocketLayerPrivate::nativeClose() {