• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KDECore

kstreamsocket.cpp

Go to the documentation of this file.
00001 /*  -*- C++ -*-
00002  *  Copyright (C) 2003 Thiago Macieira <thiago.macieira@kdemail.net>
00003  *
00004  *
00005  *  Permission is hereby granted, free of charge, to any person obtaining
00006  *  a copy of this software and associated documentation files (the
00007  *  "Software"), to deal in the Software without restriction, including
00008  *  without limitation the rights to use, copy, modify, merge, publish,
00009  *  distribute, sublicense, and/or sell copies of the Software, and to
00010  *  permit persons to whom the Software is furnished to do so, subject to
00011  *  the following conditions:
00012  *
00013  *  The above copyright notice and this permission notice shall be included 
00014  *  in all copies or substantial portions of the Software.
00015  *
00016  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00017  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00018  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00019  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
00020  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
00021  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00022  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00023  */
00024 
00025 #include <config.h>
00026 
00027 #include <qsocketnotifier.h>
00028 #include <qdatetime.h>
00029 #include <qtimer.h>
00030 #include <qguardedptr.h>
00031 
00032 #include "ksocketaddress.h"
00033 #include "kresolver.h"
00034 #include "ksocketdevice.h"
00035 #include "kstreamsocket.h"
00036 
00037 using namespace KNetwork;
00038 
00039 class KNetwork::KStreamSocketPrivate
00040 {
00041 public:
00042   KResolverResults::ConstIterator local, peer;
00043   QTime startTime;
00044   QTimer timer;
00045 
00046   int timeout;
00047 
00048   inline KStreamSocketPrivate()
00049     : timeout(0)
00050   { }
00051 };
00052 
00053 KStreamSocket::KStreamSocket(const QString& node, const QString& service,
00054                  QObject* parent, const char *name)
00055   : KClientSocketBase(parent, name), d(new KStreamSocketPrivate)
00056 {
00057   peerResolver().setNodeName(node);
00058   peerResolver().setServiceName(service);
00059   peerResolver().setFamily(KResolver::KnownFamily);
00060   localResolver().setFamily(KResolver::KnownFamily);
00061 
00062   setSocketOptions(socketOptions() & ~Blocking);
00063 
00064   QObject::connect(&d->timer, SIGNAL(timeout()), this, SLOT(timeoutSlot()));
00065 }
00066 
00067 KStreamSocket::~KStreamSocket()
00068 {
00069   delete d;
00070   // KClientSocketBase's destructor closes the socket
00071 }
00072 
00073 int KStreamSocket::timeout() const
00074 {
00075   return d->timeout;
00076 }
00077 
00078 int KStreamSocket::remainingTimeout() const
00079 {
00080   if (state() != Connecting)
00081     return timeout();
00082   if (timeout() <= 0)
00083     return 0;
00084 
00085   return timeout() - d->startTime.elapsed();
00086 }
00087 
00088 void KStreamSocket::setTimeout(int msecs)
00089 {
00090   d->timeout = msecs;
00091 
00092   if (state() == Connecting)
00093     d->timer.changeInterval(msecs);
00094 }
00095 
00096 bool KStreamSocket::bind(const QString& node, const QString& service)
00097 {
00098   if (state() != Idle)
00099     return false;
00100 
00101   if (!node.isNull())
00102     localResolver().setNodeName(node);
00103   if (!service.isNull())
00104     localResolver().setServiceName(service);
00105   return true;
00106 }
00107 
00108 bool KStreamSocket::connect(const QString& node, const QString& service)
00109 {
00110   if (state() == Connected)
00111     return true;        // already connected
00112 
00113   if (state() > Connected)
00114     return false;       // can't do much here
00115 
00116   if (!node.isNull())
00117     peerResolver().setNodeName(node);
00118   if (!service.isNull())
00119     peerResolver().setServiceName(service);
00120 
00121   if (state() == Connecting && !blocking())
00122     {
00123       setError(IO_ConnectError, InProgress);
00124       emit gotError(InProgress);
00125       return true;      // we're already connecting
00126     }
00127 
00128   if (state() < HostFound)
00129     {
00130       // connection hasn't started yet
00131       if (!blocking())
00132     {
00133       QObject::connect(this, SIGNAL(hostFound()), SLOT(hostFoundSlot()));
00134       return lookup();
00135     }
00136 
00137       // blocking mode
00138       if (!lookup())
00139     return false;       // lookup failure
00140     }
00141 
00142   /*
00143    * lookup results are available here
00144    */
00145 
00146   if (timeout() > 0)
00147     {
00148       if (!blocking() && !d->timer.isActive())
00149     d->timer.start(timeout(), true);
00150       else
00151     {
00152       // blocking connection with timeout
00153       // this must be handled as a special case because it requires a
00154       // non-blocking socket
00155 
00156       d->timer.stop();  // no need for a timer here
00157 
00158       socketDevice()->setBlocking(false);
00159       while (true)
00160         {
00161           connectionEvent();
00162           if (state() < Connecting)
00163         return false;   // error connecting
00164           if (state() == Connected)
00165         return true;    // connected!
00166 
00167           if (remainingTimeout() <= 0)
00168         {
00169           // we've timed out
00170           timeoutSlot();
00171           return false;
00172         }
00173 
00174           if (socketDevice()->error() == InProgress)
00175         {
00176           bool timedout;
00177           socketDevice()->poll(remainingTimeout(), &timedout);
00178           if (timedout)
00179             {
00180               timeoutSlot();
00181               return false;
00182             }
00183         }
00184         }
00185     }
00186     }
00187 
00188   connectionEvent();
00189   return error() == NoError;
00190 }
00191 
00192 bool KStreamSocket::connect(const KResolverEntry& entry)
00193 {
00194   return KClientSocketBase::connect(entry);
00195 }
00196 
00197 void KStreamSocket::hostFoundSlot()
00198 {
00199   QObject::disconnect(this, SLOT(hostFoundSlot()));
00200   if (timeout() > 0)
00201     d->timer.start(timeout(), true);
00202   QTimer::singleShot(0, this, SLOT(connectionEvent()));
00203 }
00204 
00205 void KStreamSocket::connectionEvent()
00206 {
00207   if (state() != HostFound && state() != Connecting)
00208     return;         // nothing to do
00209 
00210   const KResolverResults& peer = peerResults();
00211   if (state() == HostFound)
00212     {
00213       d->startTime.start();
00214 
00215       setState(Connecting);
00216       emit stateChanged(Connecting);
00217       d->peer = peer.begin();
00218       d->local = localResults().begin(); // just to be on the safe side
00219     }
00220 
00221   while (d->peer != peer.end())
00222     {
00223       const KResolverEntry &r = *d->peer;
00224 
00225       if (socketDevice()->socket() != -1)
00226     {
00227       // we have an existing file descriptor
00228       // this means that we've got activity in it (connection result)
00229       if (socketDevice()->connect(r) && socketDevice()->error() == NoError)
00230         {
00231           // yes, it did connect!
00232           connectionSucceeded(r);
00233           return;
00234         }
00235       else if (socketDevice()->error() == InProgress)
00236         // nope, still in progress
00237         return;
00238 
00239       // no, the socket failed to connect
00240       copyError();
00241       socketDevice()->close();
00242       ++d->peer;
00243       continue;
00244     }
00245 
00246       // try to bind
00247       if (!bindLocallyFor(r))
00248     {
00249       // could not find a matching family
00250       ++d->peer;
00251       continue;
00252     }
00253 
00254       {
00255     bool skip = false;
00256     emit aboutToConnect(r, skip);
00257     if (skip)
00258       {
00259         ++d->peer;
00260         continue;
00261       }
00262       }
00263 
00264       if (socketDevice()->connect(r) || socketDevice()->error() == InProgress)
00265     {
00266       // socket is attempting to connect
00267       if (socketDevice()->error() == InProgress)
00268         {
00269           QSocketNotifier *n = socketDevice()->readNotifier();
00270           QObject::connect(n, SIGNAL(activated(int)),
00271                    this, SLOT(connectionEvent()));
00272           n->setEnabled(true);
00273 
00274           n = socketDevice()->writeNotifier();
00275           QObject::connect(n, SIGNAL(activated(int)),
00276                    this, SLOT(connectionEvent()));
00277           n->setEnabled(true);
00278 
00279           return;       // wait for activity
00280         }
00281 
00282       // socket has connected
00283       connectionSucceeded(r);
00284       return;
00285     }
00286 
00287       // connection failed
00288       // try next
00289       copyError();
00290       socketDevice()->close();
00291       ++d->peer;
00292     }
00293 
00294   // that was the last item
00295   socketDevice()->setSocketOptions(socketOptions());
00296   setState(Idle);
00297   emit stateChanged(Idle);
00298   emit gotError(error());
00299   return;
00300 }
00301 
00302 void KStreamSocket::timeoutSlot()
00303 {
00304   if (state() != Connecting)
00305     return;
00306 
00307   // halt the connections
00308   socketDevice()->close();  // this also kills the notifiers
00309 
00310   setError(IO_TimeOutError, Timeout);
00311   setState(HostFound);
00312   emit stateChanged(HostFound);
00313 
00314   QGuardedPtr<KStreamSocket> that = this;
00315   emit gotError(Timeout);
00316   if (!that.isNull())
00317     emit timedOut();
00318 }
00319 
00320 bool KStreamSocket::bindLocallyFor(const KResolverEntry& peer)
00321 {
00322   const KResolverResults& local = localResults();
00323 
00324   if (local.isEmpty())
00325     // user doesn't want to bind to any specific local address
00326     return true;
00327 
00328   bool foundone = false;
00329   // scan the local resolution for a matching family
00330   for (d->local = local.begin(); d->local != local.end(); ++d->local)
00331     if ((*d->local).family() == peer.family())
00332       {
00333     // found a suitable address!
00334     foundone = true;
00335 
00336     if (socketDevice()->bind(*d->local))
00337       return true;
00338       }
00339   
00340   if (!foundone)
00341     {
00342       // found nothing
00343       setError(IO_BindError, NotSupported);
00344       emit gotError(NotSupported);
00345     }
00346   else
00347     copyError();
00348   return false;
00349 }
00350 
00351 void KStreamSocket::connectionSucceeded(const KResolverEntry& peer)
00352 {
00353   QObject::disconnect(socketDevice()->readNotifier(), 0, this, SLOT(connectionEvent()));
00354   QObject::disconnect(socketDevice()->writeNotifier(), 0, this, SLOT(connectionEvent()));
00355 
00356   resetError();
00357   setFlags(IO_Sequential | IO_Raw | IO_ReadWrite | IO_Open | IO_Async);
00358   setState(Connected);
00359   socketDevice()->setSocketOptions(socketOptions());
00360   d->timer.stop();
00361   emit stateChanged(Connected);
00362   
00363   if (!localResults().isEmpty())
00364     emit bound(*d->local);
00365   emit connected(peer);
00366 }
00367 
00368 #include "kstreamsocket.moc"

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal