kdecore Library API Documentation

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