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