summaryrefslogtreecommitdiffstats
path: root/src/network/socket/qlocalsocket_win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/socket/qlocalsocket_win.cpp')
-rw-r--r--src/network/socket/qlocalsocket_win.cpp537
1 files changed, 537 insertions, 0 deletions
diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp
new file mode 100644
index 0000000..e759d0b
--- /dev/null
+++ b/src/network/socket/qlocalsocket_win.cpp
@@ -0,0 +1,537 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlocalsocket.h"
+#include "qlocalsocket_p.h"
+
+#include <private/qthread_p.h>
+#include <qcoreapplication.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+#define NOTIFYTIMEOUT 100
+
+void QLocalSocketPrivate::init()
+{
+ Q_Q(QLocalSocket);
+ QObject::connect(&dataNotifier, SIGNAL(timeout()), q, SLOT(_q_notified()));
+ overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+}
+
+void QLocalSocketPrivate::setErrorString(const QString &function)
+{
+ Q_Q(QLocalSocket);
+ BOOL windowsError = GetLastError();
+ QLocalSocket::LocalSocketState currentState = state;
+
+ // If the connectToServer fails due to WaitNamedPipe() time-out, assume ConnectionError
+ if (state == QLocalSocket::ConnectingState && windowsError == ERROR_SEM_TIMEOUT)
+ windowsError = ERROR_NO_DATA;
+
+ switch (windowsError) {
+ case ERROR_PIPE_NOT_CONNECTED:
+ case ERROR_BROKEN_PIPE:
+ case ERROR_NO_DATA:
+ error = QLocalSocket::ConnectionError;
+ errorString = QLocalSocket::tr("%1: Connection error").arg(function);
+ state = QLocalSocket::UnconnectedState;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ error = QLocalSocket::ServerNotFoundError;
+ errorString = QLocalSocket::tr("%1: Invalid name").arg(function);
+ state = QLocalSocket::UnconnectedState;
+ break;
+ default:
+ error = QLocalSocket::UnknownSocketError;
+ errorString = QLocalSocket::tr("%1: Unknown error %2").arg(function).arg(windowsError);
+#if defined QLOCALSOCKET_DEBUG
+ qWarning() << "QLocalSocket error not handled:" << errorString;
+#endif
+ state = QLocalSocket::UnconnectedState;
+ }
+
+ if (currentState != state) {
+ q->emit stateChanged(state);
+ if (state == QLocalSocket::UnconnectedState)
+ q->emit disconnected();
+ }
+ emit q->error(error);
+}
+
+QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
+ handle(INVALID_HANDLE_VALUE),
+ pipeWriter(0),
+ readBufferMaxSize(0),
+ error(QLocalSocket::UnknownSocketError),
+ readyReadEmitted(false),
+ pipeClosed(false),
+ state(QLocalSocket::UnconnectedState)
+{
+}
+
+void QLocalSocket::connectToServer(const QString &name, OpenMode openMode)
+{
+ Q_D(QLocalSocket);
+ if (state() == ConnectedState || state() == ConnectingState)
+ return;
+
+ d->error = QLocalSocket::UnknownSocketError;
+ d->errorString = QString();
+ d->state = ConnectingState;
+ emit stateChanged(d->state);
+ if (name.isEmpty()) {
+ d->error = QLocalSocket::ServerNotFoundError;
+ setErrorString(QLocalSocket::tr("%1: Invalid name").arg(QLatin1String("QLocalSocket::connectToServer")));
+ d->state = UnconnectedState;
+ emit error(d->error);
+ emit stateChanged(d->state);
+ return;
+ }
+
+ QString pipePath = QLatin1String("\\\\.\\pipe\\");
+ if (name.startsWith(pipePath))
+ d->fullServerName = name;
+ else
+ d->fullServerName = pipePath + name;
+ // Try to open a named pipe
+ HANDLE localSocket;
+ 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
+ 0, // default attributes
+ 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
+ 0, // default attributes
+ NULL); // no template file
+ });
+ if (localSocket != INVALID_HANDLE_VALUE)
+ break;
+ DWORD error = GetLastError();
+ // It is really an error only if it is not ERROR_PIPE_BUSY
+ if (ERROR_PIPE_BUSY != error) {
+ break;
+ }
+
+ // 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 (localSocket == INVALID_HANDLE_VALUE) {
+ d->setErrorString(QLatin1String("QLocalSocket::connectToServer"));
+ d->fullServerName = QString();
+ return;
+ }
+
+ // we have a valid handle
+ d->serverName = name;
+ if (setSocketDescriptor((quintptr)localSocket), openMode) {
+ d->handle = localSocket;
+ emit connected();
+ }
+}
+
+// This is reading from the buffer
+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) {
+ *data = d->readBuffer.getChar();
+ return 1;
+ }
+
+ 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);
+ }
+ return readSoFar;
+}
+
+/*!
+ \internal
+ read from the socket
+ */
+qint64 QLocalSocketPrivate::readData(char *data, qint64 maxSize)
+{
+ 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;
+ }
+ return bytesRead;
+}
+
+/*!
+ \internal
+ Reads data from the socket into the readbuffer
+ */
+bool QLocalSocketPrivate::readFromSocket()
+{
+ qint64 bytesToRead = bytesAvailable();
+ if (bytesToRead == 0)
+ return false;
+
+ if (readBufferMaxSize && bytesToRead
+ > (readBufferMaxSize - readBuffer.size()))
+ bytesToRead = readBufferMaxSize - readBuffer.size();
+
+ char *ptr = readBuffer.reserve(bytesToRead);
+ qint64 readBytes = readData(ptr, bytesToRead);
+ if (readBytes == 0) {
+ readBuffer.chop(bytesToRead);
+ return false;
+ }
+ readyReadEmitted = false;
+ readBuffer.chop(int(bytesToRead - (readBytes < 0 ? qint64(0) : readBytes)));
+ return true;
+}
+
+qint64 QLocalSocket::writeData(const char *data, qint64 maxSize)
+{
+ Q_D(QLocalSocket);
+ if (!d->pipeWriter) {
+ d->pipeWriter = new QWindowsPipeWriter(d->handle, this);
+ d->pipeWriter->start();
+ connect(d->pipeWriter, SIGNAL(canWrite()), this, SLOT(_q_canWrite()));
+ }
+ return d->pipeWriter->write(data, maxSize);
+}
+
+void QLocalSocket::abort()
+{
+ close();
+}
+
+/*!
+ The number of bytes available from the pipe
+ */
+qint64 QLocalSocketPrivate::bytesAvailable()
+{
+ Q_Q(QLocalSocket);
+ if (q->state() != QLocalSocket::ConnectedState)
+ return 0;
+ DWORD bytes;
+ if (PeekNamedPipe(handle, NULL, 0, NULL, &bytes, NULL)) {
+ return bytes;
+ } else {
+ if (ERROR_BROKEN_PIPE == GetLastError() && !pipeClosed) {
+ pipeClosed = true;
+ QTimer::singleShot(0, q, SLOT(_q_pipeClosed()));
+ }
+ }
+ return 0;
+}
+
+void QLocalSocketPrivate::_q_pipeClosed()
+{
+ Q_Q(QLocalSocket);
+ q->close();
+}
+
+qint64 QLocalSocket::bytesAvailable() const
+{
+ Q_D(const QLocalSocket);
+ qint64 available = QIODevice::bytesAvailable();
+ available += (qint64) d->readBuffer.size();
+ return available;
+}
+
+qint64 QLocalSocket::bytesToWrite() const
+{
+ Q_D(const QLocalSocket);
+ return (d->pipeWriter) ? d->pipeWriter->bytesToWrite() : 0;
+}
+
+bool QLocalSocket::canReadLine() const
+{
+ Q_D(const QLocalSocket);
+ if (state() != ConnectedState)
+ return false;
+ return (d->readBuffer.indexOf('\n') != -1 || QIODevice::canReadLine());
+}
+
+void QLocalSocket::close()
+{
+ Q_D(QLocalSocket);
+ if (state() == UnconnectedState)
+ return;
+
+ QIODevice::close();
+ d->state = ClosingState;
+ emit stateChanged(d->state);
+ d->readyReadEmitted = false;
+ emit readChannelFinished();
+ d->serverName = QString();
+ d->fullServerName = QString();
+
+ if (state() != UnconnectedState && bytesToWrite() > 0) {
+ disconnectFromServer();
+ return;
+ }
+ d->pipeClosed = false;
+ DisconnectNamedPipe(d->handle);
+ CloseHandle(d->handle);
+ d->handle = INVALID_HANDLE_VALUE;
+ d->state = UnconnectedState;
+ emit stateChanged(d->state);
+ emit disconnected();
+ if (d->pipeWriter) {
+ delete d->pipeWriter;
+ d->pipeWriter = 0;
+ }
+ d->dataNotifier.stop();
+}
+
+bool QLocalSocket::flush()
+{
+ Q_D(QLocalSocket);
+ if (d->pipeWriter)
+ return d->pipeWriter->waitForWrite(0);
+ return false;
+}
+
+void QLocalSocket::disconnectFromServer()
+{
+ Q_D(QLocalSocket);
+ flush();
+ if (d->pipeWriter && d->pipeWriter->bytesToWrite() != 0) {
+ d->state = QLocalSocket::ClosingState;
+ emit stateChanged(d->state);
+ } else {
+ close();
+ }
+}
+
+QLocalSocket::LocalSocketError QLocalSocket::error() const
+{
+ Q_D(const QLocalSocket);
+ return d->error;
+}
+
+bool QLocalSocket::setSocketDescriptor(quintptr socketDescriptor,
+ LocalSocketState socketState, OpenMode openMode)
+{
+ Q_D(QLocalSocket);
+ d->readBuffer.clear();
+ QIODevice::open(openMode);
+ d->handle = (int*)socketDescriptor;
+ d->state = socketState;
+ emit stateChanged(d->state);
+ if (d->threadData->eventDispatcher)
+ d->dataNotifier.start(NOTIFYTIMEOUT);
+ return true;
+}
+
+void QLocalSocketPrivate::_q_canWrite()
+{
+ Q_Q(QLocalSocket);
+ if (state == QLocalSocket::ClosingState)
+ q->close();
+}
+
+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();
+ }
+ }
+}
+
+quintptr QLocalSocket::socketDescriptor() const
+{
+ Q_D(const QLocalSocket);
+ return (quintptr)d->handle;
+}
+
+qint64 QLocalSocket::readBufferSize() const
+{
+ Q_D(const QLocalSocket);
+ return d->readBufferMaxSize;
+}
+
+void QLocalSocket::setReadBufferSize(qint64 size)
+{
+ Q_D(QLocalSocket);
+ d->readBufferMaxSize = size;
+}
+
+bool QLocalSocket::waitForConnected(int msecs)
+{
+ Q_UNUSED(msecs);
+ return (state() == ConnectedState);
+}
+
+bool QLocalSocket::waitForDisconnected(int msecs)
+{
+ Q_D(QLocalSocket);
+ if (state() == UnconnectedState)
+ return false;
+ QIncrementalSleepTimer timer(msecs);
+ forever {
+ d->_q_notified();
+ if (d->pipeClosed)
+ close();
+ if (state() == UnconnectedState)
+ return true;
+ Sleep(timer.nextSleepTime());
+ if (timer.hasTimedOut())
+ break;
+ }
+
+ return false;
+}
+
+bool QLocalSocket::isValid() const
+{
+ Q_D(const QLocalSocket);
+ return (d->handle != INVALID_HANDLE_VALUE);
+}
+
+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;
+ }
+
+ return false;
+}
+
+bool QLocalSocket::waitForBytesWritten(int msecs)
+{
+ Q_D(const QLocalSocket);
+ 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;
+}
+
+QT_END_NAMESPACE