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