kstreamsocket.cpp

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 
00031 #include "ksocketaddress.h"
00032 #include "kresolver.h"
00033 #include "ksocketdevice.h"
00034 #include "kstreamsocket.h"
00035 
00036 using namespace KNetwork;
00037 
00038 class KNetwork::KStreamSocketPrivate
00039 {
00040 public:
00041   KResolverResults::ConstIterator local, peer;
00042   QTime startTime;
00043   QTimer timer;
00044 
00045   int timeout;
00046 
00047   inline KStreamSocketPrivate()
00048     : timeout(0)
00049   { }
00050 };
00051 
00052 KStreamSocket::KStreamSocket(const QString& node, const QString& service,
00053                  QObject* parent, const char *name)
00054   : KClientSocketBase(parent, name), d(new KStreamSocketPrivate)
00055 {
00056   peerResolver().setNodeName(node);
00057   peerResolver().setServiceName(service);
00058   peerResolver().setFamily(KResolver::KnownFamily);
00059   localResolver().setFamily(KResolver::KnownFamily);
00060 
00061   setSocketOptions(socketOptions() & ~Blocking);
00062 
00063   QObject::connect(&d->timer, SIGNAL(timeout()), this, SLOT(timeoutSlot()));
00064 }
00065 
00066 KStreamSocket::~KStreamSocket()
00067 {
00068   delete d;
00069   // KClientSocketBase's destructor closes the socket
00070 }
00071 
00072 int KStreamSocket::timeout() const
00073 {
00074   return d->timeout;
00075 }
00076 
00077 int KStreamSocket::remainingTimeout() const
00078 {
00079   if (state() != Connecting)
00080     return timeout();
00081   if (timeout() <= 0)
00082     return 0;
00083 
00084   return timeout() - d->startTime.elapsed();
00085 }
00086 
00087 void KStreamSocket::setTimeout(int msecs)
00088 {
00089   d->timeout = msecs;
00090 
00091   if (state() == Connecting)
00092     d->timer.changeInterval(msecs);
00093 }
00094 
00095 bool KStreamSocket::bind(const QString& node, const QString& service)
00096 {
00097   if (state() != Idle)
00098     return false;
00099 
00100   if (!node.isNull())
00101     localResolver().setNodeName(node);
00102   if (!service.isNull())
00103     localResolver().setServiceName(service);
00104   return true;
00105 }
00106 
00107 bool KStreamSocket::connect(const QString& node, const QString& service)
00108 {
00109   if (state() == Connected)
00110     return true;        // already connected
00111 
00112   if (state() > Connected)
00113     return false;       // can't do much here
00114 
00115   if (!node.isNull())
00116     peerResolver().setNodeName(node);
00117   if (!service.isNull())
00118     peerResolver().setServiceName(service);
00119 
00120   if (state() == Connecting && !blocking())
00121     {
00122       setError(IO_ConnectError, InProgress);
00123       emit gotError(InProgress);
00124       return true;      // we're already connecting
00125     }
00126 
00127   if (state() < HostFound)
00128     {
00129       // connection hasn't started yet
00130       if (!blocking())
00131     {
00132       QObject::connect(this, SIGNAL(hostFound()), SLOT(hostFoundSlot()));
00133       return lookup();
00134     }
00135 
00136       // blocking mode
00137       if (!lookup())
00138     return false;       // lookup failure
00139     }
00140 
00141   /*
00142    * lookup results are available here
00143    */
00144 
00145   if (timeout() > 0)
00146     {
00147       if (!blocking() && !d->timer.isActive())
00148     d->timer.start(timeout(), true);
00149       else
00150     {
00151       // blocking connection with timeout
00152       // this must be handled as a special case because it requires a
00153       // non-blocking socket
00154 
00155       d->timer.stop();  // no need for a timer here
00156 
00157       socketDevice()->setBlocking(false);
00158       while (true)
00159         {
00160           connectionEvent();
00161           if (state() < Connecting)
00162         return false;   // error connecting
00163           if (state() == Connected)
00164         return true;    // connected!
00165 
00166           if (remainingTimeout() <= 0)
00167         {
00168           // we've timed out
00169           timeoutSlot();
00170           return false;
00171         }
00172 
00173           if (socketDevice()->error() == InProgress)
00174         {
00175           bool timedout;
00176           socketDevice()->poll(remainingTimeout(), &timedout);
00177           if (timedout)
00178             {
00179               timeoutSlot();
00180               return false;
00181             }
00182         }
00183         }
00184     }
00185     }
00186 
00187   connectionEvent();
00188   return error() == NoError;
00189 }
00190 
00191 bool KStreamSocket::connect(const KResolverEntry& entry)
00192 {
00193   return KClientSocketBase::connect(entry);
00194 }
00195 
00196 void KStreamSocket::hostFoundSlot()
00197 {
00198   QObject::disconnect(this, SLOT(hostFoundSlot()));
00199   if (timeout() > 0)
00200     d->timer.start(timeout(), true);
00201   QTimer::singleShot(0, this, SLOT(connectionEvent()));
00202 }
00203 
00204 void KStreamSocket::connectionEvent()
00205 {
00206   if (state() != HostFound && state() != Connecting)
00207     return;         // nothing to do
00208 
00209   const KResolverResults& peer = peerResults();
00210   if (state() == HostFound)
00211     {
00212       d->startTime.start();
00213 
00214       setState(Connecting);
00215       emit stateChanged(Connecting);
00216       d->peer = peer.begin();
00217       d->local = localResults().begin(); // just to be on the safe side
00218     }
00219 
00220   while (d->peer != peer.end())
00221     {
00222       const KResolverEntry &r = *d->peer;
00223 
00224       if (socketDevice()->socket() != -1)
00225     {
00226       // we have an existing file descriptor
00227       // this means that we've got activity in it (connection result)
00228       if (socketDevice()->connect(r) && socketDevice()->error() == NoError)
00229         {
00230           // yes, it did connect!
00231           connectionSucceeded(r);
00232           return;
00233         }
00234       else if (socketDevice()->error() == InProgress)
00235         // nope, still in progress
00236         return;
00237 
00238       // no, the socket failed to connect
00239       copyError();
00240       socketDevice()->close();
00241       ++d->peer;
00242       continue;
00243     }
00244 
00245       // try to bind
00246       if (!bindLocallyFor(r))
00247     {
00248       // could not find a matching family
00249       ++d->peer;
00250       continue;
00251     }
00252 
00253       {
00254     bool skip = false;
00255     emit aboutToConnect(r, skip);
00256     if (skip)
00257       {
00258         ++d->peer;
00259         continue;
00260       }
00261       }
00262 
00263       if (socketDevice()->connect(r) || socketDevice()->error() == InProgress)
00264     {
00265       // socket is attempting to connect
00266       if (socketDevice()->error() == InProgress)
00267         {
00268           QSocketNotifier *n = socketDevice()->readNotifier();
00269           QObject::connect(n, SIGNAL(activated(int)),
00270                    this, SLOT(connectionEvent()));
00271           n->setEnabled(true);
00272 
00273           n = socketDevice()->writeNotifier();
00274           QObject::connect(n, SIGNAL(activated(int)),
00275                    this, SLOT(connectionEvent()));
00276           n->setEnabled(true);
00277 
00278           return;       // wait for activity
00279         }
00280 
00281       // socket has connected
00282       connectionSucceeded(r);
00283       return;
00284     }
00285 
00286       // connection failed
00287       // try next
00288       copyError();
00289       socketDevice()->close();
00290       ++d->peer;
00291     }
00292 
00293   // that was the last item
00294   socketDevice()->setSocketOptions(socketOptions());
00295   setState(Idle);
00296   emit stateChanged(Idle);
00297   emit gotError(error());
00298   return;
00299 }
00300 
00301 void KStreamSocket::timeoutSlot()
00302 {
00303   if (state() != Connecting)
00304     return;
00305 
00306   // halt the connections
00307   socketDevice()->close();  // this also kills the notifiers
00308 
00309   setError(IO_TimeOutError, Timeout);
00310   setState(HostFound);
00311   emit stateChanged(HostFound);
00312   emit gotError(Timeout);
00313   emit timedOut();
00314 }
00315 
00316 bool KStreamSocket::bindLocallyFor(const KResolverEntry& peer)
00317 {
00318   const KResolverResults& local = localResults();
00319 
00320   if (local.isEmpty())
00321     // user doesn't want to bind to any specific local address
00322     return true;
00323 
00324   bool foundone = false;
00325   // scan the local resolution for a matching family
00326   for (d->local = local.begin(); d->local != local.end(); ++d->local)
00327     if ((*d->local).family() == peer.family())
00328       {
00329     // found a suitable address!
00330     foundone = true;
00331 
00332     if (socketDevice()->bind(*d->local))
00333       return true;
00334       }
00335   
00336   if (!foundone)
00337     {
00338       // found nothing
00339       setError(IO_BindError, NotSupported);
00340       emit gotError(NotSupported);
00341     }
00342   else
00343     copyError();
00344   return false;
00345 }
00346 
00347 void KStreamSocket::connectionSucceeded(const KResolverEntry& peer)
00348 {
00349   QObject::disconnect(socketDevice()->readNotifier(), 0, this, SLOT(connectionEvent()));
00350   QObject::disconnect(socketDevice()->writeNotifier(), 0, this, SLOT(connectionEvent()));
00351 
00352   resetError();
00353   setFlags(IO_Sequential | IO_Raw | IO_ReadWrite | IO_Open | IO_Async);
00354   setState(Connected);
00355   socketDevice()->setSocketOptions(socketOptions());
00356   d->timer.stop();
00357   emit stateChanged(Connected);
00358   
00359   if (!localResults().isEmpty())
00360     emit bound(*d->local);
00361   emit connected(peer);
00362 }
00363 
00364 #include "kstreamsocket.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys