Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qlocalsocket_win.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qlocalsocket_p.h"
6#include <qdeadlinetimer.h>
7
9
10using namespace Qt::StringLiterals;
11
12namespace {
13struct QSocketPoller
14{
15 QSocketPoller(const QLocalSocketPrivate &socket);
16
17 qint64 getRemainingTime(const QDeadlineTimer &deadline) const;
18 bool poll(const QDeadlineTimer &deadline);
19
20 enum { maxHandles = 2 };
21 HANDLE handles[maxHandles];
22 DWORD handleCount = 0;
23 bool waitForClose = false;
24 bool writePending = false;
25};
26
27QSocketPoller::QSocketPoller(const QLocalSocketPrivate &socket)
28{
29 if (socket.pipeWriter && socket.pipeWriter->isWriteOperationActive()) {
30 handles[handleCount++] = socket.pipeWriter->syncEvent();
31 writePending = true;
32 }
33 if (socket.pipeReader->isReadOperationActive())
34 handles[handleCount++] = socket.pipeReader->syncEvent();
35 else
36 waitForClose = true;
37}
38
39qint64 QSocketPoller::getRemainingTime(const QDeadlineTimer &deadline) const
40{
41 const qint64 sleepTime = 10;
42 qint64 remainingTime = deadline.remainingTime();
43 if (waitForClose && (remainingTime > sleepTime || remainingTime == -1))
44 return sleepTime;
45
46 return remainingTime;
47}
48
60bool QSocketPoller::poll(const QDeadlineTimer &deadline)
61{
62 Q_ASSERT(handleCount != 0);
63 QDeadlineTimer timer(getRemainingTime(deadline));
64 DWORD waitRet;
65
66 do {
67 waitRet = WaitForMultipleObjectsEx(handleCount, handles, FALSE,
68 timer.remainingTime(), TRUE);
69 } while (waitRet == WAIT_IO_COMPLETION);
70
71 if (waitRet == WAIT_TIMEOUT)
72 return waitForClose || !deadline.hasExpired();
73
74 return waitRet - WAIT_OBJECT_0 < handleCount;
75}
76} // anonymous namespace
77
79{
80 Q_Q(QLocalSocket);
81 pipeReader = new QWindowsPipeReader(q);
83 this, &QLocalSocketPrivate::_q_canRead);
84 q->connect(pipeReader, SIGNAL(pipeClosed()), SLOT(_q_pipeClosed()), Qt::QueuedConnection);
85 q->connect(pipeReader, SIGNAL(winError(ulong,QString)), SLOT(_q_winError(ulong,QString)));
86}
87
88void QLocalSocketPrivate::_q_winError(ulong windowsError, const QString &function)
89{
90 Q_Q(QLocalSocket);
92
93 // If the connectToServer fails due to WaitNamedPipe() time-out, assume ConnectionError
94 if (state == QLocalSocket::ConnectingState && windowsError == ERROR_SEM_TIMEOUT)
95 windowsError = ERROR_NO_DATA;
96
97 switch (windowsError) {
98 case ERROR_PIPE_NOT_CONNECTED:
99 case ERROR_BROKEN_PIPE:
100 case ERROR_NO_DATA:
102 errorString = QLocalSocket::tr("%1: Connection error").arg(function);
104 break;
105 case ERROR_FILE_NOT_FOUND:
107 errorString = QLocalSocket::tr("%1: Invalid name").arg(function);
109 break;
110 case ERROR_ACCESS_DENIED:
112 errorString = QLocalSocket::tr("%1: Access denied").arg(function);
114 break;
115 default:
117 errorString = QLocalSocket::tr("%1: Unknown error %2").arg(function).arg(windowsError);
118#if defined QLOCALSOCKET_DEBUG
119 qWarning() << "QLocalSocket error not handled:" << errorString;
120#endif
122 }
123
124 if (currentState != state) {
125 emit q->stateChanged(state);
127 emit q->disconnected();
128 }
129 emit q->errorOccurred(error);
130}
131
133 handle(INVALID_HANDLE_VALUE),
134 pipeWriter(0),
135 pipeReader(0),
136 error(QLocalSocket::UnknownSocketError),
137 state(QLocalSocket::UnconnectedState),
138 emittedReadyRead(false),
139 emittedBytesWritten(false)
140{
141}
142
143QLocalSocketPrivate::~QLocalSocketPrivate()
144{
146 Q_ASSERT(handle == INVALID_HANDLE_VALUE);
147 Q_ASSERT(pipeWriter == nullptr);
148}
149
150void QLocalSocket::connectToServer(OpenMode openMode)
151{
152 Q_D(QLocalSocket);
153 if (state() == ConnectedState || state() == ConnectingState) {
154 d->error = OperationError;
155 d->errorString = tr("Trying to connect while connection is in progress");
157 return;
158 }
159
161 d->errorString = QString();
162 d->state = ConnectingState;
163 emit stateChanged(d->state);
164 if (d->serverName.isEmpty()) {
165 d->error = ServerNotFoundError;
166 d->errorString = tr("%1: Invalid name").arg("QLocalSocket::connectToServer"_L1);
167 d->state = UnconnectedState;
168 emit errorOccurred(d->error);
169 emit stateChanged(d->state);
170 return;
171 }
172
173 const auto pipePath = "\\\\.\\pipe\\"_L1;
174 if (d->serverName.startsWith(pipePath))
175 d->fullServerName = d->serverName;
176 else
177 d->fullServerName = pipePath + d->serverName;
178 // Try to open a named pipe
179 HANDLE localSocket;
180 forever {
181 DWORD permissions = (openMode & QIODevice::ReadOnly) ? GENERIC_READ : 0;
182 permissions |= (openMode & QIODevice::WriteOnly) ? GENERIC_WRITE : 0;
183 localSocket = CreateFile(reinterpret_cast<const wchar_t *>(d->fullServerName.utf16()), // pipe name
184 permissions,
185 0, // no sharing
186 NULL, // default security attributes
187 OPEN_EXISTING, // opens existing pipe
188 FILE_FLAG_OVERLAPPED,
189 NULL); // no template file
190
191 if (localSocket != INVALID_HANDLE_VALUE)
192 break;
193 DWORD error = GetLastError();
194 // It is really an error only if it is not ERROR_PIPE_BUSY
195 if (ERROR_PIPE_BUSY != error) {
196 break;
197 }
198
199 // All pipe instances are busy, so wait until connected or up to 5 seconds.
200 if (!WaitNamedPipe((const wchar_t *)d->fullServerName.utf16(), 5000))
201 break;
202 }
203
204 if (localSocket == INVALID_HANDLE_VALUE) {
205 const DWORD winError = GetLastError();
206 d->_q_winError(winError, "QLocalSocket::connectToServer"_L1);
207 d->fullServerName = QString();
208 return;
209 }
210
211 // we have a valid handle
212 if (setSocketDescriptor(reinterpret_cast<qintptr>(localSocket), ConnectedState, openMode))
213 emit connected();
214}
215
217{
218 // QWindowsPipeReader's reading functions return error codes
219 // that don't match what we need.
220 switch (res) {
221 case 0: // EOF -> transform to error
222 return -1;
223 case -2: // EWOULDBLOCK -> no error, just no bytes
224 return 0;
225 default:
226 return res;
227 }
228}
229
230// This is reading from the buffer
232{
233 Q_D(QLocalSocket);
234
235 if (!maxSize)
236 return 0;
237
238 return transformPipeReaderResult(d->pipeReader->read(data, maxSize));
239}
240
242{
243 Q_D(QLocalSocket);
244
245 if (!maxSize)
246 return 0;
247
248 // QIODevice::readLine() reserves space for the trailing '\0' byte,
249 // so we must read 'maxSize + 1' bytes.
250 return transformPipeReaderResult(d->pipeReader->readLine(data, maxSize + 1));
251}
252
254{
255 Q_D(QLocalSocket);
256
257 if (!maxSize)
258 return 0;
259
260 return transformPipeReaderResult(d->pipeReader->skip(maxSize));
261}
262
264{
265 Q_D(QLocalSocket);
266 if (!isValid()) {
267 d->error = OperationError;
268 d->errorString = tr("Socket is not connected");
269 return -1;
270 }
271
272 if (len == 0)
273 return 0;
274
275 if (!d->pipeWriter) {
276 d->pipeWriter = new QWindowsPipeWriter(d->handle, this);
278 d, &QLocalSocketPrivate::_q_bytesWritten);
280 d, &QLocalSocketPrivate::_q_writeFailed);
281 }
282
283 if (d->isWriteChunkCached(data, len))
284 d->pipeWriter->write(*(d->currentWriteChunk));
285 else
286 d->pipeWriter->write(data, len);
287
288 return len;
289}
290
292{
293 Q_D(QLocalSocket);
294 if (d->pipeWriter) {
295 delete d->pipeWriter;
296 d->pipeWriter = 0;
297 }
298 close();
299}
300
301void QLocalSocketPrivate::_q_canRead()
302{
303 Q_Q(QLocalSocket);
304 if (!emittedReadyRead) {
305 QScopedValueRollback<bool> guard(emittedReadyRead, true);
306 emit q->readyRead();
307 }
308}
309
310void QLocalSocketPrivate::_q_pipeClosed()
311{
312 Q_Q(QLocalSocket);
314 return;
315
318 emit q->stateChanged(state);
320 return;
321 }
322
325 pipeReader->stop();
326 delete pipeWriter;
327 pipeWriter = nullptr;
328 if (handle != INVALID_HANDLE_VALUE) {
329 DisconnectNamedPipe(handle);
330 CloseHandle(handle);
331 handle = INVALID_HANDLE_VALUE;
332 }
333
335 emit q->stateChanged(state);
336 emit q->readChannelFinished();
337 emit q->disconnected();
338}
339
341{
342 Q_D(const QLocalSocket);
343 qint64 available = QIODevice::bytesAvailable();
344 available += d->pipeReader->bytesAvailable();
345 return available;
346}
347
349{
350 Q_D(const QLocalSocket);
351 return d->pipeWriterBytesToWrite();
352}
353
354bool QLocalSocket::canReadLine() const
355{
356 Q_D(const QLocalSocket);
357 return QIODevice::canReadLine() || d->pipeReader->canReadLine();
358}
359
361{
362 Q_D(QLocalSocket);
363
365 d->pipeReader->stopAndClear();
366 d->serverName = QString();
367 d->fullServerName = QString();
369}
370
372{
373 Q_D(QLocalSocket);
374
375 return d->pipeWriter && d->pipeWriter->checkForWrite();
376}
377
379{
380 Q_D(QLocalSocket);
381
382 if (bytesToWrite() == 0) {
383 d->_q_pipeClosed();
384 } else if (d->state != QLocalSocket::ClosingState) {
386 emit stateChanged(d->state);
387 }
388}
389
391{
392 Q_D(const QLocalSocket);
393 return d->error;
394}
395
396bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
397 LocalSocketState socketState, OpenMode openMode)
398{
399 Q_D(QLocalSocket);
400 d->pipeReader->stop();
401 d->handle = reinterpret_cast<HANDLE>(socketDescriptor);
402 d->state = socketState;
403 d->pipeReader->setHandle(d->handle);
405 emit stateChanged(d->state);
406 if (d->state == ConnectedState && openMode.testFlag(QIODevice::ReadOnly))
407 d->pipeReader->startAsyncRead();
408 return true;
409}
410
411qint64 QLocalSocketPrivate::pipeWriterBytesToWrite() const
412{
413 return pipeWriter ? pipeWriter->bytesToWrite() : qint64(0);
414}
415
416void QLocalSocketPrivate::_q_bytesWritten(qint64 bytes)
417{
418 Q_Q(QLocalSocket);
419 if (!emittedBytesWritten) {
420 QScopedValueRollback<bool> guard(emittedBytesWritten, true);
421 emit q->bytesWritten(bytes);
422 }
424 q->disconnectFromServer();
425}
426
427void QLocalSocketPrivate::_q_writeFailed()
428{
429 Q_Q(QLocalSocket);
431 errorString = QLocalSocket::tr("Remote closed");
432 emit q->errorOccurred(error);
433
434 _q_pipeClosed();
435}
436
438{
439 Q_D(const QLocalSocket);
440 return reinterpret_cast<qintptr>(d->handle);
441}
442
444{
445 Q_D(const QLocalSocket);
446 return d->pipeReader->maxReadBufferSize();
447}
448
450{
451 Q_D(QLocalSocket);
452 d->pipeReader->setMaxReadBufferSize(size);
453}
454
455bool QLocalSocket::waitForConnected(int msecs)
456{
457 Q_UNUSED(msecs);
458 return (state() == ConnectedState);
459}
460
462{
463 Q_D(QLocalSocket);
464 if (state() == UnconnectedState) {
465 qWarning("QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState");
466 return false;
467 }
468
470 bool wasChecked = false;
471 while (!d->pipeReader->isPipeClosed()) {
472 if (wasChecked && deadline.hasExpired())
473 return false;
474
475 QSocketPoller poller(*d);
476 // The first parameter of the WaitForMultipleObjectsEx() call cannot
477 // be zero. So we have to call SleepEx() here.
478 if (!poller.writePending && poller.waitForClose) {
479 // Prevent waiting on the first pass, if both the pipe reader
480 // and the pipe writer are inactive.
481 if (wasChecked)
482 SleepEx(poller.getRemainingTime(deadline), TRUE);
483 } else if (!poller.poll(deadline)) {
484 return false;
485 }
486
487 if (d->pipeWriter)
488 d->pipeWriter->checkForWrite();
489
490 // When the read buffer is full, the read sequence is not running,
491 // so we need to peek the pipe to detect disconnection.
492 if (poller.waitForClose && isValid())
493 d->pipeReader->checkPipeState();
494
495 d->pipeReader->checkForReadyRead();
496 wasChecked = true;
497 }
498 d->_q_pipeClosed();
499 return true;
500}
501
502bool QLocalSocket::isValid() const
503{
504 Q_D(const QLocalSocket);
505 return d->handle != INVALID_HANDLE_VALUE;
506}
507
508bool QLocalSocket::waitForReadyRead(int msecs)
509{
510 Q_D(QLocalSocket);
511
512 if (d->state != QLocalSocket::ConnectedState)
513 return false;
514
516 while (!d->pipeReader->isPipeClosed()) {
517 QSocketPoller poller(*d);
518 if (poller.waitForClose || !poller.poll(deadline))
519 return false;
520
521 if (d->pipeWriter)
522 d->pipeWriter->checkForWrite();
523
524 if (d->pipeReader->checkForReadyRead())
525 return true;
526 }
527 d->_q_pipeClosed();
528 return false;
529}
530
532{
533 Q_D(QLocalSocket);
534
535 if (d->state == QLocalSocket::UnconnectedState)
536 return false;
537
539 bool wasChecked = false;
540 while (!d->pipeReader->isPipeClosed()) {
541 if (wasChecked && deadline.hasExpired())
542 return false;
543
544 QSocketPoller poller(*d);
545 if (!poller.writePending || !poller.poll(deadline))
546 return false;
547
548 Q_ASSERT(d->pipeWriter);
549 if (d->pipeWriter->checkForWrite())
550 return true;
551
552 if (poller.waitForClose && isValid())
553 d->pipeReader->checkPipeState();
554
555 d->pipeReader->checkForReadyRead();
556 wasChecked = true;
557 }
558 d->_q_pipeClosed();
559 return false;
560}
561
\inmodule QtCore
bool hasExpired() const noexcept
Returns true if this QDeadlineTimer object has expired, false if there remains time left.
qint64 remainingTime() const noexcept
Returns the remaining time in this QDeadlineTimer object in milliseconds.
virtual bool open(QIODeviceBase::OpenMode mode)
Opens the device and sets its OpenMode to mode.
QIODeviceBase::OpenMode openMode() const
Returns the mode in which the device has been opened; i.e.
virtual qint64 bytesAvailable() const
Returns the number of bytes that are available for reading.
virtual void close()
First emits aboutToClose(), then closes the device and sets its OpenMode to NotOpen.
virtual bool canReadLine() const
Returns true if a complete line of data can be read from the device; otherwise returns false.
QLocalSocket::LocalSocketState state
The QLocalSocket class provides a local socket.
bool setSocketDescriptor(qintptr socketDescriptor, LocalSocketState socketState=ConnectedState, OpenMode openMode=ReadWrite)
Initializes QLocalSocket with the native socket descriptor socketDescriptor.
void connected()
This signal is emitted after connectToServer() has been called and a connection has been successfully...
LocalSocketState state() const
Returns the state of the socket.
virtual qint64 readData(char *, qint64) override
\reimp
void abort()
Aborts the current connection and resets the socket.
virtual qint64 bytesToWrite() const override
\reimp
virtual bool canReadLine() const override
\reimp
bool waitForBytesWritten(int msecs=30000) override
\reimp
LocalSocketState
This enum describes the different states in which a socket can be.
bool isValid() const
Returns true if the socket is valid and ready for use; otherwise returns false.
bool flush()
This function writes as much as possible from the internal write buffer to the socket,...
LocalSocketError error() const
Returns the type of error that last occurred.
qint64 readBufferSize() const
Returns the size of the internal read buffer.
bool waitForDisconnected(int msecs=30000)
Waits until the socket has disconnected, up to msecs milliseconds.
void disconnectFromServer()
Attempts to close the socket.
qint64 readLineData(char *data, qint64 maxSize) override
\reimp
bool waitForConnected(int msecs=30000)
Waits until the socket is connected, up to msecs milliseconds.
bool waitForReadyRead(int msecs=30000) override
This function blocks until data is available for reading and the \l{QIODevice::}{readyRead()} signal ...
void connectToServer(OpenMode openMode=ReadWrite)
void setReadBufferSize(qint64 size)
Sets the size of QLocalSocket's internal read buffer to be size bytes.
qintptr socketDescriptor() const
Returns the native socket descriptor of the QLocalSocket object if this is available; otherwise retur...
virtual void close() override
Closes the I/O device for the socket and calls disconnectFromServer() to close the socket's connectio...
qint64 skipData(qint64 maxSize) override
\reimp
virtual qint64 bytesAvailable() const override
\reimp
void stateChanged(QLocalSocket::LocalSocketState socketState)
This signal is emitted whenever QLocalSocket's state changes.
virtual qint64 writeData(const char *, qint64) override
\reimp
LocalSocketError
The LocalServerError enumeration represents the errors that can occur.
void errorOccurred(QLocalSocket::LocalSocketError socketError)
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
Definition qobject_p.h:299
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
int remainingTime
the remaining time in milliseconds
Definition qtimer.h:24
void bytesWritten(qint64 bytes)
else opt state
[0]
QAbstractSocket::SocketState socketState(QIODevice *device)
Combined button and popup list for selecting options.
Q_MULTIMEDIA_EXPORT QString errorString(HRESULT hr)
void * HANDLE
@ QueuedConnection
DBusConnection const char DBusError * error
#define forever
Definition qforeach.h:78
static qint64 transformPipeReaderResult(qint64 res)
#define qWarning
Definition qlogging.h:166
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLuint64 GLenum void * handle
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint res
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLsizei len
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define tr(X)
#define emit
#define Q_UNUSED(x)
unsigned long ulong
Definition qtypes.h:35
long long qint64
Definition qtypes.h:60
ptrdiff_t qintptr
Definition qtypes.h:166
QDeadlineTimer deadline(30s)
QTcpSocket * socket
[1]
QTimer * timer
[3]