source: trunk/src/network/socket/qlocalsocket_win.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 18.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtNetwork module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qlocalsocket_p.h"
43
44#include <private/qthread_p.h>
45#include <qcoreapplication.h>
46#include <qdebug.h>
47
48QT_BEGIN_NAMESPACE
49
50void QLocalSocketPrivate::init()
51{
52 Q_Q(QLocalSocket);
53 memset(&overlapped, 0, sizeof(overlapped));
54 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
55 dataReadNotifier = new QWinEventNotifier(overlapped.hEvent, q);
56 q->connect(dataReadNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_notified()));
57}
58
59void QLocalSocketPrivate::setErrorString(const QString &function)
60{
61 Q_Q(QLocalSocket);
62 BOOL windowsError = GetLastError();
63 QLocalSocket::LocalSocketState currentState = state;
64
65 // If the connectToServer fails due to WaitNamedPipe() time-out, assume ConnectionError
66 if (state == QLocalSocket::ConnectingState && windowsError == ERROR_SEM_TIMEOUT)
67 windowsError = ERROR_NO_DATA;
68
69 switch (windowsError) {
70 case ERROR_PIPE_NOT_CONNECTED:
71 case ERROR_BROKEN_PIPE:
72 case ERROR_NO_DATA:
73 error = QLocalSocket::ConnectionError;
74 errorString = QLocalSocket::tr("%1: Connection error").arg(function);
75 state = QLocalSocket::UnconnectedState;
76 break;
77 case ERROR_FILE_NOT_FOUND:
78 error = QLocalSocket::ServerNotFoundError;
79 errorString = QLocalSocket::tr("%1: Invalid name").arg(function);
80 state = QLocalSocket::UnconnectedState;
81 break;
82 default:
83 error = QLocalSocket::UnknownSocketError;
84 errorString = QLocalSocket::tr("%1: Unknown error %2").arg(function).arg(windowsError);
85#if defined QLOCALSOCKET_DEBUG
86 qWarning() << "QLocalSocket error not handled:" << errorString;
87#endif
88 state = QLocalSocket::UnconnectedState;
89 }
90
91 if (currentState != state) {
92 q->emit stateChanged(state);
93 if (state == QLocalSocket::UnconnectedState)
94 q->emit disconnected();
95 }
96 emit q->error(error);
97}
98
99QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
100 handle(INVALID_HANDLE_VALUE),
101 pipeWriter(0),
102 readBufferMaxSize(0),
103 actualReadBufferSize(0),
104 error(QLocalSocket::UnknownSocketError),
105 readSequenceStarted(false),
106 pendingReadyRead(false),
107 pipeClosed(false),
108 state(QLocalSocket::UnconnectedState)
109{
110}
111
112QLocalSocketPrivate::~QLocalSocketPrivate()
113{
114 destroyPipeHandles();
115 CloseHandle(overlapped.hEvent);
116}
117
118void QLocalSocketPrivate::destroyPipeHandles()
119{
120 if (handle != INVALID_HANDLE_VALUE) {
121 DisconnectNamedPipe(handle);
122 CloseHandle(handle);
123 }
124}
125
126void QLocalSocket::connectToServer(const QString &name, OpenMode openMode)
127{
128 Q_D(QLocalSocket);
129 if (state() == ConnectedState || state() == ConnectingState)
130 return;
131
132 d->error = QLocalSocket::UnknownSocketError;
133 d->errorString = QString();
134 d->state = ConnectingState;
135 emit stateChanged(d->state);
136 if (name.isEmpty()) {
137 d->error = QLocalSocket::ServerNotFoundError;
138 setErrorString(QLocalSocket::tr("%1: Invalid name").arg(QLatin1String("QLocalSocket::connectToServer")));
139 d->state = UnconnectedState;
140 emit error(d->error);
141 emit stateChanged(d->state);
142 return;
143 }
144
145 QString pipePath = QLatin1String("\\\\.\\pipe\\");
146 if (name.startsWith(pipePath))
147 d->fullServerName = name;
148 else
149 d->fullServerName = pipePath + name;
150 // Try to open a named pipe
151 HANDLE localSocket;
152 forever {
153 DWORD permissions = (openMode & QIODevice::ReadOnly) ? GENERIC_READ : 0;
154 permissions |= (openMode & QIODevice::WriteOnly) ? GENERIC_WRITE : 0;
155 localSocket = CreateFile((const wchar_t *)d->fullServerName.utf16(), // pipe name
156 permissions,
157 0, // no sharing
158 NULL, // default security attributes
159 OPEN_EXISTING, // opens existing pipe
160 FILE_FLAG_OVERLAPPED,
161 NULL); // no template file
162
163 if (localSocket != INVALID_HANDLE_VALUE)
164 break;
165 DWORD error = GetLastError();
166 // It is really an error only if it is not ERROR_PIPE_BUSY
167 if (ERROR_PIPE_BUSY != error) {
168 break;
169 }
170
171 // All pipe instances are busy, so wait until connected or up to 5 seconds.
172 if (!WaitNamedPipe((const wchar_t *)d->fullServerName.utf16(), 5000))
173 break;
174 }
175
176 if (localSocket == INVALID_HANDLE_VALUE) {
177 d->setErrorString(QLatin1String("QLocalSocket::connectToServer"));
178 d->fullServerName = QString();
179 return;
180 }
181
182 // we have a valid handle
183 d->serverName = name;
184 if (setSocketDescriptor((quintptr)localSocket, ConnectedState, openMode)) {
185 d->handle = localSocket;
186 emit connected();
187 }
188}
189
190// This is reading from the buffer
191qint64 QLocalSocket::readData(char *data, qint64 maxSize)
192{
193 Q_D(QLocalSocket);
194
195 if (d->pipeClosed && d->actualReadBufferSize == 0)
196 return -1; // signal EOF
197
198 qint64 readSoFar;
199 // If startAsyncRead() read data, copy it to its destination.
200 if (maxSize == 1 && d->actualReadBufferSize > 0) {
201 *data = d->readBuffer.getChar();
202 d->actualReadBufferSize--;
203 readSoFar = 1;
204 } else {
205 qint64 bytesToRead = qMin(qint64(d->actualReadBufferSize), maxSize);
206 readSoFar = 0;
207 while (readSoFar < bytesToRead) {
208 const char *ptr = d->readBuffer.readPointer();
209 int bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar,
210 qint64(d->readBuffer.nextDataBlockSize()));
211 memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
212 readSoFar += bytesToReadFromThisBlock;
213 d->readBuffer.free(bytesToReadFromThisBlock);
214 d->actualReadBufferSize -= bytesToReadFromThisBlock;
215 }
216 }
217
218 if (d->pipeClosed) {
219 if (d->actualReadBufferSize == 0)
220 QTimer::singleShot(0, this, SLOT(_q_pipeClosed()));
221 } else {
222 if (!d->readSequenceStarted)
223 d->startAsyncRead();
224 d->checkReadyRead();
225 }
226
227 return readSoFar;
228}
229
230/*!
231 \internal
232 Schedules or cancels a readyRead() emission depending on actual data availability
233 */
234void QLocalSocketPrivate::checkReadyRead()
235{
236 if (actualReadBufferSize > 0) {
237 if (!pendingReadyRead) {
238 Q_Q(QLocalSocket);
239 QTimer::singleShot(0, q, SLOT(_q_emitReadyRead()));
240 pendingReadyRead = true;
241 }
242 } else {
243 pendingReadyRead = false;
244 }
245}
246
247/*!
248 \internal
249 Reads data from the socket into the readbuffer
250 */
251void QLocalSocketPrivate::startAsyncRead()
252{
253 do {
254 DWORD bytesToRead = checkPipeState();
255 if (pipeClosed)
256 return;
257
258 if (bytesToRead == 0) {
259 // There are no bytes in the pipe but we need to
260 // start the overlapped read with some buffer size.
261 bytesToRead = initialReadBufferSize;
262 }
263
264 if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) {
265 bytesToRead = readBufferMaxSize - readBuffer.size();
266 if (bytesToRead == 0) {
267 // Buffer is full. User must read data from the buffer
268 // before we can read more from the pipe.
269 return;
270 }
271 }
272
273 char *ptr = readBuffer.reserve(bytesToRead);
274
275 readSequenceStarted = true;
276 if (ReadFile(handle, ptr, bytesToRead, NULL, &overlapped)) {
277 completeAsyncRead();
278 } else {
279 switch (GetLastError()) {
280 case ERROR_IO_PENDING:
281 // This is not an error. We're getting notified, when data arrives.
282 return;
283 case ERROR_MORE_DATA:
284 // This is not an error. The synchronous read succeeded.
285 // We're connected to a message mode pipe and the message
286 // didn't fit into the pipe's system buffer.
287 completeAsyncRead();
288 break;
289 case ERROR_PIPE_NOT_CONNECTED:
290 {
291 // It may happen, that the other side closes the connection directly
292 // after writing data. Then we must set the appropriate socket state.
293 pipeClosed = true;
294 Q_Q(QLocalSocket);
295 emit q->readChannelFinished();
296 return;
297 }
298 default:
299 setErrorString(QLatin1String("QLocalSocketPrivate::startAsyncRead"));
300 return;
301 }
302 }
303 } while (!readSequenceStarted);
304}
305
306/*!
307 \internal
308 Sets the correct size of the read buffer after a read operation.
309 Returns false, if an error occurred or the connection dropped.
310 */
311bool QLocalSocketPrivate::completeAsyncRead()
312{
313 ResetEvent(overlapped.hEvent);
314 readSequenceStarted = false;
315
316 DWORD bytesRead;
317 if (!GetOverlappedResult(handle, &overlapped, &bytesRead, TRUE)) {
318 switch (GetLastError()) {
319 case ERROR_MORE_DATA:
320 // This is not an error. We're connected to a message mode
321 // pipe and the message didn't fit into the pipe's system
322 // buffer. We will read the remaining data in the next call.
323 break;
324 case ERROR_PIPE_NOT_CONNECTED:
325 return false;
326 default:
327 setErrorString(QLatin1String("QLocalSocketPrivate::completeAsyncRead"));
328 return false;
329 }
330 }
331
332 actualReadBufferSize += bytesRead;
333 readBuffer.truncate(actualReadBufferSize);
334 return true;
335}
336
337qint64 QLocalSocket::writeData(const char *data, qint64 maxSize)
338{
339 Q_D(QLocalSocket);
340 if (!d->pipeWriter) {
341 d->pipeWriter = new QWindowsPipeWriter(d->handle, this);
342 connect(d->pipeWriter, SIGNAL(canWrite()), this, SLOT(_q_canWrite()));
343 connect(d->pipeWriter, SIGNAL(bytesWritten(qint64)), this, SIGNAL(bytesWritten(qint64)));
344 d->pipeWriter->start();
345 }
346 return d->pipeWriter->write(data, maxSize);
347}
348
349void QLocalSocket::abort()
350{
351 close();
352}
353
354/*!
355 \internal
356 Returns the number of available bytes in the pipe.
357 Sets QLocalSocketPrivate::pipeClosed to true if the connection is broken.
358 */
359DWORD QLocalSocketPrivate::checkPipeState()
360{
361 Q_Q(QLocalSocket);
362 DWORD bytes;
363 if (PeekNamedPipe(handle, NULL, 0, NULL, &bytes, NULL)) {
364 return bytes;
365 } else {
366 if (!pipeClosed) {
367 pipeClosed = true;
368 emit q->readChannelFinished();
369 if (actualReadBufferSize == 0)
370 QTimer::singleShot(0, q, SLOT(_q_pipeClosed()));
371 }
372 }
373 return 0;
374}
375
376void QLocalSocketPrivate::_q_pipeClosed()
377{
378 Q_Q(QLocalSocket);
379 q->close();
380}
381
382qint64 QLocalSocket::bytesAvailable() const
383{
384 Q_D(const QLocalSocket);
385 qint64 available = QIODevice::bytesAvailable();
386 available += (qint64) d->actualReadBufferSize;
387 return available;
388}
389
390qint64 QLocalSocket::bytesToWrite() const
391{
392 Q_D(const QLocalSocket);
393 return (d->pipeWriter) ? d->pipeWriter->bytesToWrite() : 0;
394}
395
396bool QLocalSocket::canReadLine() const
397{
398 Q_D(const QLocalSocket);
399 if (state() != ConnectedState)
400 return false;
401 return (QIODevice::canReadLine()
402 || d->readBuffer.indexOf('\n', d->actualReadBufferSize) != -1);
403}
404
405void QLocalSocket::close()
406{
407 Q_D(QLocalSocket);
408 if (state() == UnconnectedState)
409 return;
410
411 QIODevice::close();
412 d->state = ClosingState;
413 emit stateChanged(d->state);
414 if (!d->pipeClosed)
415 emit readChannelFinished();
416 d->serverName = QString();
417 d->fullServerName = QString();
418
419 if (state() != UnconnectedState && bytesToWrite() > 0) {
420 disconnectFromServer();
421 return;
422 }
423 d->readSequenceStarted = false;
424 d->pendingReadyRead = false;
425 d->pipeClosed = false;
426 d->destroyPipeHandles();
427 d->handle = INVALID_HANDLE_VALUE;
428 ResetEvent(d->overlapped.hEvent);
429 d->state = UnconnectedState;
430 emit stateChanged(d->state);
431 emit disconnected();
432 if (d->pipeWriter) {
433 delete d->pipeWriter;
434 d->pipeWriter = 0;
435 }
436}
437
438bool QLocalSocket::flush()
439{
440 Q_D(QLocalSocket);
441 if (d->pipeWriter)
442 return d->pipeWriter->waitForWrite(0);
443 return false;
444}
445
446void QLocalSocket::disconnectFromServer()
447{
448 Q_D(QLocalSocket);
449
450 // Are we still connected?
451 if (!isValid()) {
452 // If we have unwritten data, the pipeWriter is still present.
453 // It must be destroyed before close() to prevent an infinite loop.
454 delete d->pipeWriter;
455 d->pipeWriter = 0;
456 }
457
458 flush();
459 if (d->pipeWriter && d->pipeWriter->bytesToWrite() != 0) {
460 d->state = QLocalSocket::ClosingState;
461 emit stateChanged(d->state);
462 } else {
463 close();
464 }
465}
466
467QLocalSocket::LocalSocketError QLocalSocket::error() const
468{
469 Q_D(const QLocalSocket);
470 return d->error;
471}
472
473bool QLocalSocket::setSocketDescriptor(quintptr socketDescriptor,
474 LocalSocketState socketState, OpenMode openMode)
475{
476 Q_D(QLocalSocket);
477 d->readBuffer.clear();
478 d->actualReadBufferSize = 0;
479 QIODevice::open(openMode);
480 d->handle = (int*)socketDescriptor;
481 d->state = socketState;
482 emit stateChanged(d->state);
483 if (d->state == ConnectedState && openMode.testFlag(QIODevice::ReadOnly)) {
484 d->startAsyncRead();
485 d->checkReadyRead();
486 }
487 return true;
488}
489
490void QLocalSocketPrivate::_q_canWrite()
491{
492 Q_Q(QLocalSocket);
493 if (state == QLocalSocket::ClosingState)
494 q->close();
495}
496
497void QLocalSocketPrivate::_q_notified()
498{
499 Q_Q(QLocalSocket);
500 if (!completeAsyncRead()) {
501 pipeClosed = true;
502 emit q->readChannelFinished();
503 if (actualReadBufferSize == 0)
504 QTimer::singleShot(0, q, SLOT(_q_pipeClosed()));
505 return;
506 }
507 startAsyncRead();
508 pendingReadyRead = false;
509 emit q->readyRead();
510}
511
512void QLocalSocketPrivate::_q_emitReadyRead()
513{
514 if (pendingReadyRead) {
515 Q_Q(QLocalSocket);
516 pendingReadyRead = false;
517 emit q->readyRead();
518 }
519}
520
521quintptr QLocalSocket::socketDescriptor() const
522{
523 Q_D(const QLocalSocket);
524 return (quintptr)d->handle;
525}
526
527qint64 QLocalSocket::readBufferSize() const
528{
529 Q_D(const QLocalSocket);
530 return d->readBufferMaxSize;
531}
532
533void QLocalSocket::setReadBufferSize(qint64 size)
534{
535 Q_D(QLocalSocket);
536 d->readBufferMaxSize = size;
537}
538
539bool QLocalSocket::waitForConnected(int msecs)
540{
541 Q_UNUSED(msecs);
542 return (state() == ConnectedState);
543}
544
545bool QLocalSocket::waitForDisconnected(int msecs)
546{
547 Q_D(QLocalSocket);
548 if (state() == UnconnectedState)
549 return false;
550 if (!openMode().testFlag(QIODevice::ReadOnly)) {
551 qWarning("QLocalSocket::waitForDisconnected isn't supported for write only pipes.");
552 return false;
553 }
554 QIncrementalSleepTimer timer(msecs);
555 forever {
556 d->checkPipeState();
557 if (d->pipeClosed)
558 close();
559 if (state() == UnconnectedState)
560 return true;
561 Sleep(timer.nextSleepTime());
562 if (timer.hasTimedOut())
563 break;
564 }
565
566 return false;
567}
568
569bool QLocalSocket::isValid() const
570{
571 Q_D(const QLocalSocket);
572 if (d->handle == INVALID_HANDLE_VALUE)
573 return false;
574
575 return PeekNamedPipe(d->handle, NULL, 0, NULL, NULL, NULL);
576}
577
578bool QLocalSocket::waitForReadyRead(int msecs)
579{
580 Q_D(QLocalSocket);
581
582 if (bytesAvailable() > 0)
583 return true;
584
585 if (d->state != QLocalSocket::ConnectedState)
586 return false;
587
588 // We already know that the pipe is gone, but did not enter the event loop yet.
589 if (d->pipeClosed) {
590 close();
591 return false;
592 }
593
594 Q_ASSERT(d->readSequenceStarted);
595 DWORD result = WaitForSingleObject(d->overlapped.hEvent, msecs == -1 ? INFINITE : msecs);
596 switch (result) {
597 case WAIT_OBJECT_0:
598 d->_q_notified();
599 // We just noticed that the pipe is gone.
600 if (d->pipeClosed) {
601 close();
602 return false;
603 }
604 return true;
605 case WAIT_TIMEOUT:
606 return false;
607 }
608
609 qWarning("QLocalSocket::waitForReadyRead WaitForSingleObject failed with error code %d.", int(GetLastError()));
610 return false;
611}
612
613bool QLocalSocket::waitForBytesWritten(int msecs)
614{
615 Q_D(const QLocalSocket);
616 if (!d->pipeWriter)
617 return false;
618
619 // Wait for the pipe writer to acknowledge that it has
620 // written. This will succeed if either the pipe writer has
621 // already written the data, or if it manages to write data
622 // within the given timeout.
623 return d->pipeWriter->waitForWrite(msecs);
624}
625
626QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.