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 Wed Sep 29 09:40:09 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003