# kdecore Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# 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   setState(Idle);
    00295   emit stateChanged(Idle);
    00296   emit gotError(error());
    00297   return;
    00298 }
    00299
    00300 void KStreamSocket::timeoutSlot()
    00301 {
    00302   if (state() != Connecting)
    00303     return;
    00304
    00305   // halt the connections
    00306   socketDevice()->close();  // this also kills the notifiers
    00307
    00308   setError(IO_TimeOutError, Timeout);
    00309   setState(HostFound);
    00310   emit stateChanged(HostFound);
    00311   emit gotError(Timeout);
    00312   emit timedOut();
    00313 }
    00314
    00315 bool KStreamSocket::bindLocallyFor(const KResolverEntry& peer)
    00316 {
    00317   const KResolverResults& local = localResults();
    00318
    00319   if (local.isEmpty())
    00320     // user doesn't want to bind to any specific local address
    00321     return true;
    00322
    00323   bool foundone = false;
    00324   // scan the local resolution for a matching family
    00325   for (d->local = local.begin(); d->local != local.end(); ++d->local)
    00326     if ((*d->local).family() == peer.family())
    00327       {
    00328     // found a suitable address!
    00329     foundone = true;
    00330
    00331     if (socketDevice()->bind(*d->local))
    00332       return true;
    00333       }
    00334
    00335   if (!foundone)
    00336     {
    00337       // found nothing
    00338       setError(IO_BindError, NotSupported);
    00339       emit gotError(NotSupported);
    00340     }
    00341   else
    00342     copyError();
    00343   return false;
    00344 }
    00345
    00346 void KStreamSocket::connectionSucceeded(const KResolverEntry& peer)
    00347 {
    00348   QObject::disconnect(socketDevice()->readNotifier(), 0, this, SLOT(connectionEvent()));
    00349   QObject::disconnect(socketDevice()->writeNotifier(), 0, this, SLOT(connectionEvent()));
    00350
    00351   resetError();
    00352   setFlags(IO_Sequential | IO_Raw | IO_ReadWrite | IO_Open | IO_Async);
    00353   setState(Connected);
    00354   socketDevice()->setSocketOptions(socketOptions());
    00355   d->timer.stop();
    00356   emit stateChanged(Connected);
    00357
    00358   if (!localResults().isEmpty())
    00359     emit bound(*d->local);
    00360   emit connected(peer);
    00361 }
    00362
    00363 #include "kstreamsocket.moc"

This file is part of the documentation for kdecore Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:18:11 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003