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 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 (remainingTimeout() <= 0) 00164 { 00165 // we've timed out 00166 timeoutSlot(); 00167 return false; 00168 } 00169 00170 if (socketDevice()->error() == InProgress) 00171 { 00172 bool timedout; 00173 socketDevice()->poll(remainingTimeout(), &timedout); 00174 if (timedout) 00175 { 00176 timeoutSlot(); 00177 return false; 00178 } 00179 } 00180 } 00181 } 00182 } 00183 00184 connectionEvent(); 00185 return error() == NoError; 00186 } 00187 00188 bool KStreamSocket::connect(const KResolverEntry& entry) 00189 { 00190 return KClientSocketBase::connect(entry); 00191 } 00192 00193 void KStreamSocket::hostFoundSlot() 00194 { 00195 QObject::disconnect(this, SLOT(hostFoundSlot())); 00196 if (timeout() > 0) 00197 d->timer.start(timeout(), true); 00198 QTimer::singleShot(0, this, SLOT(connectionEvent())); 00199 } 00200 00201 void KStreamSocket::connectionEvent() 00202 { 00203 if (state() != HostFound && state() != Connecting) 00204 return; // nothing to do 00205 00206 const KResolverResults& peer = peerResults(); 00207 if (state() == HostFound) 00208 { 00209 d->startTime.start(); 00210 00211 setState(Connecting); 00212 emit stateChanged(Connecting); 00213 d->peer = peer.begin(); 00214 d->local = localResults().begin(); // just to be on the safe side 00215 } 00216 00217 while (d->peer != peer.end()) 00218 { 00219 const KResolverEntry &r = *d->peer; 00220 00221 if (socketDevice()->socket() != -1) 00222 { 00223 // we have an existing file descriptor 00224 // this means that we've got activity in it (connection result) 00225 if (socketDevice()->connect(r) && socketDevice()->error() == NoError) 00226 { 00227 // yes, it did connect! 00228 connectionSucceeded(r); 00229 return; 00230 } 00231 else if (socketDevice()->error() == InProgress) 00232 // nope, still in progress 00233 return; 00234 00235 // no, the socket failed to connect 00236 copyError(); 00237 socketDevice()->close(); 00238 ++d->peer; 00239 continue; 00240 } 00241 00242 // try to bind 00243 if (!bindLocallyFor(r)) 00244 { 00245 // could not find a matching family 00246 ++d->peer; 00247 continue; 00248 } 00249 00250 { 00251 bool skip = false; 00252 emit aboutToConnect(r, skip); 00253 if (skip) 00254 { 00255 ++d->peer; 00256 continue; 00257 } 00258 } 00259 00260 if (socketDevice()->connect(r) || socketDevice()->error() == InProgress) 00261 { 00262 // socket is attempting to connect 00263 if (socketDevice()->error() == InProgress) 00264 { 00265 QSocketNotifier *n = socketDevice()->readNotifier(); 00266 QObject::connect(n, SIGNAL(activated(int)), 00267 this, SLOT(connectionEvent())); 00268 n->setEnabled(true); 00269 00270 n = socketDevice()->writeNotifier(); 00271 QObject::connect(n, SIGNAL(activated(int)), 00272 this, SLOT(connectionEvent())); 00273 n->setEnabled(true); 00274 00275 return; // wait for activity 00276 } 00277 00278 // socket has connected 00279 continue; // let the beginning of the loop handle success 00280 } 00281 00282 // connection failed 00283 // try next 00284 copyError(); 00285 socketDevice()->close(); 00286 ++d->peer; 00287 } 00288 00289 // that was the last item 00290 setState(Idle); 00291 emit stateChanged(Idle); 00292 emit gotError(error()); 00293 return; 00294 } 00295 00296 void KStreamSocket::timeoutSlot() 00297 { 00298 if (state() != Connecting) 00299 return; 00300 00301 // halt the connections 00302 socketDevice()->close(); // this also kills the notifiers 00303 00304 setError(IO_TimeOutError, Timeout); 00305 setState(HostFound); 00306 emit stateChanged(HostFound); 00307 emit gotError(Timeout); 00308 emit timedOut(); 00309 } 00310 00311 bool KStreamSocket::bindLocallyFor(const KResolverEntry& peer) 00312 { 00313 const KResolverResults& local = localResults(); 00314 00315 if (local.isEmpty()) 00316 // user doesn't want to bind to any specific local address 00317 return true; 00318 00319 bool foundone = false; 00320 // scan the local resolution for a matching family 00321 for (d->local = local.begin(); d->local != local.end(); ++d->local) 00322 if ((*d->local).family() == peer.family()) 00323 { 00324 // found a suitable address! 00325 foundone = true; 00326 00327 if (socketDevice()->bind(*d->local)) 00328 return true; 00329 } 00330 00331 if (!foundone) 00332 { 00333 // found nothing 00334 setError(IO_BindError, NotSupported); 00335 emit gotError(NotSupported); 00336 } 00337 else 00338 copyError(); 00339 return false; 00340 } 00341 00342 void KStreamSocket::connectionSucceeded(const KResolverEntry& peer) 00343 { 00344 QObject::disconnect(socketDevice()->readNotifier(), 0, this, SLOT(connectionEvent())); 00345 QObject::disconnect(socketDevice()->writeNotifier(), 0, this, SLOT(connectionEvent())); 00346 00347 resetError(); 00348 setFlags(IO_Sequential | IO_Raw | IO_ReadWrite | IO_Open | IO_Async); 00349 setState(Connected); 00350 socketDevice()->setSocketOptions(socketOptions()); 00351 d->timer.stop(); 00352 emit stateChanged(Connected); 00353 00354 if (!localResults().isEmpty()) 00355 emit bound(*d->local); 00356 emit connected(peer); 00357 } 00358 00359 #include "kstreamsocket.moc"
KDE Logo
This file is part of the documentation for kdecore Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Apr 12 22:47:41 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003