diff options
Diffstat (limited to 'src/network/socket')
25 files changed, 1007 insertions, 631 deletions
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 5c4dcdf..b85a9d2 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -48,7 +48,7 @@ common to all socket types. \reentrant - \ingroup io + \ingroup network \inmodule QtNetwork QAbstractSocket is the base class for QTcpSocket and QUdpSocket @@ -160,7 +160,7 @@ issue to be aware of, though: You must make sure that enough data is available before attempting to read it using operator>>(). - \sa QFtp, QHttp, QTcpServer + \sa QFtp, QNetworkAccessManager, QTcpServer */ /*! @@ -332,6 +332,22 @@ \sa QAbstractSocket::state() */ +/*! + \enum QAbstractSocket::SocketOption + \since 4.6 + + This enum represents the options that can be set on a socket. + If desired, they can be set after having received the connected() signal from + the socket or after having received a new socket from a QTcpServer. + + \value LowDelayOption Try to optimize the socket for low latency. For a QTcpSocket + this would set the TCP_NODELAY option and disable Nagle's algorithm. Set this to 1 + to enable. + \value KeepAliveOption Set this to 1 to enable the SO_KEEPALIVE socket option + + \sa QAbstractSocket::setSocketOption(), QAbstractSocket::socketOption() +*/ + #include "qabstractsocket.h" #include "qabstractsocket_p.h" @@ -852,7 +868,7 @@ void QAbstractSocketPrivate::_q_startConnecting(const QHostInfo &hostInfo) if (i != 0) s += ", "; s += addresses.at(i).toString(); } - s += "}"; + s += '}'; qDebug("QAbstractSocketPrivate::_q_startConnecting(hostInfo == %s)", s.toLatin1().constData()); #endif @@ -1053,6 +1069,7 @@ void QAbstractSocketPrivate::_q_abortConnectionAttempt() #endif if (socketEngine) socketEngine->setWriteNotificationEnabled(false); + connectTimer->stop(); if (addresses.isEmpty()) { @@ -1265,8 +1282,9 @@ void QAbstractSocket::connectToHostImplementation(const QString &hostName, quint (int) openMode); #endif - if (d->state == ConnectedState || d->state == ConnectingState || d->state == ClosingState) { - qWarning("QAbstractSocket::connectToHost() called when already connecting/connected to \"%s\"", qPrintable(hostName)); + if (d->state == ConnectedState || d->state == ConnectingState + || d->state == ClosingState || d->state == HostLookupState) { + qWarning("QAbstractSocket::connectToHost() called when already looking up or connecting/connected to \"%s\"", qPrintable(hostName)); return; } @@ -1548,6 +1566,56 @@ bool QAbstractSocket::setSocketDescriptor(int socketDescriptor, SocketState sock return true; } +/*! + Sets the option \a option to the value described by \a value. + + \sa socketOption() + \since 4.6 +*/ +void QAbstractSocket::setSocketOption(QAbstractSocket::SocketOption option, QVariant value) +{ + if (!d_func()->socketEngine) + return; + + switch (option) { + case LowDelayOption: + d_func()->socketEngine->setOption(QAbstractSocketEngine::LowDelayOption, value.toInt()); + break; + + case KeepAliveOption: + d_func()->socketEngine->setOption(QAbstractSocketEngine::KeepAliveOption, value.toInt()); + break; + } +} + +/*! + Returns the value of the \a option option. + + \sa setSocketOption() + \since 4.6 +*/ +QVariant QAbstractSocket::socketOption(QAbstractSocket::SocketOption option) +{ + if (!d_func()->socketEngine) + return QVariant(); + + int ret = -1; + switch (option) { + case LowDelayOption: + ret = d_func()->socketEngine->option(QAbstractSocketEngine::LowDelayOption); + break; + + case KeepAliveOption: + ret = d_func()->socketEngine->option(QAbstractSocketEngine::KeepAliveOption); + break; + } + if (ret == -1) + return QVariant(); + else + return QVariant(ret); +} + + /* Returns the difference between msecs and elapsed. If msecs is -1, however, -1 is returned. @@ -1658,13 +1726,13 @@ bool QAbstractSocket::waitForConnected(int msecs) } /*! - This function blocks until data is available for reading and the + This function blocks until new data is available for reading and the \l{QIODevice::}{readyRead()} signal has been emitted. The function will timeout after \a msecs milliseconds; the default timeout is 30000 milliseconds. The function returns true if the readyRead() signal is emitted and - there is data available for reading; otherwise it returns false + there is new data available for reading; otherwise it returns false (if an error occurred or the operation timed out). \sa waitForBytesWritten() @@ -2596,7 +2664,7 @@ Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, QAbstractSocket::SocketError er debug << "QAbstractSocket::ProxyProtocolError"; break; default: - debug << "QAbstractSocket::SocketError(" << int(error) << ")"; + debug << "QAbstractSocket::SocketError(" << int(error) << ')'; break; } return debug; @@ -2627,7 +2695,7 @@ Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, QAbstractSocket::SocketState st debug << "QAbstractSocket::ClosingState"; break; default: - debug << "QAbstractSocket::SocketState(" << int(state) << ")"; + debug << "QAbstractSocket::SocketState(" << int(state) << ')'; break; } return debug; diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h index c0a71cd..6cc86ef 100644 --- a/src/network/socket/qabstractsocket.h +++ b/src/network/socket/qabstractsocket.h @@ -116,6 +116,10 @@ public: Connection = ConnectedState #endif }; + enum SocketOption { + LowDelayOption, // TCP_NODELAY + KeepAliveOption // SO_KEEPALIVE + }; QAbstractSocket(SocketType socketType, QObject *parent); virtual ~QAbstractSocket(); @@ -149,6 +153,10 @@ public: bool setSocketDescriptor(int socketDescriptor, SocketState state = ConnectedState, OpenMode openMode = ReadWrite); + // ### Qt 5: Make virtual? + void setSocketOption(QAbstractSocket::SocketOption o, QVariant v); + QVariant socketOption(QAbstractSocket::SocketOption o); + SocketType socketType() const; SocketState state() const; SocketError error() const; diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index 630fd0a..0d7f84f 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -92,7 +92,9 @@ public: SendBufferSocketOption, AddressReusable, BindExclusively, - ReceiveOutOfBandData + ReceiveOutOfBandData, + LowDelayOption, + KeepAliveOption }; virtual bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) = 0; diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index 334ff1d..bb0915a 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -276,13 +276,30 @@ qint64 QHttpSocketEngine::pendingDatagramSize() const } #endif // QT_NO_UDPSOCKET -int QHttpSocketEngine::option(SocketOption) const +int QHttpSocketEngine::option(SocketOption option) const { + Q_D(const QHttpSocketEngine); + if (d->socket) { + // convert the enum and call the real socket + if (option == QAbstractSocketEngine::LowDelayOption) + return d->socket->socketOption(QAbstractSocket::LowDelayOption).toInt(); + if (option == QAbstractSocketEngine::KeepAliveOption) + return d->socket->socketOption(QAbstractSocket::KeepAliveOption).toInt(); + } return -1; } -bool QHttpSocketEngine::setOption(SocketOption, int) +bool QHttpSocketEngine::setOption(SocketOption option, int value) { + Q_D(QHttpSocketEngine); + if (d->socket) { + // convert the enum and call the real socket + if (option == QAbstractSocketEngine::LowDelayOption) + d->socket->setSocketOption(QAbstractSocket::LowDelayOption, value); + if (option == QAbstractSocketEngine::KeepAliveOption) + d->socket->setSocketOption(QAbstractSocket::KeepAliveOption, value); + return true; + } return false; } diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp index 1cdcb70..20291d1 100644 --- a/src/network/socket/qlocalserver.cpp +++ b/src/network/socket/qlocalserver.cpp @@ -78,8 +78,6 @@ QT_BEGIN_NAMESPACE to use it without one. In that case, you must use waitForNewConnection(), which blocks until either a connection is available or a timeout expires. - Note that this feature is not supported on Windows 9x. - \sa QLocalSocket, QTcpServer */ @@ -276,9 +274,17 @@ QLocalSocket *QLocalServer::nextPendingConnection() if (d->pendingConnections.isEmpty()) return 0; QLocalSocket *nextSocket = d->pendingConnections.dequeue(); +#ifdef Q_OS_SYMBIAN + if(!d->socketNotifier) + return nextSocket; +#endif +#ifndef QT_LOCALSOCKET_TCP + if (d->pendingConnections.size() <= d->maxPendingConnections) #ifndef Q_OS_WIN - d->socketNotifier->setEnabled(d->pendingConnections.size() - <= d->maxPendingConnections); + d->socketNotifier->setEnabled(true); +#else + d->connectionEventNotifier->setEnabled(true); +#endif #endif return nextSocket; } diff --git a/src/network/socket/qlocalserver.h b/src/network/socket/qlocalserver.h index cf91002..acc3eb5 100644 --- a/src/network/socket/qlocalserver.h +++ b/src/network/socket/qlocalserver.h @@ -86,15 +86,7 @@ protected: private: Q_DISABLE_COPY(QLocalServer) -#if defined(QT_LOCALSOCKET_TCP) Q_PRIVATE_SLOT(d_func(), void _q_onNewConnection()) -#elif defined(Q_OS_WIN) - Q_PRIVATE_SLOT(d_func(), void _q_openSocket(HANDLE handle)) - Q_PRIVATE_SLOT(d_func(), void _q_stoppedListening()) - Q_PRIVATE_SLOT(d_func(), void _q_setError(QAbstractSocket::SocketError error, const QString &errorString)) -#else - Q_PRIVATE_SLOT(d_func(), void _q_socketActivated()) -#endif }; #endif // QT_NO_LOCALSERVER diff --git a/src/network/socket/qlocalserver_p.h b/src/network/socket/qlocalserver_p.h index d431c19..09f40c2 100644 --- a/src/network/socket/qlocalserver_p.h +++ b/src/network/socket/qlocalserver_p.h @@ -63,7 +63,7 @@ # include <qtcpserver.h> #elif defined(Q_OS_WIN) # include <qt_windows.h> -# include <qthread.h> +# include <private/qwineventnotifier_p.h> #else # include <private/qnativesocketengine_p.h> # include <qsocketnotifier.h> @@ -71,52 +71,13 @@ QT_BEGIN_NAMESPACE -#if defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP) - -/*! - \internal - QLocalServerThread exists because Windows does not have a - way to provide notifications when there is a new connections to - the server. - */ -class QLocalServerThread : public QThread -{ - Q_OBJECT - -Q_SIGNALS: - void connected(HANDLE newSocket); - void error(QAbstractSocket::SocketError error, const QString &errorString); - -public: - QLocalServerThread(QObject *parent = 0); - ~QLocalServerThread(); - void closeServer(); - -public: - QString setName(const QString &name); - void run(); - void stop(); - bool makeHandle(); - - HANDLE gotConnectionEvent; - QQueue<HANDLE> pendingHandles; - int maxPendingConnections; -private: - HANDLE stopEvent; - QString fullServerName; -}; - -#endif - class QLocalServerPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QLocalServer) public: QLocalServerPrivate() : -#if defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP) - inWaitingFunction(false), -#elif !defined(QT_LOCALSOCKET_TCP) +#if !defined(QT_LOCALSOCKET_TCP) && !defined(Q_OS_WIN) listenSocket(-1), socketNotifier(0), #endif maxPendingConnections(30), error(QAbstractSocket::UnknownSocketError) @@ -128,22 +89,26 @@ public: static bool removeServer(const QString &name); void closeServer(); void waitForNewConnection(int msec, bool *timedOut); + void _q_onNewConnection(); #if defined(QT_LOCALSOCKET_TCP) - void _q_onNewConnection(); QTcpServer tcpServer; QMap<quintptr, QTcpSocket*> socketMap; #elif defined(Q_OS_WIN) - void _q_openSocket(HANDLE socket); - void _q_stoppedListening(); - void _q_setError(QAbstractSocket::SocketError error, const QString &errorString); + struct Listener { + HANDLE handle; + OVERLAPPED overlapped; + }; + + void setError(const QString &function); + bool addListener(); - QLocalServerThread waitForConnection; - bool inWaitingFunction; + QList<Listener> listeners; + HANDLE eventHandle; + QWinEventNotifier *connectionEventNotifier; #else void setError(const QString &function); - void _q_socketActivated(); int listenSocket; QSocketNotifier *socketNotifier; diff --git a/src/network/socket/qlocalserver_unix.cpp b/src/network/socket/qlocalserver_unix.cpp index f3baf9b..01452ea 100644 --- a/src/network/socket/qlocalserver_unix.cpp +++ b/src/network/socket/qlocalserver_unix.cpp @@ -43,6 +43,7 @@ #include "qlocalserver_p.h" #include "qlocalsocket.h" #include "qlocalsocket_p.h" +#include "qnet_unix_p.h" #ifndef QT_NO_LOCALSERVER @@ -53,6 +54,10 @@ #include <qdir.h> #include <qdatetime.h> +#ifdef Q_OS_VXWORKS +# include <selectLib.h> +#endif + QT_BEGIN_NAMESPACE void QLocalServerPrivate::init() @@ -88,7 +93,7 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName) serverName = requestedServerName; // create the unix socket - listenSocket = qSocket(PF_UNIX, SOCK_STREAM, 0); + listenSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0); if (-1 == listenSocket) { setError(QLatin1String("QLocalServer::listen")); closeServer(); @@ -106,8 +111,26 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName) ::memcpy(addr.sun_path, fullServerName.toLatin1().data(), fullServerName.toLatin1().size() + 1); +#ifdef Q_OS_SYMBIAN + // In SYMBIAN OS it can currently happen that accept is called twice, + // once from waitForNewConnection and once via QSocketNotfier activity + // + // As an workaround, we set the socket to non blocking so possible + // subsequent call to accept will not block in any case + // + // This change can be removed once more generic fix to select thread + // syncronization problem is implemented. + int flags = fcntl(listenSocket, F_GETFL, 0); + if (-1 == flags + || -1 == (fcntl(listenSocket, F_SETFL, flags | O_NONBLOCK))) { + setError(QLatin1String("QLocalServer::listen")); + closeServer(); + return false; + } +#endif + // bind - if(-1 == qBind(listenSocket, (sockaddr *)&addr, sizeof(sockaddr_un))) { + if(-1 == QT_SOCKET_BIND(listenSocket, (sockaddr *)&addr, sizeof(sockaddr_un))) { setError(QLatin1String("QLocalServer::listen")); // if address is in use already, just close the socket, but do not delete the file if(errno == EADDRINUSE) @@ -120,7 +143,7 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName) } // listen for connections - if (-1 == qListen(listenSocket, 50)) { + if (-1 == qt_safe_listen(listenSocket, 50)) { setError(QLatin1String("QLocalServer::listen")); closeServer(); listenSocket = -1; @@ -132,7 +155,7 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName) socketNotifier = new QSocketNotifier(listenSocket, QSocketNotifier::Read, q); q->connect(socketNotifier, SIGNAL(activated(int)), - q, SLOT(_q_socketActivated())); + q, SLOT(_q_onNewConnection())); socketNotifier->setEnabled(maxPendingConnections > 0); return true; } @@ -164,7 +187,7 @@ void QLocalServerPrivate::closeServer() We have received a notification that we can read on the listen socket. Accept the new socket. */ -void QLocalServerPrivate::_q_socketActivated() +void QLocalServerPrivate::_q_onNewConnection() { Q_Q(QLocalServer); if (-1 == listenSocket) @@ -172,7 +195,7 @@ void QLocalServerPrivate::_q_socketActivated() ::sockaddr_un addr; QT_SOCKLEN_T length = sizeof(sockaddr_un); - int connectedSocket = qAccept(listenSocket, (sockaddr *)&addr, &length); + int connectedSocket = qt_safe_accept(listenSocket, (sockaddr *)&addr, &length); if(-1 == connectedSocket) { setError(QLatin1String("QLocalSocket::activated")); closeServer(); @@ -209,7 +232,7 @@ void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut) break; } if (result > 0) - _q_socketActivated(); + _q_onNewConnection(); } if (timedOut) *timedOut = (result == 0); diff --git a/src/network/socket/qlocalserver_win.cpp b/src/network/socket/qlocalserver_win.cpp index 16369f0..93cbe92 100644 --- a/src/network/socket/qlocalserver_win.cpp +++ b/src/network/socket/qlocalserver_win.cpp @@ -44,81 +44,26 @@ #include "qlocalsocket.h" #include <qdebug.h> -#include <qdatetime.h> -#include <qcoreapplication.h> -#include <QMetaType> // The buffer size need to be 0 otherwise data could be // lost if the socket that has written data closes the connection // before it is read. Pipewriter is used for write buffering. #define BUFSIZE 0 -QT_BEGIN_NAMESPACE +// ###: This should be a property. Should replace the insane 50 on unix as well. +#define SYSTEM_MAX_PENDING_SOCKETS 8 -QLocalServerThread::QLocalServerThread(QObject *parent) : QThread(parent), - maxPendingConnections(1) -{ - stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - gotConnectionEvent = CreateEvent(NULL, TRUE, FALSE, NULL); -} - -QLocalServerThread::~QLocalServerThread() -{ - stop(); - closeServer(); - CloseHandle(stopEvent); - CloseHandle(gotConnectionEvent); -} - -void QLocalServerThread::stop() -{ - if (isRunning()) { - SetEvent(stopEvent); - wait(); - ResetEvent(stopEvent); - } -} - -void QLocalServerThread::closeServer() -{ - while (!pendingHandles.isEmpty()) - CloseHandle(pendingHandles.dequeue()); -} - -QString QLocalServerThread::setName(const QString &name) -{ - QString pipePath = QLatin1String("\\\\.\\pipe\\"); - if (name.startsWith(pipePath)) - fullServerName = name; - else - fullServerName = pipePath + name; - for (int i = pendingHandles.count(); i < maxPendingConnections; ++i) - if (!makeHandle()) - break; - return fullServerName; -} +QT_BEGIN_NAMESPACE -bool QLocalServerThread::makeHandle() +bool QLocalServerPrivate::addListener() { - if (pendingHandles.count() >= maxPendingConnections) - return false; + // The object must not change its address once the + // contained OVERLAPPED struct is passed to Windows. + listeners << Listener(); + Listener &listener = listeners.last(); - HANDLE handle = INVALID_HANDLE_VALUE; - QT_WA({ - handle = CreateNamedPipeW( - (TCHAR*)fullServerName.utf16(), // pipe name - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access - PIPE_TYPE_MESSAGE | // message type pipe - PIPE_READMODE_MESSAGE | // message-read mode - PIPE_WAIT, // blocking mode - PIPE_UNLIMITED_INSTANCES, // max. instances - BUFSIZE, // output buffer size - BUFSIZE, // input buffer size - 3000, // client time-out - NULL); - }, { - handle = CreateNamedPipeA( - fullServerName.toLocal8Bit().constData(), // pipe name + listener.handle = CreateNamedPipe( + (const wchar_t *)fullServerName.utf16(), // pipe name PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access PIPE_TYPE_MESSAGE | // message type pipe PIPE_READMODE_MESSAGE | // message-read mode @@ -128,69 +73,44 @@ bool QLocalServerThread::makeHandle() BUFSIZE, // input buffer size 3000, // client time-out NULL); - }); - if (INVALID_HANDLE_VALUE == handle) { + if (listener.handle == INVALID_HANDLE_VALUE) { + setError(QLatin1String("QLocalServerPrivate::addListener")); + listeners.removeLast(); return false; } - pendingHandles.enqueue(handle); + + memset(&listener.overlapped, 0, sizeof(listener.overlapped)); + listener.overlapped.hEvent = eventHandle; + if (!ConnectNamedPipe(listener.handle, &listener.overlapped)) { + switch (GetLastError()) { + case ERROR_IO_PENDING: + break; + case ERROR_PIPE_CONNECTED: + SetEvent(eventHandle); + break; + default: + CloseHandle(listener.handle); + setError(QLatin1String("QLocalServerPrivate::addListener")); + listeners.removeLast(); + return false; + } + } else { + Q_ASSERT_X(false, "QLocalServerPrivate::addListener", "The impossible happened"); + SetEvent(eventHandle); + } return true; } -void QLocalServerThread::run() +void QLocalServerPrivate::setError(const QString &function) { - OVERLAPPED op; - HANDLE handleArray[2]; - memset(&op, 0, sizeof(op)); - handleArray[0] = op.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - handleArray[1] = stopEvent; - HANDLE handle = INVALID_HANDLE_VALUE; - - forever { - if (INVALID_HANDLE_VALUE == handle) { - makeHandle(); - if (!pendingHandles.isEmpty()) - handle = pendingHandles.dequeue(); - } - if (INVALID_HANDLE_VALUE == handle) { - int windowsError = GetLastError(); - QString function = QLatin1String("QLocalServer::run"); - QString errorString = QLocalServer::tr("%1: Unknown error %2").arg(function).arg(windowsError); - emit error(QAbstractSocket::UnknownSocketError, errorString); - CloseHandle(handleArray[0]); - SetEvent(gotConnectionEvent); - return; - } - - BOOL isConnected = ConnectNamedPipe(handle, &op) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); - if (!isConnected) { - switch (WaitForMultipleObjects(2, handleArray, FALSE, INFINITE)) - { - case WAIT_OBJECT_0 + 1: - CloseHandle(handle); - CloseHandle(handleArray[0]); - return; - } - } - emit connected(handle); - handle = INVALID_HANDLE_VALUE; - ResetEvent(handleArray[0]); - SetEvent(gotConnectionEvent); - } + int windowsError = GetLastError(); + errorString = QString::fromLatin1("%1: %2").arg(function).arg(qt_error_string(windowsError)); + error = QAbstractSocket::UnknownSocketError; } void QLocalServerPrivate::init() { - Q_Q(QLocalServer); - qRegisterMetaType<HANDLE>("HANDLE"); - q->connect(&waitForConnection, SIGNAL(connected(HANDLE)), - q, SLOT(_q_openSocket(HANDLE)), Qt::QueuedConnection); - q->connect(&waitForConnection, SIGNAL(finished()), - q, SLOT(_q_stoppedListening()), Qt::QueuedConnection); - q->connect(&waitForConnection, SIGNAL(terminated()), - q, SLOT(_q_stoppedListening()), Qt::QueuedConnection); - q->connect(&waitForConnection, SIGNAL(error(QAbstractSocket::SocketError, const QString &)), - q, SLOT(_q_setError(QAbstractSocket::SocketError, const QString &))); } bool QLocalServerPrivate::removeServer(const QString &name) @@ -201,35 +121,71 @@ bool QLocalServerPrivate::removeServer(const QString &name) bool QLocalServerPrivate::listen(const QString &name) { - fullServerName = waitForConnection.setName(name); - serverName = name; - waitForConnection.start(); - return true; -} + Q_Q(QLocalServer); -void QLocalServerPrivate::_q_setError(QAbstractSocket::SocketError e, const QString &eString) -{ - error = e; - errorString = eString; -} + QString pipePath = QLatin1String("\\\\.\\pipe\\"); + if (name.startsWith(pipePath)) + fullServerName = name; + else + fullServerName = pipePath + name; -void QLocalServerPrivate::_q_stoppedListening() -{ - Q_Q(QLocalServer); - if (!inWaitingFunction) - q->close(); + // Use only one event for all listeners of one socket. + // The idea is that listener events are rare, so polling all listeners once in a while is + // cheap compared to waiting for N additional events in each iteration of the main loop. + eventHandle = CreateEvent(NULL, TRUE, FALSE, NULL); + connectionEventNotifier = new QWinEventNotifier(eventHandle , q); + q->connect(connectionEventNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_onNewConnection())); + + for (int i = 0; i < SYSTEM_MAX_PENDING_SOCKETS; ++i) + if (!addListener()) + return false; + return true; } -void QLocalServerPrivate::_q_openSocket(HANDLE handle) +void QLocalServerPrivate::_q_onNewConnection() { Q_Q(QLocalServer); - q->incomingConnection((int)handle); + DWORD dummy; + + // Reset first, otherwise we could reset an event which was asserted + // immediately after we checked the conn status. + ResetEvent(eventHandle); + + // Testing shows that there is indeed absolutely no guarantee which listener gets + // a client connection first, so there is no way around polling all of them. + for (int i = 0; i < listeners.size(); ) { + HANDLE handle = listeners[i].handle; + if (GetOverlappedResult(handle, &listeners[i].overlapped, &dummy, FALSE)) { + listeners.removeAt(i); + + addListener(); + + if (pendingConnections.size() > maxPendingConnections) + connectionEventNotifier->setEnabled(false); + + // Make this the last thing so connected slots can wreak the least havoc + q->incomingConnection((quintptr)handle); + } else { + if (GetLastError() != ERROR_IO_INCOMPLETE) { + setError(QLatin1String("QLocalServerPrivate::_q_onNewConnection")); + closeServer(); + return; + } + + ++i; + } + } } void QLocalServerPrivate::closeServer() { - waitForConnection.stop(); - waitForConnection.closeServer(); + connectionEventNotifier->setEnabled(false); // Otherwise, closed handle is checked before deleter runs + connectionEventNotifier->deleteLater(); + connectionEventNotifier = 0; + CloseHandle(eventHandle); + for (int i = 0; i < listeners.size(); ++i) + CloseHandle(listeners[i].handle); + listeners.clear(); } void QLocalServerPrivate::waitForNewConnection(int msecs, bool *timedOut) @@ -238,14 +194,12 @@ void QLocalServerPrivate::waitForNewConnection(int msecs, bool *timedOut) if (!pendingConnections.isEmpty() || !q->isListening()) return; - DWORD result = WaitForSingleObject(waitForConnection.gotConnectionEvent, - (msecs == -1) ? INFINITE : msecs); + DWORD result = WaitForSingleObject(eventHandle, (msecs == -1) ? INFINITE : msecs); if (result == WAIT_TIMEOUT) { if (timedOut) *timedOut = true; } else { - ResetEvent(waitForConnection.gotConnectionEvent); - QCoreApplication::instance()->processEvents(); + _q_onNewConnection(); } } diff --git a/src/network/socket/qlocalsocket.cpp b/src/network/socket/qlocalsocket.cpp index 32e70ee..54b2be3 100644 --- a/src/network/socket/qlocalsocket.cpp +++ b/src/network/socket/qlocalsocket.cpp @@ -63,7 +63,8 @@ QT_BEGIN_NAMESPACE waitForReadyRead(), waitForBytesWritten(), and waitForDisconnected() which blocks until the operation is complete or the timeout expires. - Note that this feature is not supported on Window 9x. + Note that this feature is not supported on versions of Windows earlier than + Windows XP. \sa QLocalServer */ @@ -102,7 +103,7 @@ QT_BEGIN_NAMESPACE opened in the mode specified by \a openMode, and enters the socket state specified by \a socketState. - Note: It is not possible to initialize two local sockets with the same + \note It is not possible to initialize two local sockets with the same native socket descriptor. \sa socketDescriptor(), state(), openMode() @@ -207,10 +208,10 @@ QT_BEGIN_NAMESPACE Returns true if the socket is valid and ready for use; otherwise returns false. - Note: The socket's state must be ConnectedState before reading + \note The socket's state must be ConnectedState before reading and writing can occur. - \sa state() + \sa state(), connectToServer() */ /*! @@ -243,9 +244,9 @@ QT_BEGIN_NAMESPACE */ /*! - \fn bool QLocalSocket::waitForConnected(int msec) + \fn bool QLocalSocket::waitForConnected(int msecs) - Waits until the socket is connected, up to \a msec milliseconds. If the + Waits until the socket is connected, up to \a msecs milliseconds. If the connection has been established, this function returns true; otherwise it returns false. In the case where it returns false, you can call error() to determine the cause of the error. @@ -255,7 +256,7 @@ QT_BEGIN_NAMESPACE \snippet doc/src/snippets/code/src_network_socket_qlocalsocket_unix.cpp 0 - If msecs is -1, this function will not time out. + If \a msecs is -1, this function will not time out. \sa connectToServer(), connected() */ @@ -274,7 +275,7 @@ QT_BEGIN_NAMESPACE \snippet doc/src/snippets/code/src_network_socket_qlocalsocket_unix.cpp 1 - If msecs is -1, this function will not time out. + If \a msecs is -1, this function will not time out. \sa disconnectFromServer(), close() */ @@ -309,9 +310,10 @@ QT_BEGIN_NAMESPACE parameter describes the type of error that occurred. QLocalSocket::LocalSocketError is not a registered metatype, so for queued - connections, you will have to register it with Q_DECLARE_METATYPE. + connections, you will have to register it with Q_DECLARE_METATYPE() and + qRegisterMetaType(). - \sa error(), errorString() + \sa error(), errorString(), {Creating Custom Qt Types} */ /*! @@ -321,9 +323,10 @@ QT_BEGIN_NAMESPACE The \a socketState parameter is the new state. QLocalSocket::SocketState is not a registered metatype, so for queued - connections, you will have to register it with Q_DECLARE_METATYPE. + connections, you will have to register it with Q_DECLARE_METATYPE() and + qRegisterMetaType(). - \sa state() + \sa state(), {Creating Custom Qt Types} */ /*! @@ -365,7 +368,7 @@ QString QLocalSocket::serverName() const /*! Returns the server path that the socket is connected to. - Note: This is platform specific + \note The return value of this function is platform specific. \sa connectToServer(), serverName() */ @@ -468,7 +471,7 @@ QDebug operator<<(QDebug debug, QLocalSocket::LocalSocketError error) debug << "QLocalSocket::UnknownSocketError"; break; default: - debug << "QLocalSocket::SocketError(" << int(error) << ")"; + debug << "QLocalSocket::SocketError(" << int(error) << ')'; break; } return debug; @@ -490,7 +493,7 @@ QDebug operator<<(QDebug debug, QLocalSocket::LocalSocketState state) debug << "QLocalSocket::ClosingState"; break; default: - debug << "QLocalSocket::SocketState(" << int(state) << ")"; + debug << "QLocalSocket::SocketState(" << int(state) << ')'; break; } return debug; diff --git a/src/network/socket/qlocalsocket.h b/src/network/socket/qlocalsocket.h index 35ae068..7c0fe78 100644 --- a/src/network/socket/qlocalsocket.h +++ b/src/network/socket/qlocalsocket.h @@ -134,6 +134,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_notified()) Q_PRIVATE_SLOT(d_func(), void _q_canWrite()) Q_PRIVATE_SLOT(d_func(), void _q_pipeClosed()) + Q_PRIVATE_SLOT(d_func(), void _q_emitReadyRead()) #else Q_PRIVATE_SLOT(d_func(), void _q_stateChanged(QAbstractSocket::SocketState)) Q_PRIVATE_SLOT(d_func(), void _q_error(QAbstractSocket::SocketError)) diff --git a/src/network/socket/qlocalsocket_p.h b/src/network/socket/qlocalsocket_p.h index 74ab779..a774cbf 100644 --- a/src/network/socket/qlocalsocket_p.h +++ b/src/network/socket/qlocalsocket_p.h @@ -65,6 +65,7 @@ #elif defined(Q_OS_WIN) # include "private/qwindowspipewriter_p.h" # include "private/qringbuffer_p.h" +# include <private/qwineventnotifier_p.h> #else # include "private/qnativesocketengine_p.h" # include <qtcpsocket.h> @@ -74,43 +75,6 @@ QT_BEGIN_NAMESPACE -#if !defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP) -static inline int qSocket(int af, int socketype, int proto) -{ - int ret; - while((ret = qt_socket_socket(af, socketype, proto)) == -1 && errno == EINTR){} - return ret; -} - -static inline int qBind(int fd, const sockaddr *sa, int len) -{ - int ret; - while((ret = QT_SOCKET_BIND(fd, (sockaddr*)sa, len)) == -1 && errno == EINTR){} - return ret; -} - -static inline int qConnect(int fd, const sockaddr *sa, int len) -{ - int ret; - while((ret = QT_SOCKET_CONNECT(fd, (sockaddr*)sa, len)) == -1 && errno == EINTR){} - return ret; -} - -static inline int qListen(int fd, int backlog) -{ - int ret; - while((ret = qt_socket_listen(fd, backlog)) == -1 && errno == EINTR){} - return ret; -} - -static inline int qAccept(int fd, struct sockaddr *addr, QT_SOCKLEN_T *addrlen) -{ - int ret; - while((ret = qt_socket_accept(fd, addr, addrlen)) == -1 && errno == EINTR){} - return ret; -} -#endif //#if !defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP) - #if !defined(Q_OS_WIN) || defined(QT_LOCALSOCKET_TCP) class QLocalUnixSocket : public QTcpSocket { @@ -172,18 +136,23 @@ public: void _q_notified(); void _q_canWrite(); void _q_pipeClosed(); - qint64 readData(char *data, qint64 maxSize); - qint64 bytesAvailable(); - bool readFromSocket(); + void _q_emitReadyRead(); + DWORD bytesAvailable(); + void startAsyncRead(); + void completeAsyncRead(); + void checkReadyRead(); HANDLE handle; OVERLAPPED overlapped; QWindowsPipeWriter *pipeWriter; qint64 readBufferMaxSize; QRingBuffer readBuffer; - QTimer dataNotifier; + int actualReadBufferSize; + QWinEventNotifier *dataReadNotifier; QLocalSocket::LocalSocketError error; - bool readyReadEmitted; + bool readSequenceStarted; + bool pendingReadyRead; bool pipeClosed; + static const qint64 initialReadBufferSize = 4096; #else QLocalUnixSocket unixSocket; QString generateErrorString(QLocalSocket::LocalSocketError, const QString &function) const; diff --git a/src/network/socket/qlocalsocket_unix.cpp b/src/network/socket/qlocalsocket_unix.cpp index 4b484dd..d3d172b 100644 --- a/src/network/socket/qlocalsocket_unix.cpp +++ b/src/network/socket/qlocalsocket_unix.cpp @@ -41,6 +41,7 @@ #include "qlocalsocket.h" #include "qlocalsocket_p.h" +#include "qnet_unix_p.h" #ifndef QT_NO_LOCALSOCKET @@ -55,6 +56,10 @@ #include <qdir.h> #include <qdebug.h> +#ifdef Q_OS_VXWORKS +# include <selectLib.h> +#endif + #define QT_CONNECT_TIMEOUT 30000 QT_BEGIN_NAMESPACE @@ -232,12 +237,12 @@ void QLocalSocket::connectToServer(const QString &name, OpenMode openMode) } // create the socket - if (-1 == (d->connectingSocket = qSocket(PF_UNIX, SOCK_STREAM, 0))) { + if (-1 == (d->connectingSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0))) { d->errorOccurred(UnsupportedSocketOperationError, QLatin1String("QLocalSocket::connectToServer")); return; } - +#ifndef Q_OS_SYMBIAN // set non blocking so we can try to connect and it wont wait int flags = fcntl(d->connectingSocket, F_GETFL, 0); if (-1 == flags @@ -246,6 +251,7 @@ void QLocalSocket::connectToServer(const QString &name, OpenMode openMode) QLatin1String("QLocalSocket::connectToServer")); return; } +#endif // _q_connectToSocket does the actual connecting d->connectingName = name; @@ -282,7 +288,7 @@ void QLocalSocketPrivate::_q_connectToSocket() } ::memcpy(name.sun_path, connectingPathName.toLatin1().data(), connectingPathName.toLatin1().size() + 1); - if (-1 == qConnect(connectingSocket, (struct sockaddr *)&name, sizeof(name))) { + if (-1 == qt_safe_connect(connectingSocket, (struct sockaddr *)&name, sizeof(name))) { QString function = QLatin1String("QLocalSocket::connectToServer"); switch (errno) { @@ -303,7 +309,7 @@ void QLocalSocketPrivate::_q_connectToSocket() case EAGAIN: // Try again later, all of the sockets listening are full if (!delayConnect) { - delayConnect = new QSocketNotifier(connectingSocket, QSocketNotifier::Write); + delayConnect = new QSocketNotifier(connectingSocket, QSocketNotifier::Write, q); q->connect(delayConnect, SIGNAL(activated(int)), q, SLOT(_q_connectToSocket())); } if (!connectTimer) { @@ -514,9 +520,9 @@ bool QLocalSocket::waitForConnected(int msec) if (state() != ConnectingState) return (state() == ConnectedState); - fd_set readfds; - FD_ZERO(&readfds); - FD_SET(d->connectingSocket, &readfds); + fd_set fds; + FD_ZERO(&fds); + FD_SET(d->connectingSocket, &fds); timeval timeout; timeout.tv_sec = msec / 1000; @@ -532,7 +538,14 @@ bool QLocalSocket::waitForConnected(int msec) timer.start(); while (state() == ConnectingState && (-1 == msec || timer.elapsed() < msec)) { - result = ::select(d->connectingSocket + 1, &readfds, 0, 0, &timeout); +#ifdef Q_OS_SYMBIAN + // On Symbian, ready-to-write is signaled when non-blocking socket + // connect is finised. Is ready-to-read really used on other + // UNIX paltforms when using non-blocking AF_UNIX socket? + result = ::select(d->connectingSocket + 1, 0, &fds, 0, &timeout); +#else + result = ::select(d->connectingSocket + 1, &fds, 0, 0, &timeout); +#endif if (-1 == result && errno != EINTR) { d->errorOccurred( QLocalSocket::UnknownSocketError, QLatin1String("QLocalSocket::waitForConnected")); diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp index 7caddf6..f70a0aa 100644 --- a/src/network/socket/qlocalsocket_win.cpp +++ b/src/network/socket/qlocalsocket_win.cpp @@ -48,13 +48,13 @@ QT_BEGIN_NAMESPACE -#define NOTIFYTIMEOUT 100 - void QLocalSocketPrivate::init() { Q_Q(QLocalSocket); - QObject::connect(&dataNotifier, SIGNAL(timeout()), q, SLOT(_q_notified())); + memset(&overlapped, 0, sizeof(overlapped)); overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + dataReadNotifier = new QWinEventNotifier(overlapped.hEvent, q); + q->connect(dataReadNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_notified())); } void QLocalSocketPrivate::setErrorString(const QString &function) @@ -101,8 +101,10 @@ QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(), handle(INVALID_HANDLE_VALUE), pipeWriter(0), readBufferMaxSize(0), + actualReadBufferSize(0), error(QLocalSocket::UnknownSocketError), - readyReadEmitted(false), + readSequenceStarted(false), + pendingReadyRead(false), pipeClosed(false), state(QLocalSocket::UnconnectedState) { @@ -137,25 +139,14 @@ void QLocalSocket::connectToServer(const QString &name, OpenMode openMode) forever { DWORD permissions = (openMode & QIODevice::ReadOnly) ? GENERIC_READ : 0; permissions |= (openMode & QIODevice::WriteOnly) ? GENERIC_WRITE : 0; - QT_WA({ - localSocket = CreateFileW( - (TCHAR*)d->fullServerName.utf16(), // pipe name - permissions, - 0, // no sharing - NULL, // default security attributes - OPEN_EXISTING, // opens existing pipe - FILE_FLAG_OVERLAPPED, - NULL); // no template file - }, { - localSocket = CreateFileA( - d->fullServerName.toLocal8Bit().constData(), // pipe name - permissions, - 0, // no sharing - NULL, // default security attributes - OPEN_EXISTING, // opens existing pipe - FILE_FLAG_OVERLAPPED, - NULL); // no template file - }); + localSocket = CreateFile((const wchar_t *)d->fullServerName.utf16(), // pipe name + permissions, + 0, // no sharing + NULL, // default security attributes + OPEN_EXISTING, // opens existing pipe + FILE_FLAG_OVERLAPPED, + NULL); // no template file + if (localSocket != INVALID_HANDLE_VALUE) break; DWORD error = GetLastError(); @@ -165,13 +156,8 @@ void QLocalSocket::connectToServer(const QString &name, OpenMode openMode) } // All pipe instances are busy, so wait until connected or up to 5 seconds. - QT_WA({ - if (!WaitNamedPipeW((TCHAR*)d->fullServerName.utf16(), 5000)) - break; - }, { - if (!WaitNamedPipeA(d->fullServerName.toLocal8Bit().constData(), 5000)) - break; - }); + if (!WaitNamedPipe((const wchar_t *)d->fullServerName.utf16(), 5000)) + break; } if (localSocket == INVALID_HANDLE_VALUE) { @@ -182,7 +168,7 @@ void QLocalSocket::connectToServer(const QString &name, OpenMode openMode) // we have a valid handle d->serverName = name; - if (setSocketDescriptor((quintptr)localSocket), openMode) { + if (setSocketDescriptor((quintptr)localSocket, ConnectedState, openMode)) { d->handle = localSocket; emit connected(); } @@ -192,82 +178,103 @@ void QLocalSocket::connectToServer(const QString &name, OpenMode openMode) qint64 QLocalSocket::readData(char *data, qint64 maxSize) { Q_D(QLocalSocket); - if (d->readBuffer.isEmpty()) { - if (!d->readFromSocket()) { - if (d->pipeClosed) - return -1; - return 0; - } - } - if (!d->dataNotifier.isActive() && d->threadData->eventDispatcher) - d->dataNotifier.start(NOTIFYTIMEOUT); - - if (d->readBuffer.isEmpty()) - return qint64(0); - - // If readFromSocket() read data, copy it to its destination. - if (maxSize == 1) { + qint64 readSoFar; + // If startAsyncRead() read data, copy it to its destination. + if (maxSize == 1 && d->actualReadBufferSize > 0) { *data = d->readBuffer.getChar(); - return 1; + d->actualReadBufferSize--; + readSoFar = 1; + } else { + qint64 bytesToRead = qMin(qint64(d->actualReadBufferSize), maxSize); + readSoFar = 0; + while (readSoFar < bytesToRead) { + const char *ptr = d->readBuffer.readPointer(); + int bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar, + qint64(d->readBuffer.nextDataBlockSize())); + memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock); + readSoFar += bytesToReadFromThisBlock; + d->readBuffer.free(bytesToReadFromThisBlock); + d->actualReadBufferSize -= bytesToReadFromThisBlock; + } } - qint64 bytesToRead = qMin(qint64(d->readBuffer.size()), maxSize); - qint64 readSoFar = 0; - while (readSoFar < bytesToRead) { - const 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 (!d->readSequenceStarted) + d->startAsyncRead(); + d->checkReadyRead(); + return readSoFar; } /*! \internal - read from the socket + Schedules or cancels a readyRead() emission depending on actual data availability */ -qint64 QLocalSocketPrivate::readData(char *data, qint64 maxSize) +void QLocalSocketPrivate::checkReadyRead() { - DWORD bytesRead = 0; - overlapped.Offset = 0; - overlapped.OffsetHigh = 0; - bool success = ReadFile(handle, data, maxSize, &bytesRead, &overlapped); - if (!success && GetLastError() == ERROR_IO_PENDING) - if (GetOverlappedResult(handle, &overlapped, &bytesRead, TRUE)) - success = true; - if (!success) { - setErrorString(QLatin1String("QLocalSocket::readData")); - return 0; + if (actualReadBufferSize > 0) { + if (!pendingReadyRead) { + Q_Q(QLocalSocket); + QTimer::singleShot(0, q, SLOT(_q_emitReadyRead())); + pendingReadyRead = true; + } + } else { + pendingReadyRead = false; } - return bytesRead; } /*! \internal Reads data from the socket into the readbuffer */ -bool QLocalSocketPrivate::readFromSocket() +void QLocalSocketPrivate::startAsyncRead() { - qint64 bytesToRead = bytesAvailable(); - if (bytesToRead == 0) - return false; + do { + DWORD bytesToRead = bytesAvailable(); + if (bytesToRead == 0) { + // There are no bytes in the pipe but we need to + // start the overlapped read with some buffer size. + bytesToRead = initialReadBufferSize; + } - if (readBufferMaxSize && bytesToRead - > (readBufferMaxSize - readBuffer.size())) - bytesToRead = readBufferMaxSize - readBuffer.size(); + if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) { + bytesToRead = readBufferMaxSize - readBuffer.size(); + if (bytesToRead == 0) { + // Buffer is full. User must read data from the buffer + // before we can read more from the pipe. + return; + } + } - char *ptr = readBuffer.reserve(bytesToRead); - qint64 readBytes = readData(ptr, bytesToRead); - if (readBytes == 0) { - readBuffer.chop(bytesToRead); - return false; + char *ptr = readBuffer.reserve(bytesToRead); + + readSequenceStarted = true; + if (ReadFile(handle, ptr, bytesToRead, NULL, &overlapped)) { + completeAsyncRead(); + } else if (GetLastError() != ERROR_IO_PENDING) { + setErrorString(QLatin1String("QLocalSocketPrivate::startAsyncRead")); + return; + } + } while (!readSequenceStarted); +} + +/*! + \internal + Sets the correct size of the read buffer after a read operation. + */ +void QLocalSocketPrivate::completeAsyncRead() +{ + ResetEvent(overlapped.hEvent); + readSequenceStarted = false; + + DWORD bytesRead; + if (!GetOverlappedResult(handle, &overlapped, &bytesRead, TRUE)) { + setErrorString(QLatin1String("QLocalSocketPrivate::completeAsyncRead")); + return; } - readyReadEmitted = false; - readBuffer.chop(int(bytesToRead - (readBytes < 0 ? qint64(0) : readBytes))); - return true; + + actualReadBufferSize += bytesRead; + readBuffer.truncate(actualReadBufferSize); } qint64 QLocalSocket::writeData(const char *data, qint64 maxSize) @@ -289,11 +296,9 @@ void QLocalSocket::abort() /*! The number of bytes available from the pipe */ -qint64 QLocalSocketPrivate::bytesAvailable() +DWORD QLocalSocketPrivate::bytesAvailable() { Q_Q(QLocalSocket); - if (q->state() != QLocalSocket::ConnectedState) - return 0; DWORD bytes; if (PeekNamedPipe(handle, NULL, 0, NULL, &bytes, NULL)) { return bytes; @@ -316,7 +321,7 @@ qint64 QLocalSocket::bytesAvailable() const { Q_D(const QLocalSocket); qint64 available = QIODevice::bytesAvailable(); - available += (qint64) d->readBuffer.size(); + available += (qint64) d->actualReadBufferSize; return available; } @@ -343,7 +348,6 @@ void QLocalSocket::close() QIODevice::close(); d->state = ClosingState; emit stateChanged(d->state); - d->readyReadEmitted = false; emit readChannelFinished(); d->serverName = QString(); d->fullServerName = QString(); @@ -352,10 +356,13 @@ void QLocalSocket::close() disconnectFromServer(); return; } + d->readSequenceStarted = false; + d->pendingReadyRead = false; d->pipeClosed = false; DisconnectNamedPipe(d->handle); CloseHandle(d->handle); d->handle = INVALID_HANDLE_VALUE; + ResetEvent(d->overlapped.hEvent); d->state = UnconnectedState; emit stateChanged(d->state); emit disconnected(); @@ -363,7 +370,6 @@ void QLocalSocket::close() delete d->pipeWriter; d->pipeWriter = 0; } - d->dataNotifier.stop(); } bool QLocalSocket::flush() @@ -397,12 +403,15 @@ bool QLocalSocket::setSocketDescriptor(quintptr socketDescriptor, { Q_D(QLocalSocket); d->readBuffer.clear(); + d->actualReadBufferSize = 0; QIODevice::open(openMode); d->handle = (int*)socketDescriptor; d->state = socketState; emit stateChanged(d->state); - if (d->threadData->eventDispatcher) - d->dataNotifier.start(NOTIFYTIMEOUT); + if (d->state == ConnectedState && openMode.testFlag(QIODevice::ReadOnly)) { + d->startAsyncRead(); + d->checkReadyRead(); + } return true; } @@ -416,20 +425,18 @@ void QLocalSocketPrivate::_q_canWrite() void QLocalSocketPrivate::_q_notified() { Q_Q(QLocalSocket); - if (0 != bytesAvailable()) { - if (readBufferMaxSize == 0 || readBuffer.size() < readBufferMaxSize) { - if (!readFromSocket()) { - return; - } - // wait until buffer is cleared before starting again - if (readBufferMaxSize && readBuffer.size() == readBufferMaxSize) { - dataNotifier.stop(); - } - } - if (!readyReadEmitted) { - readyReadEmitted = true; - q->emit readyRead(); - } + completeAsyncRead(); + startAsyncRead(); + pendingReadyRead = false; + emit q->readyRead(); +} + +void QLocalSocketPrivate::_q_emitReadyRead() +{ + if (pendingReadyRead) { + Q_Q(QLocalSocket); + pendingReadyRead = false; + emit q->readyRead(); } } @@ -462,11 +469,15 @@ bool QLocalSocket::waitForDisconnected(int msecs) Q_D(QLocalSocket); if (state() == UnconnectedState) return false; + if (!openMode().testFlag(QIODevice::ReadOnly)) { + qWarning("QLocalSocket::waitForDisconnected isn't supported for write only pipes."); + return false; + } QIncrementalSleepTimer timer(msecs); forever { - d->_q_notified(); - if (d->pipeClosed) - close(); + d->bytesAvailable(); // to check if PeekNamedPipe fails + if (d->pipeClosed) + close(); if (state() == UnconnectedState) return true; Sleep(timer.nextSleepTime()); @@ -486,22 +497,24 @@ bool QLocalSocket::isValid() const bool QLocalSocket::waitForReadyRead(int msecs) { Q_D(QLocalSocket); - QIncrementalSleepTimer timer(msecs); - forever { - d->_q_notified(); - if (bytesAvailable() > 0) { - if (!d->readyReadEmitted) { - d->readyReadEmitted = true; - emit readyRead(); - } - return true; - } - Sleep(timer.nextSleepTime()); - if (timer.hasTimedOut()) - break; + if (bytesAvailable() > 0) + return true; + + if (d->state != QLocalSocket::ConnectedState) + return false; + + Q_ASSERT(d->readSequenceStarted); + DWORD result = WaitForSingleObject(d->overlapped.hEvent, msecs == -1 ? INFINITE : msecs); + switch (result) { + case WAIT_OBJECT_0: + d->_q_notified(); + return true; + case WAIT_TIMEOUT: + return false; } + qWarning("QLocalSocket::waitForReadyRead WaitForSingleObject failed with error code %d.", int(GetLastError())); return false; } @@ -511,27 +524,11 @@ bool QLocalSocket::waitForBytesWritten(int msecs) if (!d->pipeWriter) return false; - QIncrementalSleepTimer timer(msecs); - forever { - if (d->pipeWriter->hadWritten()) - return true; - - if (d->pipeWriter->bytesToWrite() == 0) - return false; - - // Wait for the pipe writer to acknowledge that it has - // written. This will succeed if either the pipe writer has - // already written the data, or if it manages to write data - // within the given timeout. - if (d->pipeWriter->waitForWrite(0)) - return true; - - Sleep(timer.nextSleepTime()); - if (timer.hasTimedOut()) - break; - } - - return false; + // Wait for the pipe writer to acknowledge that it has + // written. This will succeed if either the pipe writer has + // already written the data, or if it manages to write data + // within the given timeout. + return d->pipeWriter->waitForWrite(msecs); } QT_END_NAMESPACE diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index 6cc71e6..3e56995 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -47,7 +47,7 @@ \brief The QNativeSocketEngine class provides low level access to a socket. \reentrant - \ingroup io + \ingroup network \inmodule QtNetwork QtSocketLayer provides basic socket functionality provided by the @@ -381,7 +381,9 @@ bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAb return false; } + // Make sure we receive out-of-band data + // On Symbian OS this works only with native IP stack, not with WinSock if (socketType == QAbstractSocket::TcpSocket && !setOption(ReceiveOutOfBandData, 1)) { qWarning("QNativeSocketEngine::initialize unable to inline out-of-band data"); @@ -911,7 +913,7 @@ bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWri d_func()->fetchConnectionParameters(); return true; } -#endif +#endif if (ret == 0) { if (timedOut) *timedOut = true; @@ -1086,7 +1088,10 @@ protected: bool QExceptionNotifier::event(QEvent *e) { if (e->type() == QEvent::SockAct) { - engine->exceptionNotification(); + if (engine->state() == QAbstractSocket::ConnectingState) + engine->connectionNotification(); + else + engine->exceptionNotification(); return true; } return QSocketNotifier::event(e); diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index 42d8145..084775f 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -52,45 +52,20 @@ // // We mean it. // - #include "QtNetwork/qhostaddress.h" #include "private/qabstractsocketengine_p.h" #ifndef Q_OS_WIN -# include "qplatformdefs.h" +# include "qplatformdefs.h" #else -# include <winsock2.h> -#endif - -QT_BEGIN_NAMESPACE - -#ifndef Q_OS_WIN -// Almost always the same. If not, specify in qplatformdefs.h. -#if !defined(QT_SOCKOPTLEN_T) -# define QT_SOCKOPTLEN_T QT_SOCKLEN_T +# include <winsock2.h> #endif -// Tru64 redefines accept -> _accept with _XOPEN_SOURCE_EXTENDED -static inline int qt_socket_accept(int s, struct sockaddr *addr, QT_SOCKLEN_T *addrlen) -{ return ::accept(s, addr, static_cast<QT_SOCKLEN_T *>(addrlen)); } -#if defined(accept) -# undef accept +#ifdef Q_OS_SYMBIAN +#include <private/qeventdispatcher_symbian_p.h> +#include <unistd.h> #endif -// UnixWare 7 redefines listen -> _listen -static inline int qt_socket_listen(int s, int backlog) -{ return ::listen(s, backlog); } -#if defined(listen) -# undef listen -#endif - -// UnixWare 7 redefines socket -> _socket -static inline int qt_socket_socket(int domain, int type, int protocol) -{ return ::socket(domain, type, protocol); } -#if defined(socket) -# undef socket -#endif - -#endif +QT_BEGIN_NAMESPACE // Use our own defines and structs which we know are correct # define QT_SS_MAXSIZE 128 diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index d30ed93..ec9f103 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -40,8 +40,8 @@ ****************************************************************************/ //#define QNATIVESOCKETENGINE_DEBUG - #include "qnativesocketengine_p.h" +#include "private/qnet_unix_p.h" #include "qiodevice.h" #include "qhostaddress.h" #include "qvarlengtharray.h" @@ -64,6 +64,13 @@ #include <ctype.h> #endif +#ifdef Q_OS_SYMBIAN // ### TODO: Are these headers right? +#include <sys/socket.h> +#include <netinet/in.h> +#else +#include <netinet/tcp.h> +#endif + QT_BEGIN_NAMESPACE #if defined QNATIVESOCKETENGINE_DEBUG @@ -100,6 +107,7 @@ static QByteArray qt_prettyDebug(const char *data, int len, int maxSize) static void qt_ignore_sigpipe() { +#ifndef Q_NO_POSIX_SIGNALS // Set to ignore SIGPIPE once only. static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0); if (atom.testAndSetRelaxed(0, 1)) { @@ -108,6 +116,10 @@ static void qt_ignore_sigpipe() noaction.sa_handler = SIG_IGN; ::sigaction(SIGPIPE, &noaction, 0); } +#else + // Posix signals are not supported by the underlying platform + // so we don't need to ignore sigpipe signal explicitly +#endif } /* @@ -126,7 +138,7 @@ static inline void qt_socket_getPortAndAddress(const qt_sockaddr *s, quint16 *po *addr = tmpAddress; #ifndef QT_NO_IPV6IFNAME char scopeid[IFNAMSIZ]; - if (::if_indextoname(s->a6.sin6_scope_id, scopeid) > 0) { + if (::if_indextoname(s->a6.sin6_scope_id, scopeid)) { addr->setScopeId(QLatin1String(scopeid)); } else #endif @@ -161,7 +173,11 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc int protocol = AF_INET; #endif int type = (socketType == QAbstractSocket::UdpSocket) ? SOCK_DGRAM : SOCK_STREAM; - int socket = qt_socket_socket(protocol, type, 0); +#ifdef Q_OS_SYMBIAN + int socket = ::socket(protocol, type, 0); +#else + int socket = qt_safe_socket(protocol, type, 0); +#endif if (socket <= 0) { switch (errno) { @@ -188,6 +204,7 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc // Ensure that the socket is closed on exec*(). ::fcntl(socket, F_SETFD, FD_CLOEXEC); + socketDescriptor = socket; return true; } @@ -202,6 +219,8 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co return -1; int n = -1; + int level = SOL_SOCKET; // default + switch (opt) { case QNativeSocketEngine::ReceiveBufferSocketOption: n = SO_RCVBUF; @@ -221,12 +240,20 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co case QNativeSocketEngine::ReceiveOutOfBandData: n = SO_OOBINLINE; break; + case QNativeSocketEngine::LowDelayOption: + level = IPPROTO_TCP; + n = TCP_NODELAY; + break; + case QNativeSocketEngine::KeepAliveOption: + n = SO_KEEPALIVE; + break; } int v = -1; QT_SOCKOPTLEN_T len = sizeof(v); - if (getsockopt(socketDescriptor, SOL_SOCKET, n, (char *) &v, &len) != -1) + if (::getsockopt(socketDescriptor, level, n, (char *) &v, &len) != -1) return v; + return -1; } @@ -241,6 +268,8 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt return false; int n = 0; + int level = SOL_SOCKET; // default + switch (opt) { case QNativeSocketEngine::ReceiveBufferSocketOption: n = SO_RCVBUF; @@ -253,6 +282,7 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt break; case QNativeSocketEngine::NonBlockingSocketOption: { // Make the socket nonblocking. +#if !defined(Q_OS_VXWORKS) int flags = ::fcntl(socketDescriptor, F_GETFL, 0); if (flags == -1) { #ifdef QNATIVESOCKETENGINE_DEBUG @@ -266,7 +296,19 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt #endif return false; } - +#else // Q_OS_VXWORKS + int onoff = 1; +#ifdef Q_OS_SYMBIAN + if (::ioctl(socketDescriptor, FIONBIO, &onoff) < 0) { +#else + if (qt_safe_ioctl(socketDescriptor, FIONBIO, &onoff) < 0) { +#endif +#ifdef QNATIVESOCKETENGINE_DEBUG + perror("QNativeSocketEnginePrivate::setOption(): ioctl(FIONBIO, 1) failed"); +#endif + return false; + } +#endif // Q_OS_VXWORKS return true; } case QNativeSocketEngine::AddressReusable: @@ -281,13 +323,24 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt case QNativeSocketEngine::ReceiveOutOfBandData: n = SO_OOBINLINE; break; + case QNativeSocketEngine::LowDelayOption: + level = IPPROTO_TCP; + n = TCP_NODELAY; + break; + case QNativeSocketEngine::KeepAliveOption: + n = SO_KEEPALIVE; + break; } - return ::setsockopt(socketDescriptor, SOL_SOCKET, n, (char *) &v, sizeof(v)) == 0; + return ::setsockopt(socketDescriptor, level, n, (char *) &v, sizeof(v)) == 0; } bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16 port) { +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug("QNativeSocketEnginePrivate::nativeConnect() : %d ", socketDescriptor); +#endif + struct sockaddr_in sockAddrIPv4; struct sockaddr *sockAddrPtr = 0; QT_SOCKLEN_T sockAddrSize = 0; @@ -325,8 +378,11 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16 } else { // unreachable } - - int connectResult = QT_SOCKET_CONNECT(socketDescriptor, sockAddrPtr, sockAddrSize); +#ifdef Q_OS_SYMBIAN + int connectResult = ::connect(socketDescriptor, sockAddrPtr, sockAddrSize); +#else + int connectResult = qt_safe_connect(socketDescriptor, sockAddrPtr, sockAddrSize); +#endif if (connectResult == -1) { switch (errno) { case EISCONN: @@ -430,6 +486,7 @@ bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &address, quint16 } int bindResult = QT_SOCKET_BIND(socketDescriptor, sockAddrPtr, sockAddrSize); + if (bindResult < 0) { switch(errno) { case EADDRINUSE: @@ -466,7 +523,11 @@ bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &address, quint16 bool QNativeSocketEnginePrivate::nativeListen(int backlog) { - if (qt_socket_listen(socketDescriptor, backlog) < 0) { +#ifdef Q_OS_SYMBIAN + if (::listen(socketDescriptor, backlog) < 0) { +#else + if (qt_safe_listen(socketDescriptor, backlog) < 0) { +#endif switch (errno) { case EADDRINUSE: setError(QAbstractSocket::AddressInUseError, @@ -493,12 +554,19 @@ bool QNativeSocketEnginePrivate::nativeListen(int backlog) int QNativeSocketEnginePrivate::nativeAccept() { - int acceptedDescriptor = qt_socket_accept(socketDescriptor, 0, 0); -#if defined (QNATIVESOCKETENGINE_DEBUG) - qDebug("QNativeSocketEnginePrivate::nativeAccept() == %i", acceptedDescriptor); +#ifdef Q_OS_SYMBIAN + int acceptedDescriptor = ::accept(socketDescriptor, 0, 0); +#else + int acceptedDescriptor = qt_safe_accept(socketDescriptor, 0, 0); #endif - // Ensure that the socket is closed on exec*() - ::fcntl(acceptedDescriptor, F_SETFD, FD_CLOEXEC); + //check if we have vaild descriptor at all + if(acceptedDescriptor > 0) { + // Ensure that the socket is closed on exec*() + ::fcntl(acceptedDescriptor, F_SETFD, FD_CLOEXEC); + } else { + qWarning("QNativeSocketEnginePrivate::nativeAccept() - acceptedDescriptor <= 0"); + } + return acceptedDescriptor; } @@ -507,7 +575,11 @@ qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const int nbytes = 0; // gives shorter than true amounts on Unix domain sockets. qint64 available = 0; - if (::ioctl(socketDescriptor, FIONREAD, (char *) &nbytes) >= 0) +#ifdef Q_OS_SYMBIAN + if (::ioctl(socketDescriptor, FIONREAD, (char *) &nbytes) >= 0) +#else + if (qt_safe_ioctl(socketDescriptor, FIONREAD, (char *) &nbytes) >= 0) +#endif available = (qint64) nbytes; #if defined (QNATIVESOCKETENGINE_DEBUG) @@ -542,16 +614,25 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const return result; } +#ifdef Q_OS_SYMBIAN +qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const +{ + size_t nbytes = 0; + ::ioctl(socketDescriptor, E32IONREAD, (char *) &nbytes); + return qint64(nbytes-28); +} +#else qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const { QVarLengthArray<char, 8192> udpMessagePeekBuffer(8192); ssize_t recvResult = -1; + for (;;) { // the data written to udpMessagePeekBuffer is discarded, so // this function is still reentrant although it might not look // so. recvResult = ::recv(socketDescriptor, udpMessagePeekBuffer.data(), - udpMessagePeekBuffer.size(), MSG_PEEK); + udpMessagePeekBuffer.size(), MSG_PEEK); if (recvResult == -1 && errno == EINTR) continue; @@ -567,7 +648,7 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const return qint64(recvResult); } - +#endif qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxSize, QHostAddress *address, quint16 *port) { @@ -609,33 +690,34 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l #if !defined(QT_NO_IPV6) struct sockaddr_in6 sockAddrIPv6; if (host.protocol() == QAbstractSocket::IPv6Protocol) { - memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6)); - sockAddrIPv6.sin6_family = AF_INET6; - sockAddrIPv6.sin6_port = htons(port); - - Q_IPV6ADDR tmp = host.toIPv6Address(); - memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &tmp, sizeof(tmp)); - sockAddrSize = sizeof(sockAddrIPv6); - sockAddrPtr = (struct sockaddr *)&sockAddrIPv6; + memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6)); + sockAddrIPv6.sin6_family = AF_INET6; + sockAddrIPv6.sin6_port = htons(port); + + Q_IPV6ADDR tmp = host.toIPv6Address(); + memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &tmp, sizeof(tmp)); + sockAddrSize = sizeof(sockAddrIPv6); + sockAddrPtr = (struct sockaddr *)&sockAddrIPv6; } else #endif if (host.protocol() == QAbstractSocket::IPv4Protocol) { - memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4)); - sockAddrIPv4.sin_family = AF_INET; - sockAddrIPv4.sin_port = htons(port); - sockAddrIPv4.sin_addr.s_addr = htonl(host.toIPv4Address()); - sockAddrSize = sizeof(sockAddrIPv4); - sockAddrPtr = (struct sockaddr *)&sockAddrIPv4; + memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4)); + sockAddrIPv4.sin_family = AF_INET; + sockAddrIPv4.sin_port = htons(port); + sockAddrIPv4.sin_addr.s_addr = htonl(host.toIPv4Address()); + sockAddrSize = sizeof(sockAddrIPv4); + sockAddrPtr = (struct sockaddr *)&sockAddrIPv4; } // ignore the SIGPIPE signal qt_ignore_sigpipe(); - - ssize_t sentBytes; - do { - sentBytes = ::sendto(socketDescriptor, data, len, - 0, sockAddrPtr, sockAddrSize); - } while (sentBytes == -1 && errno == EINTR); +#ifdef Q_OS_SYMBIAN + ssize_t sentBytes = ::sendto(socketDescriptor, data, len, + 0, sockAddrPtr, sockAddrSize); +#else + ssize_t sentBytes = qt_safe_sendto(socketDescriptor, data, len, + 0, sockAddrPtr, sockAddrSize); +#endif if (sentBytes < 0) { switch (errno) { @@ -732,7 +814,12 @@ void QNativeSocketEnginePrivate::nativeClose() #if defined (QNATIVESOCKETENGINE_DEBUG) qDebug("QNativeSocketEngine::nativeClose()"); #endif + +#ifdef Q_OS_SYMBIAN ::close(socketDescriptor); +#else + qt_safe_close(socketDescriptor); +#endif } qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len) @@ -746,7 +833,12 @@ qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len) // of an interrupting signal. ssize_t writtenBytes; do { - writtenBytes = ::write(socketDescriptor, data, len); +#ifdef Q_OS_SYMBIAN + writtenBytes = ::write(socketDescriptor, data, len); +#else + writtenBytes = qt_safe_write(socketDescriptor, data, len); +#endif + // writtenBytes = QT_WRITE(socketDescriptor, data, len); ### TODO S60: Should this line be removed or the one above it? } while (writtenBytes < 0 && errno == EINTR); if (writtenBytes < 0) { @@ -788,7 +880,11 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize) ssize_t r = 0; do { +#ifdef Q_OS_SYMBIAN r = ::read(socketDescriptor, data, maxSize); +#else + r = qt_safe_read(socketDescriptor, data, maxSize); +#endif } while (r == -1 && errno == EINTR); if (r < 0) { @@ -806,7 +902,13 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize) case EIO: setError(QAbstractSocket::NetworkError, ReadErrorString); break; +#ifdef Q_OS_SYMBIAN + case EPIPE: +#endif case ECONNRESET: +#if defined(Q_OS_VXWORKS) + case ESHUTDOWN: +#endif r = 0; break; default: @@ -833,38 +935,46 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; - QTime timer; - timer.start(); +#ifdef Q_OS_SYMBIAN + fd_set fdexception; + FD_ZERO(&fdexception); + FD_SET(socketDescriptor, &fdexception); +#endif int retval; - do { - if (selectForRead) - retval = select(socketDescriptor + 1, &fds, 0, 0, timeout < 0 ? 0 : &tv); - else - retval = select(socketDescriptor + 1, 0, &fds, 0, timeout < 0 ? 0 : &tv); + if (selectForRead) +#ifdef Q_OS_SYMBIAN + retval = ::select(socketDescriptor + 1, &fds, 0, &fdexception, timeout < 0 ? 0 : &tv); +#else + retval = qt_safe_select(socketDescriptor + 1, &fds, 0, 0, timeout < 0 ? 0 : &tv); +#endif + else +#ifdef Q_OS_SYMBIAN + retval = ::select(socketDescriptor + 1, 0, &fds, &fdexception, timeout < 0 ? 0 : &tv); +#else + retval = qt_safe_select(socketDescriptor + 1, 0, &fds, 0, timeout < 0 ? 0 : &tv); +#endif - if (retval != -1 || errno != EINTR) - break; - if (timeout > 0) { - // recalculate the timeout - int t = timeout - timer.elapsed(); - if (t < 0) { - // oops, timeout turned negative? - retval = -1; - break; +#ifdef Q_OS_SYMBIAN + bool selectForExec = false; + if(retval != 0) { + if(retval < 0) { + qWarning("nativeSelect(....) returned < 0 for socket %d", socketDescriptor); } - - tv.tv_sec = t / 1000; - tv.tv_usec = (t % 1000) * 1000; + selectForExec = FD_ISSET(socketDescriptor, &fdexception); } - } while (true); + if(selectForExec) { + qWarning("nativeSelect (selectForRead %d, retVal %d, errno %d) Unexpected exception for fd %d", + selectForRead, retval, errno, socketDescriptor); + } +#endif return retval; } int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool checkWrite, - bool *selectForRead, bool *selectForWrite) const + bool *selectForRead, bool *selectForWrite) const { fd_set fdread; FD_ZERO(&fdread); @@ -876,18 +986,48 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool c if (checkWrite) FD_SET(socketDescriptor, &fdwrite); +#ifdef Q_OS_SYMBIAN + fd_set fdexception; + FD_ZERO(&fdexception); + FD_SET(socketDescriptor, &fdexception); +#endif + struct timeval tv; tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; + int ret; +#ifndef Q_OS_SYMBIAN + ret = qt_safe_select(socketDescriptor + 1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv); +#else QTime timer; timer.start(); - int ret; do { - ret = select(socketDescriptor + 1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv); - if (ret != -1 || errno != EINTR) + ret = ::select(socketDescriptor + 1, &fdread, &fdwrite, &fdexception, timeout < 0 ? 0 : &tv); + bool selectForExec = false; + if(ret != 0) { + if(ret < 0) { + qWarning("nativeSelect(....) returned < 0 for socket %d", socketDescriptor); + } + selectForExec = FD_ISSET(socketDescriptor, &fdexception); + } + if(selectForExec) { + qWarning("nativeSelect (checkRead %d, checkWrite %d, ret %d, errno %d): Unexpected expectfds ready in fd %d", + checkRead, checkWrite, ret, errno, socketDescriptor); + if (checkRead) + FD_SET(socketDescriptor, &fdread); + if (checkWrite) + FD_SET(socketDescriptor, &fdwrite); + + if ((ret == -1) && ( errno == ECONNREFUSED || errno == EPIPE )) + ret = 1; + + } + + if (ret != -1 || errno != EINTR) { break; + } if (timeout > 0) { // recalculate the timeout @@ -902,11 +1042,13 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool c tv.tv_usec = (t % 1000) * 1000; } } while (true); +#endif + if (ret <= 0) return ret; - *selectForRead = FD_ISSET(socketDescriptor, &fdread); *selectForWrite = FD_ISSET(socketDescriptor, &fdwrite); + return ret; } diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index b8e6c49..655a18f 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -166,11 +166,11 @@ static QByteArray qt_prettyDebug(const char *data, int len, int maxLength) Extracts the port and address from a sockaddr, and stores them in \a port and \a addr if they are non-null. */ -static inline void qt_socket_getPortAndAddress(SOCKET socketDescriptor, struct sockaddr *sa, quint16 *port, QHostAddress *address) +static inline void qt_socket_getPortAndAddress(SOCKET socketDescriptor, const qt_sockaddr *sa, quint16 *port, QHostAddress *address) { #if !defined (QT_NO_IPV6) - if (sa->sa_family == AF_INET6) { - qt_sockaddr_in6 *sa6 = (qt_sockaddr_in6 *)sa; + if (sa->a.sa_family == AF_INET6) { + const qt_sockaddr_in6 *sa6 = &sa->a6; Q_IPV6ADDR tmp; for (int i = 0; i < 16; ++i) tmp.c[i] = sa6->sin6_addr.qt_s6_addr[i]; @@ -182,8 +182,8 @@ static inline void qt_socket_getPortAndAddress(SOCKET socketDescriptor, struct s WSANtohs(socketDescriptor, sa6->sin6_port, port); } else #endif - if (sa->sa_family == AF_INET) { - struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; + if (sa->a.sa_family == AF_INET) { + const sockaddr_in *sa4 = &sa->a4; unsigned long addr; WSANtohl(socketDescriptor, sa4->sin_addr.s_addr, &addr); QHostAddress a; @@ -362,6 +362,8 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co return -1; int n = -1; + int level = SOL_SOCKET; // default + switch (opt) { case QNativeSocketEngine::ReceiveBufferSocketOption: n = SO_RCVBUF; @@ -389,11 +391,18 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co case QNativeSocketEngine::ReceiveOutOfBandData: n = SO_OOBINLINE; break; + case QNativeSocketEngine::LowDelayOption: + level = IPPROTO_TCP; + n = TCP_NODELAY; + break; + case QNativeSocketEngine::KeepAliveOption: + n = SO_KEEPALIVE; + break; } int v = -1; QT_SOCKOPTLEN_T len = sizeof(v); - if (getsockopt(socketDescriptor, SOL_SOCKET, n, (char *) &v, &len) != -1) + if (getsockopt(socketDescriptor, level, n, (char *) &v, &len) != -1) return v; return -1; } @@ -409,6 +418,8 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt return false; int n = 0; + int level = SOL_SOCKET; // default + switch (opt) { case QNativeSocketEngine::ReceiveBufferSocketOption: n = SO_RCVBUF; @@ -440,9 +451,16 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt case QNativeSocketEngine::ReceiveOutOfBandData: n = SO_OOBINLINE; break; + case QNativeSocketEngine::LowDelayOption: + level = IPPROTO_TCP; + n = TCP_NODELAY; + break; + case QNativeSocketEngine::KeepAliveOption: + n = SO_KEEPALIVE; + break; } - if (::setsockopt(socketDescriptor, SOL_SOCKET, n, (char*)&v, sizeof(v)) != 0) { + if (::setsockopt(socketDescriptor, level, n, (char*)&v, sizeof(v)) != 0) { WS_ERROR_DEBUG(WSAGetLastError()); return false; } @@ -463,20 +481,15 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters() if (socketDescriptor == -1) return false; -#if !defined (QT_NO_IPV6) - struct qt_sockaddr_storage sa; -#else - struct sockaddr_in sa; -#endif - struct sockaddr *pSa = (struct sockaddr *) &sa; - - QT_SOCKLEN_T sz = sizeof(sa); + qt_sockaddr sa; + QT_SOCKLEN_T sockAddrSize = sizeof(sa); + // Determine local address memset(&sa, 0, sizeof(sa)); - if (::getsockname(socketDescriptor, pSa, &sz) == 0) { - qt_socket_getPortAndAddress(socketDescriptor, pSa, &localPort, &localAddress); + if (::getsockname(socketDescriptor, &sa.a, &sockAddrSize) == 0) { + qt_socket_getPortAndAddress(socketDescriptor, &sa, &localPort, &localAddress); // Determine protocol family - switch (pSa->sa_family) { + switch (sa.a.sa_family) { case AF_INET: socketProtocol = QAbstractSocket::IPv4Protocol; break; @@ -500,8 +513,8 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters() } memset(&sa, 0, sizeof(sa)); - if (::getpeername(socketDescriptor, pSa, &sz) == 0) { - qt_socket_getPortAndAddress(socketDescriptor, pSa, &peerPort, &peerAddress); + if (::getpeername(socketDescriptor, &sa.a, &sockAddrSize) == 0) { + qt_socket_getPortAndAddress(socketDescriptor, &sa, &peerPort, &peerAddress); } else { WS_ERROR_DEBUG(WSAGetLastError()); } @@ -533,8 +546,8 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin struct sockaddr_in sockAddrIPv4; qt_sockaddr_in6 sockAddrIPv6; - struct sockaddr *sockAddrPtr; - QT_SOCKLEN_T sockAddrSize; + struct sockaddr *sockAddrPtr = 0; + QT_SOCKLEN_T sockAddrSize = 0; qt_socket_setPortAndAddress(socketDescriptor, &sockAddrIPv4, &sockAddrIPv6, port, address, &sockAddrPtr, &sockAddrSize); @@ -638,8 +651,8 @@ bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &address, quint16 { struct sockaddr_in sockAddrIPv4; qt_sockaddr_in6 sockAddrIPv6; - struct sockaddr *sockAddrPtr; - QT_SOCKLEN_T sockAddrSize; + struct sockaddr *sockAddrPtr = 0; + QT_SOCKLEN_T sockAddrSize = 0; qt_socket_setPortAndAddress(socketDescriptor, &sockAddrIPv4, &sockAddrIPv6, port, address, &sockAddrPtr, &sockAddrSize); @@ -766,20 +779,9 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const { #if !defined(Q_OS_WINCE) // Create a sockaddr struct and reset its port number. -#if !defined(QT_NO_IPV6) - qt_sockaddr_in6 storage; - qt_sockaddr_in6 *storagePtrIPv6 = reinterpret_cast<qt_sockaddr_in6 *>(&storage); - storagePtrIPv6->sin6_port = 0; -#else - struct sockaddr storage; -#endif - sockaddr *storagePtr = reinterpret_cast<sockaddr *>(&storage); - storagePtr->sa_family = 0; - - sockaddr_in *storagePtrIPv4 = reinterpret_cast<sockaddr_in *>(&storage); - storagePtrIPv4->sin_port = 0; + qt_sockaddr storage; QT_SOCKLEN_T storageSize = sizeof(storage); - + memset(&storage, 0, storageSize); bool result = false; @@ -791,7 +793,7 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const buf.len = sizeof(c); DWORD available = 0; DWORD flags = MSG_PEEK; - int ret = ::WSARecvFrom(socketDescriptor, &buf, 1, &available, &flags, storagePtr, &storageSize,0,0); + int ret = ::WSARecvFrom(socketDescriptor, &buf, 1, &available, &flags, &storage.a, &storageSize,0,0); int err = WSAGetLastError(); if (ret == SOCKET_ERROR && err != WSAEMSGSIZE) { WS_ERROR_DEBUG(err); @@ -801,7 +803,7 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const // notifiers. flags = 0; ::WSARecvFrom(socketDescriptor, &buf, 1, &available, &flags, - storagePtr, &storageSize, 0, 0); + &storage.a, &storageSize, 0, 0); } } else { // If there's no error, or if our buffer was too small, there must be @@ -893,14 +895,11 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxL { qint64 ret = 0; -#if !defined(QT_NO_IPV6) - qt_sockaddr_storage aa; -#else - struct sockaddr_in aa; -#endif + qt_sockaddr aa; memset(&aa, 0, sizeof(aa)); QT_SOCKLEN_T sz; sz = sizeof(aa); + WSABUF buf; buf.buf = data; buf.len = maxLength; @@ -915,23 +914,17 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxL DWORD flags = 0; DWORD bytesRead = 0; - int wsaRet = ::WSARecvFrom(socketDescriptor, &buf, 1, &bytesRead, &flags, (struct sockaddr *) &aa, &sz,0,0); + int wsaRet = ::WSARecvFrom(socketDescriptor, &buf, 1, &bytesRead, &flags, &aa.a, &sz,0,0); if (wsaRet == SOCKET_ERROR) { int err = WSAGetLastError(); - if (err == WSAEMSGSIZE) { - // it is ok the buffer was to small if bytesRead is larger than - // maxLength (win 9x) then assume bytes read is really maxLenth - ret = qint64(bytesRead) > maxLength ? maxLength : qint64(bytesRead); - } else { - WS_ERROR_DEBUG(err); - setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString); - ret = -1; - } + WS_ERROR_DEBUG(err); + setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString); + ret = -1; } else { ret = qint64(bytesRead); } - qt_socket_getPortAndAddress(socketDescriptor, (struct sockaddr *) &aa, port, address); + qt_socket_getPortAndAddress(socketDescriptor, &aa, port, address); #if defined (QNATIVESOCKETENGINE_DEBUG) qDebug("QNativeSocketEnginePrivate::nativeReceiveDatagram(%p \"%s\", %li, %s, %i) == %li", @@ -950,41 +943,37 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l qint64 ret = -1; struct sockaddr_in sockAddrIPv4; qt_sockaddr_in6 sockAddrIPv6; - struct sockaddr *sockAddrPtr; - QT_SOCKLEN_T sockAddrSize; + struct sockaddr *sockAddrPtr = 0; + QT_SOCKLEN_T sockAddrSize = 0; qt_socket_setPortAndAddress(socketDescriptor, &sockAddrIPv4, &sockAddrIPv6, port, address, &sockAddrPtr, &sockAddrSize); - if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based && len > qint64(qt_socket_getMaxMsgSize(socketDescriptor))) { - // WSAEMSGSIZE is not reliable enough (win 9x) so we check max size our self. - setError(QAbstractSocket::DatagramTooLargeError, DatagramTooLargeErrorString); - } else { - WSABUF buf; + WSABUF buf; #if !defined(Q_OS_WINCE) - buf.buf = len ? (char*)data : 0; + buf.buf = len ? (char*)data : 0; #else - char tmp; - buf.buf = len ? (char*)data : &tmp; + char tmp; + buf.buf = len ? (char*)data : &tmp; #endif - buf.len = len; - DWORD flags = 0; - DWORD bytesSent = 0; - if (::WSASendTo(socketDescriptor, &buf, 1, &bytesSent, flags, sockAddrPtr, sockAddrSize, 0,0) == SOCKET_ERROR) { - int err = WSAGetLastError(); - WS_ERROR_DEBUG(err); - switch (err) { - case WSAEMSGSIZE: - setError(QAbstractSocket::DatagramTooLargeError, DatagramTooLargeErrorString); - break; - default: - setError(QAbstractSocket::NetworkError, SendDatagramErrorString); - break; - } - ret = -1; - } else { - ret = qint64(bytesSent); + buf.len = len; + DWORD flags = 0; + DWORD bytesSent = 0; + if (::WSASendTo(socketDescriptor, &buf, 1, &bytesSent, flags, sockAddrPtr, sockAddrSize, 0,0) == SOCKET_ERROR) { + int err = WSAGetLastError(); + WS_ERROR_DEBUG(err); + switch (err) { + case WSAEMSGSIZE: + setError(QAbstractSocket::DatagramTooLargeError, DatagramTooLargeErrorString); + break; + default: + setError(QAbstractSocket::NetworkError, SendDatagramErrorString); + break; } + ret = -1; + } else { + ret = qint64(bytesSent); } + #if defined (QNATIVESOCKETENGINE_DEBUG) qDebug("QNativeSocketEnginePrivate::nativeSendDatagram(%p \"%s\", %li, \"%s\", %i) == %li", data, qt_prettyDebug(data, qMin<qint64>(len, 16), len).data(), 0, address.toString().toLatin1().constData(), diff --git a/src/network/socket/qnet_unix_p.h b/src/network/socket/qnet_unix_p.h new file mode 100644 index 0000000..5e8ade7 --- /dev/null +++ b/src/network/socket/qnet_unix_p.h @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNET_UNIX_P_H +#define QNET_UNIX_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt code on Unix. This header file may change from version to +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qcore_unix_p.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#if defined(Q_OS_VXWORKS) +# include <sockLib.h> +#endif + +// for inet_addr +#include <netdb.h> +#include <arpa/inet.h> +#if defined(Q_OS_VXWORKS) +# include <hostLib.h> +#else +# include <resolv.h> +#endif + +QT_BEGIN_NAMESPACE + +// Almost always the same. If not, specify in qplatformdefs.h. +#if !defined(QT_SOCKOPTLEN_T) +# define QT_SOCKOPTLEN_T QT_SOCKLEN_T +#endif + +// UnixWare 7 redefines socket -> _socket +static inline int qt_safe_socket(int domain, int type, int protocol, int flags = 0) +{ + Q_ASSERT((flags & ~O_NONBLOCK) == 0); + + register int fd; +#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + int newtype = type | SOCK_CLOEXEC; + if (flags & O_NONBLOCK) + newtype |= SOCK_NONBLOCK; + fd = ::socket(domain, newtype, protocol); + if (fd != -1 || errno != EINVAL) + return fd; +#endif + + fd = ::socket(domain, type, protocol); + if (fd == -1) + return -1; + + ::fcntl(fd, F_SETFD, FD_CLOEXEC); + + // set non-block too? + if (flags & O_NONBLOCK) + ::fcntl(fd, F_SETFL, ::fcntl(fd, F_GETFL) | O_NONBLOCK); + + return fd; +} + +// Tru64 redefines accept -> _accept with _XOPEN_SOURCE_EXTENDED +static inline int qt_safe_accept(int s, struct sockaddr *addr, QT_SOCKLEN_T *addrlen, int flags = 0) +{ + Q_ASSERT((flags & ~O_NONBLOCK) == 0); + + register int fd; +#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC && defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + // use accept4 + int sockflags = SOCK_CLOEXEC; + if (flags & O_NONBLOCK) + sockflags |= SOCK_NONBLOCK; + fd = ::accept4(s, addr, static_cast<QT_SOCKLEN_T *>(addrlen), sockflags); + if (fd != -1 || !(errno == ENOSYS || errno == EINVAL)) + return fd; +#endif + + fd = ::accept(s, addr, static_cast<QT_SOCKLEN_T *>(addrlen)); + if (fd == -1) + return -1; + + ::fcntl(fd, F_SETFD, FD_CLOEXEC); + + // set non-block too? + if (flags & O_NONBLOCK) + ::fcntl(fd, F_SETFL, ::fcntl(fd, F_GETFL) | O_NONBLOCK); + + return fd; +} + +// UnixWare 7 redefines listen -> _listen +static inline int qt_safe_listen(int s, int backlog) +{ + return ::listen(s, backlog); +} + +static inline int qt_safe_connect(int sockfd, const struct sockaddr *addr, QT_SOCKLEN_T addrlen) +{ + register int ret; + // Solaris e.g. expects a non-const 2nd parameter + EINTR_LOOP(ret, QT_SOCKET_CONNECT(sockfd, const_cast<struct sockaddr *>(addr), addrlen)); + return ret; +} +#undef QT_SOCKET_CONNECT +#define QT_SOCKET_CONNECT qt_safe_connect + +#if defined(socket) +# undef socket +#endif +#if defined(accept) +# undef accept +#endif +#if defined(listen) +# undef listen +#endif + +// VxWorks' headers specify 'int' instead of '...' for the 3rd ioctl() parameter. +template <typename T> +static inline int qt_safe_ioctl(int sockfd, int request, T arg) +{ +#ifdef Q_OS_VXWORKS + return ::ioctl(sockfd, request, (int) arg); +#else + return ::ioctl(sockfd, request, arg); +#endif +} + +// VxWorks' headers do not specify any const modifiers +static inline in_addr_t qt_safe_inet_addr(const char *cp) +{ +#ifdef Q_OS_VXWORKS + return ::inet_addr((char *) cp); +#else + return ::inet_addr(cp); +#endif +} + +// VxWorks' headers do not specify any const modifiers +static inline int qt_safe_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *to, QT_SOCKLEN_T tolen) +{ +#ifdef MSG_NOSIGNAL + flags |= MSG_NOSIGNAL; +#endif + + register int ret; +#ifdef Q_OS_VXWORKS + EINTR_LOOP(ret, ::sendto(sockfd, (char *) buf, len, flags, (struct sockaddr *) to, tolen)); +#else + EINTR_LOOP(ret, ::sendto(sockfd, buf, len, flags, to, tolen)); +#endif + return ret; +} + +QT_END_NAMESPACE + +#endif // QNET_UNIX_P_H diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index 431459f..ed84533 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -59,7 +59,11 @@ QT_BEGIN_NAMESPACE +#ifdef Q_OS_SYMBIAN +static const int MaxWriteBufferSize = 4*1024; +#else static const int MaxWriteBufferSize = 128*1024; +#endif //#define QSOCKS5SOCKETLAYER_DEBUG @@ -153,7 +157,7 @@ static inline QString dump(const QByteArray &) { return QString(); } */ static bool qt_socks5_set_host_address_and_port(const QHostAddress &address, quint16 port, QByteArray *pBuf) { - QSOCKS5_DEBUG << "setting [" << address << ":" << port << "]"; + QSOCKS5_DEBUG << "setting [" << address << ':' << port << ']'; union { quint16 port; @@ -186,7 +190,7 @@ static bool qt_socks5_set_host_address_and_port(const QHostAddress &address, qui */ static bool qt_socks5_set_host_name_and_port(const QString &hostname, quint16 port, QByteArray *pBuf) { - QSOCKS5_DEBUG << "setting [" << hostname << ":" << port << "]"; + QSOCKS5_DEBUG << "setting [" << hostname << ':' << port << ']'; QByteArray encodedHostName = QUrl::toAce(hostname); QByteArray &buf = *pBuf; @@ -265,7 +269,7 @@ static bool qt_socks5_get_host_address_and_port(const QByteArray &buf, QHostAddr } if (ret) { - QSOCKS5_DEBUG << "got [" << address << ":" << port << "]"; + QSOCKS5_DEBUG << "got [" << address << ':' << port << ']'; *pAddress = address; *pPort = port; *pPos = pos; @@ -1124,7 +1128,7 @@ bool QSocks5SocketEngine::connectInternal() bool QSocks5SocketEngine::connectToHost(const QHostAddress &address, quint16 port) { Q_D(QSocks5SocketEngine); - QSOCKS5_DEBUG << "connectToHost" << address << ":" << port; + QSOCKS5_DEBUG << "connectToHost" << address << ':' << port; setPeerAddress(address); setPeerPort(port); @@ -1379,7 +1383,7 @@ bool QSocks5SocketEngine::bind(const QHostAddress &address, quint16 port) //### reset and error return false; } - QSOCKS5_DEBUG << "udp actual address and port" << d->localAddress << ":" << d->localPort; + QSOCKS5_DEBUG << "udp actual address and port" << d->localAddress << ':' << d->localPort; return true; #endif // QT_NO_UDPSOCKET } @@ -1478,7 +1482,7 @@ qint64 QSocks5SocketEngine::bytesAvailable() const qint64 QSocks5SocketEngine::read(char *data, qint64 maxlen) { Q_D(QSocks5SocketEngine); - QSOCKS5_Q_DEBUG << "read( , maxlen = " << maxlen << ")"; + QSOCKS5_Q_DEBUG << "read( , maxlen = " << maxlen << ')'; if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) { if (d->connectData->readBuffer.size() == 0) { if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) { @@ -1621,14 +1625,28 @@ qint64 QSocks5SocketEngine::pendingDatagramSize() const int QSocks5SocketEngine::option(SocketOption option) const { - Q_UNUSED(option); + Q_D(const QSocks5SocketEngine); + if (d->data && d->data->controlSocket) { + // convert the enum and call the real socket + if (option == QAbstractSocketEngine::LowDelayOption) + return d->data->controlSocket->socketOption(QAbstractSocket::LowDelayOption).toInt(); + if (option == QAbstractSocketEngine::KeepAliveOption) + return d->data->controlSocket->socketOption(QAbstractSocket::KeepAliveOption).toInt(); + } return -1; } bool QSocks5SocketEngine::setOption(SocketOption option, int value) { - Q_UNUSED(option); - Q_UNUSED(value); + Q_D(QSocks5SocketEngine); + if (d->data && d->data->controlSocket) { + // convert the enum and call the real socket + if (option == QAbstractSocketEngine::LowDelayOption) + d->data->controlSocket->setSocketOption(QAbstractSocket::LowDelayOption, value); + if (option == QAbstractSocketEngine::KeepAliveOption) + d->data->controlSocket->setSocketOption(QAbstractSocket::KeepAliveOption, value); + return true; + } return false; } @@ -1766,7 +1784,7 @@ void QSocks5SocketEngine::setReadNotificationEnabled(bool enable) { Q_D(QSocks5SocketEngine); - QSOCKS5_Q_DEBUG << "setReadNotificationEnabled(" << enable << ")"; + QSOCKS5_Q_DEBUG << "setReadNotificationEnabled(" << enable << ')'; bool emitSignal = false; if (!d->readNotificationEnabled @@ -1830,9 +1848,9 @@ QSocks5SocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socke QSOCKS5_DEBUG << "not proxying"; return 0; } - QSocks5SocketEngine *engine = new QSocks5SocketEngine(parent); + QScopedPointer<QSocks5SocketEngine> engine(new QSocks5SocketEngine(parent)); engine->setProxy(proxy); - return engine; + return engine.take(); } QAbstractSocketEngine *QSocks5SocketEngineHandler::createSocketEngine(int socketDescriptor, QObject *parent) diff --git a/src/network/socket/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp index 9f49d01..646987a 100644 --- a/src/network/socket/qtcpserver.cpp +++ b/src/network/socket/qtcpserver.cpp @@ -46,7 +46,7 @@ \brief The QTcpServer class provides a TCP-based server. \reentrant - \ingroup io + \ingroup network \inmodule QtNetwork This class makes it possible to accept incoming TCP connections. @@ -354,7 +354,12 @@ void QTcpServer::close() if (d->socketEngine) { d->socketEngine->close(); - d->socketEngine->deleteLater(); + QT_TRY { + d->socketEngine->deleteLater(); + } QT_CATCH(const std::bad_alloc &) { + // in out of memory situations, the socketEngine + // will be deleted in ~QTcpServer (it's a child-object of this) + } d->socketEngine = 0; } diff --git a/src/network/socket/qtcpsocket.cpp b/src/network/socket/qtcpsocket.cpp index 2770615..e04231c 100644 --- a/src/network/socket/qtcpsocket.cpp +++ b/src/network/socket/qtcpsocket.cpp @@ -47,7 +47,7 @@ \brief The QTcpSocket class provides a TCP socket. \reentrant - \ingroup io + \ingroup network \inmodule QtNetwork TCP (Transmission Control Protocol) is a reliable, @@ -60,10 +60,10 @@ \bold{Note:} TCP sockets cannot be opened in QIODevice::Unbuffered mode. - \sa QTcpServer, QUdpSocket, QFtp, QHttp, {Fortune Server Example}, - {Fortune Client Example}, {Threaded Fortune Server Example}, - {Blocking Fortune Client Example}, {Loopback Example}, - {Torrent Example} + \sa QTcpServer, QUdpSocket, QFtp, QNetworkAccessManager, + {Fortune Server Example}, {Fortune Client Example}, + {Threaded Fortune Server Example}, {Blocking Fortune Client Example}, + {Loopback Example}, {Torrent Example} */ #include "qlist.h" diff --git a/src/network/socket/qtcpsocket.h b/src/network/socket/qtcpsocket.h index d6fed15..3005faf 100644 --- a/src/network/socket/qtcpsocket.h +++ b/src/network/socket/qtcpsocket.h @@ -43,6 +43,7 @@ #define QTCPSOCKET_H #include <QtNetwork/qabstractsocket.h> +#include <QtCore/qvariant.h> QT_BEGIN_HEADER diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index 858e135..235003a 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -46,7 +46,7 @@ \reentrant \brief The QUdpSocket class provides a UDP socket. - \ingroup io + \ingroup network \inmodule QtNetwork UDP (User Datagram Protocol) is a lightweight, unreliable, @@ -95,6 +95,13 @@ This enum describes the different flags you can pass to modify the behavior of QUdpSocket::bind(). + \note On Symbian OS bind flags behaviour depends on process capabilties. + If process has NetworkControl capability, the bind attempt with + ReuseAddressHint will always succeed even if the address and port is already + bound by another socket with any flags. If process does not have + NetworkControl capability, the bind attempt to address and port already + bound by another socket will always fail. + \value ShareAddress Allow other services to bind to the same address and port. This is useful when multiple processes share the load of a single service by listening to the same address and port @@ -350,6 +357,9 @@ qint64 QUdpSocket::pendingDatagramSize() const fragmented by the IP layer before arriving at their final destination. + \warning In S60 5.0 and earlier versions, the writeDatagram return + value is not reliable for large datagrams. + \warning Calling this function on a connected UDP socket may result in an error and no packet being sent. If you are using a connected socket, use write() to send datagrams. @@ -368,6 +378,16 @@ qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddre return -1; qint64 sent = d->socketEngine->writeDatagram(data, size, address, port); +#ifdef Q_OS_SYMBIAN + if( QSysInfo::s60Version() <= QSysInfo::SV_S60_5_0 ) { + // This is evil hack, but for some reason native RSocket::SendTo returns 0, + // for large datagrams (such as 600 bytes). Based on comments from Open C team + // this should happen only in platforms <= S60 5.0. + // As an workaround, we just set sent = size + if( sent == 0 ) + sent = size; + } +#endif d->cachedSocketDescriptor = d->socketEngine->socketDescriptor(); if (sent >= 0) { @@ -380,7 +400,7 @@ qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddre return sent; } -/*! +/*! \fn qint64 QUdpSocket::writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port) \overload diff --git a/src/network/socket/socket.pri b/src/network/socket/socket.pri index b1fe64a..2bafe13 100644 --- a/src/network/socket/socket.pri +++ b/src/network/socket/socket.pri @@ -28,7 +28,8 @@ SOURCES += socket/qabstractsocketengine.cpp \ unix:SOURCES += socket/qnativesocketengine_unix.cpp \ socket/qlocalsocket_unix.cpp \ socket/qlocalserver_unix.cpp - +unix:HEADERS += \ + socket/qnet_unix_p.h win32:SOURCES += socket/qnativesocketengine_win.cpp \ socket/qlocalsocket_win.cpp \ @@ -42,5 +43,3 @@ wince*: { DEFINES += QT_LOCALSOCKET_TCP } - - |