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

Last change on this file since 318 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 15.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qlocalsocket.h"
43#include "qlocalsocket_p.h"
44
45#include <private/qthread_p.h>
46#include <qcoreapplication.h>
47#include <qdebug.h>
48
49QT_BEGIN_NAMESPACE
50
51#define NOTIFYTIMEOUT 100
52
53void QLocalSocketPrivate::init()
54{
55 Q_Q(QLocalSocket);
56 QObject::connect(&dataNotifier, SIGNAL(timeout()), q, SLOT(_q_notified()));
57 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
58}
59
60void QLocalSocketPrivate::setErrorString(const QString &function)
61{
62 Q_Q(QLocalSocket);
63 BOOL windowsError = GetLastError();
64 QLocalSocket::LocalSocketState currentState = state;
65
66 // If the connectToServer fails due to WaitNamedPipe() time-out, assume ConnectionError
67 if (state == QLocalSocket::ConnectingState && windowsError == ERROR_SEM_TIMEOUT)
68 windowsError = ERROR_NO_DATA;
69
70 switch (windowsError) {
71 case ERROR_PIPE_NOT_CONNECTED:
72 case ERROR_BROKEN_PIPE:
73 case ERROR_NO_DATA:
74 error = QLocalSocket::ConnectionError;
75 errorString = QLocalSocket::tr("%1: Connection error").arg(function);
76 state = QLocalSocket::UnconnectedState;
77 break;
78 case ERROR_FILE_NOT_FOUND:
79 error = QLocalSocket::ServerNotFoundError;
80 errorString = QLocalSocket::tr("%1: Invalid name").arg(function);
81 state = QLocalSocket::UnconnectedState;
82 break;
83 default:
84 error = QLocalSocket::UnknownSocketError;
85 errorString = QLocalSocket::tr("%1: Unknown error %2").arg(function).arg(windowsError);
86#if defined QLOCALSOCKET_DEBUG
87 qWarning() << "QLocalSocket error not handled:" << errorString;
88#endif
89 state = QLocalSocket::UnconnectedState;
90 }
91
92 if (currentState != state) {
93 q->emit stateChanged(state);
94 if (state == QLocalSocket::UnconnectedState)
95 q->emit disconnected();
96 }
97 emit q->error(error);
98}
99
100QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
101 handle(INVALID_HANDLE_VALUE),
102 pipeWriter(0),
103 readBufferMaxSize(0),
104 error(QLocalSocket::UnknownSocketError),
105 readyReadEmitted(false),
106 pipeClosed(false),
107 state(QLocalSocket::UnconnectedState)
108{
109}
110
111void QLocalSocket::connectToServer(const QString &name, OpenMode openMode)
112{
113 Q_D(QLocalSocket);
114 if (state() == ConnectedState || state() == ConnectingState)
115 return;
116
117 d->error = QLocalSocket::UnknownSocketError;
118 d->errorString = QString();
119 d->state = ConnectingState;
120 emit stateChanged(d->state);
121 if (name.isEmpty()) {
122 d->error = QLocalSocket::ServerNotFoundError;
123 setErrorString(QLocalSocket::tr("%1: Invalid name").arg(QLatin1String("QLocalSocket::connectToServer")));
124 d->state = UnconnectedState;
125 emit error(d->error);
126 emit stateChanged(d->state);
127 return;
128 }
129
130 QString pipePath = QLatin1String("\\\\.\\pipe\\");
131 if (name.startsWith(pipePath))
132 d->fullServerName = name;
133 else
134 d->fullServerName = pipePath + name;
135 // Try to open a named pipe
136 HANDLE localSocket;
137 forever {
138 DWORD permissions = (openMode & QIODevice::ReadOnly) ? GENERIC_READ : 0;
139 permissions |= (openMode & QIODevice::WriteOnly) ? GENERIC_WRITE : 0;
140 QT_WA({
141 localSocket = CreateFileW(
142 (TCHAR*)d->fullServerName.utf16(), // pipe name
143 permissions,
144 0, // no sharing
145 NULL, // default security attributes
146 OPEN_EXISTING, // opens existing pipe
147 0, // default attributes
148 NULL); // no template file
149 }, {
150 localSocket = CreateFileA(
151 d->fullServerName.toLocal8Bit().constData(), // pipe name
152 permissions,
153 0, // no sharing
154 NULL, // default security attributes
155 OPEN_EXISTING, // opens existing pipe
156 0, // default attributes
157 NULL); // no template file
158 });
159 if (localSocket != INVALID_HANDLE_VALUE)
160 break;
161 DWORD error = GetLastError();
162 // It is really an error only if it is not ERROR_PIPE_BUSY
163 if (ERROR_PIPE_BUSY != error) {
164 break;
165 }
166
167 // All pipe instances are busy, so wait until connected or up to 5 seconds.
168 QT_WA({
169 if (!WaitNamedPipeW((TCHAR*)d->fullServerName.utf16(), 5000))
170 break;
171 }, {
172 if (!WaitNamedPipeA(d->fullServerName.toLocal8Bit().constData(), 5000))
173 break;
174 });
175 }
176
177 if (localSocket == INVALID_HANDLE_VALUE) {
178 d->setErrorString(QLatin1String("QLocalSocket::connectToServer"));
179 d->fullServerName = QString();
180 return;
181 }
182
183 // we have a valid handle
184 d->serverName = name;
185 if (setSocketDescriptor((quintptr)localSocket), openMode) {
186 d->handle = localSocket;
187 emit connected();
188 }
189}
190
191// This is reading from the buffer
192qint64 QLocalSocket::readData(char *data, qint64 maxSize)
193{
194 Q_D(QLocalSocket);
195 if (d->readBuffer.isEmpty()) {
196 if (!d->readFromSocket()) {
197 if (d->pipeClosed)
198 return -1;
199 return 0;
200 }
201 }
202
203 if (!d->dataNotifier.isActive() && d->threadData->eventDispatcher)
204 d->dataNotifier.start(NOTIFYTIMEOUT);
205
206 if (d->readBuffer.isEmpty())
207 return qint64(0);
208
209 // If readFromSocket() read data, copy it to its destination.
210 if (maxSize == 1) {
211 *data = d->readBuffer.getChar();
212 return 1;
213 }
214
215 qint64 bytesToRead = qMin(qint64(d->readBuffer.size()), maxSize);
216 qint64 readSoFar = 0;
217 while (readSoFar < bytesToRead) {
218 const char *ptr = d->readBuffer.readPointer();
219 int bytesToReadFromThisBlock = qMin(int(bytesToRead - readSoFar),
220 d->readBuffer.nextDataBlockSize());
221 memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
222 readSoFar += bytesToReadFromThisBlock;
223 d->readBuffer.free(bytesToReadFromThisBlock);
224 }
225 return readSoFar;
226}
227
228/*!
229 \internal
230 read from the socket
231 */
232qint64 QLocalSocketPrivate::readData(char *data, qint64 maxSize)
233{
234 DWORD bytesRead = 0;
235 overlapped.Offset = 0;
236 overlapped.OffsetHigh = 0;
237 bool success = ReadFile(handle, data, maxSize, &bytesRead, &overlapped);
238 if (!success && GetLastError() == ERROR_IO_PENDING)
239 if (GetOverlappedResult(handle, &overlapped, &bytesRead, TRUE))
240 success = true;
241 if (!success) {
242 setErrorString(QLatin1String("QLocalSocket::readData"));
243 return 0;
244 }
245 return bytesRead;
246}
247
248/*!
249 \internal
250 Reads data from the socket into the readbuffer
251 */
252bool QLocalSocketPrivate::readFromSocket()
253{
254 qint64 bytesToRead = bytesAvailable();
255 if (bytesToRead == 0)
256 return false;
257
258 if (readBufferMaxSize && bytesToRead
259 > (readBufferMaxSize - readBuffer.size()))
260 bytesToRead = readBufferMaxSize - readBuffer.size();
261
262 char *ptr = readBuffer.reserve(bytesToRead);
263 qint64 readBytes = readData(ptr, bytesToRead);
264 if (readBytes == 0) {
265 readBuffer.chop(bytesToRead);
266 return false;
267 }
268 readyReadEmitted = false;
269 readBuffer.chop(int(bytesToRead - (readBytes < 0 ? qint64(0) : readBytes)));
270 return true;
271}
272
273qint64 QLocalSocket::writeData(const char *data, qint64 maxSize)
274{
275 Q_D(QLocalSocket);
276 if (!d->pipeWriter) {
277 d->pipeWriter = new QWindowsPipeWriter(d->handle, this);
278 d->pipeWriter->start();
279 connect(d->pipeWriter, SIGNAL(canWrite()), this, SLOT(_q_canWrite()));
280 }
281 return d->pipeWriter->write(data, maxSize);
282}
283
284void QLocalSocket::abort()
285{
286 close();
287}
288
289/*!
290 The number of bytes available from the pipe
291 */
292qint64 QLocalSocketPrivate::bytesAvailable()
293{
294 Q_Q(QLocalSocket);
295 if (q->state() != QLocalSocket::ConnectedState)
296 return 0;
297 DWORD bytes;
298 if (PeekNamedPipe(handle, NULL, 0, NULL, &bytes, NULL)) {
299 return bytes;
300 } else {
301 if (ERROR_BROKEN_PIPE == GetLastError() && !pipeClosed) {
302 pipeClosed = true;
303 QTimer::singleShot(0, q, SLOT(_q_pipeClosed()));
304 }
305 }
306 return 0;
307}
308
309void QLocalSocketPrivate::_q_pipeClosed()
310{
311 Q_Q(QLocalSocket);
312 q->close();
313}
314
315qint64 QLocalSocket::bytesAvailable() const
316{
317 Q_D(const QLocalSocket);
318 qint64 available = QIODevice::bytesAvailable();
319 available += (qint64) d->readBuffer.size();
320 return available;
321}
322
323qint64 QLocalSocket::bytesToWrite() const
324{
325 Q_D(const QLocalSocket);
326 return (d->pipeWriter) ? d->pipeWriter->bytesToWrite() : 0;
327}
328
329bool QLocalSocket::canReadLine() const
330{
331 Q_D(const QLocalSocket);
332 if (state() != ConnectedState)
333 return false;
334 return (d->readBuffer.indexOf('\n') != -1 || QIODevice::canReadLine());
335}
336
337void QLocalSocket::close()
338{
339 Q_D(QLocalSocket);
340 if (state() == UnconnectedState)
341 return;
342
343 QIODevice::close();
344 d->state = ClosingState;
345 emit stateChanged(d->state);
346 d->readyReadEmitted = false;
347 emit readChannelFinished();
348 d->serverName = QString();
349 d->fullServerName = QString();
350
351 if (state() != UnconnectedState && bytesToWrite() > 0) {
352 disconnectFromServer();
353 return;
354 }
355 d->pipeClosed = false;
356 DisconnectNamedPipe(d->handle);
357 CloseHandle(d->handle);
358 d->handle = INVALID_HANDLE_VALUE;
359 d->state = UnconnectedState;
360 emit stateChanged(d->state);
361 emit disconnected();
362 if (d->pipeWriter) {
363 delete d->pipeWriter;
364 d->pipeWriter = 0;
365 }
366 d->dataNotifier.stop();
367}
368
369bool QLocalSocket::flush()
370{
371 Q_D(QLocalSocket);
372 if (d->pipeWriter)
373 return d->pipeWriter->waitForWrite(0);
374 return false;
375}
376
377void QLocalSocket::disconnectFromServer()
378{
379 Q_D(QLocalSocket);
380 flush();
381 if (d->pipeWriter && d->pipeWriter->bytesToWrite() != 0) {
382 d->state = QLocalSocket::ClosingState;
383 emit stateChanged(d->state);
384 } else {
385 close();
386 }
387}
388
389QLocalSocket::LocalSocketError QLocalSocket::error() const
390{
391 Q_D(const QLocalSocket);
392 return d->error;
393}
394
395bool QLocalSocket::setSocketDescriptor(quintptr socketDescriptor,
396 LocalSocketState socketState, OpenMode openMode)
397{
398 Q_D(QLocalSocket);
399 d->readBuffer.clear();
400 QIODevice::open(openMode);
401 d->handle = (int*)socketDescriptor;
402 d->state = socketState;
403 emit stateChanged(d->state);
404 if (d->threadData->eventDispatcher)
405 d->dataNotifier.start(NOTIFYTIMEOUT);
406 return true;
407}
408
409void QLocalSocketPrivate::_q_canWrite()
410{
411 Q_Q(QLocalSocket);
412 if (state == QLocalSocket::ClosingState)
413 q->close();
414}
415
416void QLocalSocketPrivate::_q_notified()
417{
418 Q_Q(QLocalSocket);
419 if (0 != bytesAvailable()) {
420 if (readBufferMaxSize == 0 || readBuffer.size() < readBufferMaxSize) {
421 if (!readFromSocket()) {
422 return;
423 }
424 // wait until buffer is cleared before starting again
425 if (readBufferMaxSize && readBuffer.size() == readBufferMaxSize) {
426 dataNotifier.stop();
427 }
428 }
429 if (!readyReadEmitted) {
430 readyReadEmitted = true;
431 q->emit readyRead();
432 }
433 }
434}
435
436quintptr QLocalSocket::socketDescriptor() const
437{
438 Q_D(const QLocalSocket);
439 return (quintptr)d->handle;
440}
441
442qint64 QLocalSocket::readBufferSize() const
443{
444 Q_D(const QLocalSocket);
445 return d->readBufferMaxSize;
446}
447
448void QLocalSocket::setReadBufferSize(qint64 size)
449{
450 Q_D(QLocalSocket);
451 d->readBufferMaxSize = size;
452}
453
454bool QLocalSocket::waitForConnected(int msecs)
455{
456 Q_UNUSED(msecs);
457 return (state() == ConnectedState);
458}
459
460bool QLocalSocket::waitForDisconnected(int msecs)
461{
462 Q_D(QLocalSocket);
463 if (state() == UnconnectedState)
464 return false;
465 QIncrementalSleepTimer timer(msecs);
466 forever {
467 d->_q_notified();
468 if (d->pipeClosed)
469 close();
470 if (state() == UnconnectedState)
471 return true;
472 Sleep(timer.nextSleepTime());
473 if (timer.hasTimedOut())
474 break;
475 }
476
477 return false;
478}
479
480bool QLocalSocket::isValid() const
481{
482 Q_D(const QLocalSocket);
483 return (d->handle != INVALID_HANDLE_VALUE);
484}
485
486bool QLocalSocket::waitForReadyRead(int msecs)
487{
488 Q_D(QLocalSocket);
489 QIncrementalSleepTimer timer(msecs);
490 forever {
491 d->_q_notified();
492 if (bytesAvailable() > 0) {
493 if (!d->readyReadEmitted) {
494 d->readyReadEmitted = true;
495 emit readyRead();
496 }
497 return true;
498 }
499
500 Sleep(timer.nextSleepTime());
501 if (timer.hasTimedOut())
502 break;
503 }
504
505 return false;
506}
507
508bool QLocalSocket::waitForBytesWritten(int msecs)
509{
510 Q_D(const QLocalSocket);
511 if (!d->pipeWriter)
512 return false;
513
514 QIncrementalSleepTimer timer(msecs);
515 forever {
516 if (d->pipeWriter->hadWritten())
517 return true;
518
519 if (d->pipeWriter->bytesToWrite() == 0)
520 return false;
521
522 // Wait for the pipe writer to acknowledge that it has
523 // written. This will succeed if either the pipe writer has
524 // already written the data, or if it manages to write data
525 // within the given timeout.
526 if (d->pipeWriter->waitForWrite(0))
527 return true;
528
529 Sleep(timer.nextSleepTime());
530 if (timer.hasTimedOut())
531 break;
532 }
533
534 return false;
535}
536
537QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.