140 perform a \e reverse lookup). On success, the resulting QHostInfo will |
138 perform a \e reverse lookup). On success, the resulting QHostInfo will |
141 contain both the resolved domain name and IP addresses for the host |
139 contain both the resolved domain name and IP addresses for the host |
142 name. Example: |
140 name. Example: |
143 |
141 |
144 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 4 |
142 \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 4 |
|
143 |
|
144 \note There is no guarantee on the order the signals will be emitted |
|
145 if you start multiple requests with lookupHost(). |
145 |
146 |
146 \sa abortHostLookup(), addresses(), error(), fromName() |
147 \sa abortHostLookup(), addresses(), error(), fromName() |
147 */ |
148 */ |
148 int QHostInfo::lookupHost(const QString &name, QObject *receiver, |
149 int QHostInfo::lookupHost(const QString &name, QObject *receiver, |
149 const char *member) |
150 const char *member) |
157 return -1; |
158 return -1; |
158 } |
159 } |
159 |
160 |
160 qRegisterMetaType<QHostInfo>("QHostInfo"); |
161 qRegisterMetaType<QHostInfo>("QHostInfo"); |
161 |
162 |
162 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) |
163 int id = theIdCounter.fetchAndAddRelaxed(1); // generate unique ID |
163 QWindowsSockInit bust; // makes sure WSAStartup was callled |
164 |
|
165 if (name.isEmpty()) { |
|
166 QHostInfo hostInfo(id); |
|
167 hostInfo.setError(QHostInfo::HostNotFound); |
|
168 hostInfo.setErrorString(QObject::tr("No host name given")); |
|
169 QScopedPointer<QHostInfoResult> result(new QHostInfoResult); |
|
170 QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)), |
|
171 receiver, member, Qt::QueuedConnection); |
|
172 result.data()->emitResultsReady(hostInfo); |
|
173 return id; |
|
174 } |
|
175 |
|
176 #ifdef QT_NO_THREAD |
|
177 QHostInfo hostInfo = QHostInfoAgent::fromName(name); |
|
178 hostInfo.setLookupId(id); |
|
179 QScopedPointer<QHostInfoResult> result(new QHostInfoResult); |
|
180 QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)), |
|
181 receiver, member, Qt::QueuedConnection); |
|
182 result.data()->emitResultsReady(hostInfo); |
|
183 #else |
|
184 QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id); |
|
185 QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); |
|
186 theHostInfoLookupManager()->scheduleLookup(runnable); |
164 #endif |
187 #endif |
165 |
188 |
166 QScopedPointer<QHostInfoResult> result(new QHostInfoResult); |
189 return id; |
167 result.data()->autoDelete = false; |
190 } |
168 QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)), |
191 |
169 receiver, member); |
192 /*! |
170 int id = result.data()->lookupId = theIdCounter.fetchAndAddRelaxed(1); |
193 Aborts the host lookup with the ID \a id, as returned by lookupHost(). |
171 |
194 |
172 if (name.isEmpty()) { |
195 \sa lookupHost(), lookupId() |
173 QHostInfo info(id); |
196 */ |
174 info.setError(QHostInfo::HostNotFound); |
197 void QHostInfo::abortHostLookup(int id) |
175 info.setErrorString(QObject::tr("No host name given")); |
198 { |
176 QMetaObject::invokeMethod(result.data(), "emitResultsReady", Qt::QueuedConnection, |
199 #ifndef QT_NO_THREAD |
177 Q_ARG(QHostInfo, info)); |
200 theHostInfoLookupManager()->abortLookup(id); |
178 result.take()->autoDelete = true; |
|
179 return id; |
|
180 } |
|
181 |
|
182 QHostInfoAgent *agent = theAgent(); |
|
183 agent->addHostName(name, result.take()); |
|
184 |
|
185 #if !defined QT_NO_THREAD |
|
186 if (!agent->isRunning()) |
|
187 agent->start(); |
|
188 #else |
201 #else |
189 // if (!agent->isRunning()) |
202 // we cannot abort if it was non threaded.. the result signal has already been posted |
190 agent->run(); |
203 Q_UNUSED(id); |
191 // else |
|
192 // agent->wakeOne(); |
|
193 #endif |
204 #endif |
194 return id; |
|
195 } |
|
196 |
|
197 /*! |
|
198 Aborts the host lookup with the ID \a id, as returned by lookupHost(). |
|
199 |
|
200 \sa lookupHost(), lookupId() |
|
201 */ |
|
202 void QHostInfo::abortHostLookup(int id) |
|
203 { |
|
204 QHostInfoAgent *agent = theAgent(); |
|
205 agent->abortLookup(id); |
|
206 } |
205 } |
207 |
206 |
208 /*! |
207 /*! |
209 Looks up the IP address(es) for the given host \a name. The |
208 Looks up the IP address(es) for the given host \a name. The |
210 function blocks during the lookup which means that execution of |
209 function blocks during the lookup which means that execution of |
226 |
225 |
227 return QHostInfoAgent::fromName(name); |
226 return QHostInfoAgent::fromName(name); |
228 } |
227 } |
229 |
228 |
230 /*! |
229 /*! |
231 \internal |
|
232 Pops a query off the queries list, performs a blocking call to |
|
233 QHostInfoAgent::lookupHost(), and emits the resultsReady() |
|
234 signal. This process repeats until the queries list is empty. |
|
235 */ |
|
236 void QHostInfoAgent::run() |
|
237 { |
|
238 #ifndef QT_NO_THREAD |
|
239 // Dont' allow thread termination during event delivery, but allow it |
|
240 // during the actual blocking host lookup stage. |
|
241 setTerminationEnabled(false); |
|
242 forever |
|
243 #endif |
|
244 { |
|
245 QHostInfoQuery *query; |
|
246 { |
|
247 #ifndef QT_NO_THREAD |
|
248 // the queries list is shared between threads. lock all |
|
249 // access to it. |
|
250 QMutexLocker locker(&mutex); |
|
251 if (!quit && queries.isEmpty()) |
|
252 cond.wait(&mutex); |
|
253 if (quit) { |
|
254 // Reset the quit variable in case QCoreApplication is |
|
255 // destroyed and recreated. |
|
256 quit = false; |
|
257 break; |
|
258 } |
|
259 if (queries.isEmpty()) |
|
260 continue; |
|
261 #else |
|
262 if (queries.isEmpty()) |
|
263 return; |
|
264 #endif |
|
265 query = queries.takeFirst(); |
|
266 pendingQueryId = query->object->lookupId; |
|
267 } |
|
268 |
|
269 #if defined(QHOSTINFO_DEBUG) |
|
270 qDebug("QHostInfoAgent::run(%p): looking up \"%s\"", this, |
|
271 query->hostName.toLatin1().constData()); |
|
272 #endif |
|
273 |
|
274 #ifndef QT_NO_THREAD |
|
275 // Start query - allow termination at this point, but not outside. We |
|
276 // don't want to all termination during event delivery, but we don't |
|
277 // want the lookup to prevent the app from quitting (the agent |
|
278 // destructor terminates the thread). |
|
279 setTerminationEnabled(true); |
|
280 #endif |
|
281 QHostInfo info = fromName(query->hostName); |
|
282 #ifndef QT_NO_THREAD |
|
283 setTerminationEnabled(false); |
|
284 #endif |
|
285 |
|
286 int id = query->object->lookupId; |
|
287 info.setLookupId(id); |
|
288 if (pendingQueryId == id) |
|
289 query->object->emitResultsReady(info); |
|
290 delete query; |
|
291 } |
|
292 } |
|
293 |
|
294 /*! |
|
295 \enum QHostInfo::HostInfoError |
230 \enum QHostInfo::HostInfoError |
296 |
231 |
297 This enum describes the various errors that can occur when trying |
232 This enum describes the various errors that can occur when trying |
298 to resolve a host name. |
233 to resolve a host name. |
299 |
234 |
465 Windows networks. |
400 Windows networks. |
466 |
401 |
467 \sa hostName() |
402 \sa hostName() |
468 */ |
403 */ |
469 |
404 |
|
405 #ifndef QT_NO_THREAD |
|
406 QHostInfoRunnable::QHostInfoRunnable(QString hn, int i) : toBeLookedUp(hn), id(i) |
|
407 { |
|
408 setAutoDelete(true); |
|
409 } |
|
410 |
|
411 // the QHostInfoLookupManager will at some point call this via a QThreadPool |
|
412 void QHostInfoRunnable::run() |
|
413 { |
|
414 QHostInfoLookupManager *manager = theHostInfoLookupManager(); |
|
415 // check aborted |
|
416 if (manager->wasAborted(id)) { |
|
417 manager->lookupFinished(this); |
|
418 return; |
|
419 } |
|
420 |
|
421 // check cache |
|
422 // FIXME |
|
423 |
|
424 // if not in cache: OS lookup |
|
425 QHostInfo hostInfo = QHostInfoAgent::fromName(toBeLookedUp); |
|
426 |
|
427 // save to cache |
|
428 // FIXME |
|
429 |
|
430 // check aborted again |
|
431 if (manager->wasAborted(id)) { |
|
432 manager->lookupFinished(this); |
|
433 return; |
|
434 } |
|
435 |
|
436 // signal emission |
|
437 hostInfo.setLookupId(id); |
|
438 resultEmitter.emitResultsReady(hostInfo); |
|
439 |
|
440 manager->lookupFinished(this); |
|
441 |
|
442 // thread goes back to QThreadPool |
|
443 } |
|
444 |
|
445 QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), wasDeleted(false) |
|
446 { |
|
447 moveToThread(QCoreApplicationPrivate::mainThread()); |
|
448 threadPool.setMaxThreadCount(5); // do 5 DNS lookups in parallel |
|
449 } |
|
450 |
|
451 QHostInfoLookupManager::~QHostInfoLookupManager() |
|
452 { |
|
453 wasDeleted = true; |
|
454 } |
|
455 |
|
456 void QHostInfoLookupManager::work() |
|
457 { |
|
458 if (wasDeleted) |
|
459 return; |
|
460 |
|
461 // goals of this function: |
|
462 // - launch new lookups via the thread pool |
|
463 // - make sure only one lookup per host/IP is in progress |
|
464 |
|
465 QMutexLocker locker(&mutex); |
|
466 |
|
467 if (!finishedLookups.isEmpty()) { |
|
468 // remove ID from aborted if it is in there |
|
469 for (int i = 0; i < finishedLookups.length(); i++) { |
|
470 abortedLookups.removeAll(finishedLookups.at(i)->id); |
|
471 } |
|
472 |
|
473 finishedLookups.clear(); |
|
474 } |
|
475 |
|
476 if (!postponedLookups.isEmpty()) { |
|
477 // try to start the postponed ones |
|
478 |
|
479 QMutableListIterator<QHostInfoRunnable*> iterator(postponedLookups); |
|
480 while (iterator.hasNext()) { |
|
481 QHostInfoRunnable* postponed = iterator.next(); |
|
482 |
|
483 // check if none of the postponed hostnames is currently running |
|
484 bool alreadyRunning = false; |
|
485 for (int i = 0; i < currentLookups.length(); i++) { |
|
486 if (currentLookups.at(i)->toBeLookedUp == postponed->toBeLookedUp) { |
|
487 alreadyRunning = true; |
|
488 break; |
|
489 } |
|
490 } |
|
491 if (!alreadyRunning) { |
|
492 iterator.remove(); |
|
493 scheduledLookups.prepend(postponed); // prepend! we want to finish it ASAP |
|
494 } |
|
495 } |
|
496 } |
|
497 |
|
498 if (!scheduledLookups.isEmpty()) { |
|
499 // try to start the new ones |
|
500 QMutableListIterator<QHostInfoRunnable*> iterator(scheduledLookups); |
|
501 while (iterator.hasNext()) { |
|
502 QHostInfoRunnable *scheduled = iterator.next(); |
|
503 |
|
504 // check if a lookup for this host is already running, then postpone |
|
505 for (int i = 0; i < currentLookups.size(); i++) { |
|
506 if (currentLookups.at(i)->toBeLookedUp == scheduled->toBeLookedUp) { |
|
507 iterator.remove(); |
|
508 postponedLookups.append(scheduled); |
|
509 scheduled = 0; |
|
510 break; |
|
511 } |
|
512 } |
|
513 |
|
514 if (scheduled && threadPool.tryStart(scheduled)) { |
|
515 // runnable now running in new thread, track this in currentLookups |
|
516 iterator.remove(); |
|
517 currentLookups.append(scheduled); |
|
518 } else if (scheduled) { |
|
519 // wanted to start, but could not because thread pool is busy |
|
520 break; |
|
521 } else { |
|
522 // was postponed, continue iterating |
|
523 continue; |
|
524 } |
|
525 }; |
|
526 } |
|
527 } |
|
528 |
|
529 // called by QHostInfo |
|
530 void QHostInfoLookupManager::scheduleLookup(QHostInfoRunnable *r) |
|
531 { |
|
532 if (wasDeleted) |
|
533 return; |
|
534 |
|
535 QMutexLocker locker(&this->mutex); |
|
536 scheduledLookups.enqueue(r); |
|
537 work(); |
|
538 } |
|
539 |
|
540 // called by QHostInfo |
|
541 void QHostInfoLookupManager::abortLookup(int id) |
|
542 { |
|
543 if (wasDeleted) |
|
544 return; |
|
545 |
|
546 QMutexLocker locker(&this->mutex); |
|
547 if (!abortedLookups.contains(id)) |
|
548 abortedLookups.append(id); |
|
549 } |
|
550 |
|
551 // called from QHostInfoRunnable |
|
552 bool QHostInfoLookupManager::wasAborted(int id) |
|
553 { |
|
554 if (wasDeleted) |
|
555 return true; |
|
556 |
|
557 QMutexLocker locker(&this->mutex); |
|
558 return abortedLookups.contains(id); |
|
559 } |
|
560 |
|
561 // called from QHostInfoRunnable |
|
562 void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r) |
|
563 { |
|
564 if (wasDeleted) |
|
565 return; |
|
566 |
|
567 QMutexLocker locker(&this->mutex); |
|
568 currentLookups.removeOne(r); |
|
569 finishedLookups.append(r); |
|
570 work(); |
|
571 } |
|
572 |
|
573 #endif // QT_NO_THREAD |
|
574 |
470 QT_END_NAMESPACE |
575 QT_END_NAMESPACE |