KDELibs4Support

k3streamsocket.cpp
1 /* -*- C++ -*-
2  * Copyright (C) 2003 Thiago Macieira <[email protected]>
3  *
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include "k3streamsocket.h"
26 
27 #include <config-network.h>
28 
29 #include <QSocketNotifier>
30 #include <QDateTime>
31 #include <QTimer>
32 #include <QElapsedTimer>
33 #include <QPointer>
34 
35 #include "k3socketaddress.h"
36 #include "k3resolver.h"
37 #include "k3socketdevice.h"
38 
39 using namespace KNetwork;
40 
41 class KNetwork::KStreamSocketPrivate
42 {
43 public:
45  QElapsedTimer startTime;
46  QTimer timer;
47 
48  int timeout;
49 
50  inline KStreamSocketPrivate()
51  : timeout(0)
52  { }
53 };
54 
55 KStreamSocket::KStreamSocket(const QString &node, const QString &service,
56  QObject *parent)
57  : KClientSocketBase(parent), d(new KStreamSocketPrivate)
58 {
59  peerResolver().setNodeName(node);
60  peerResolver().setServiceName(service);
61  peerResolver().setFamily(KResolver::KnownFamily);
62  localResolver().setFamily(KResolver::KnownFamily);
63 
64  setSocketOptions(socketOptions() & ~Blocking);
65 
66  QObject::connect(&d->timer, SIGNAL(timeout()), this, SLOT(timeoutSlot()));
67 }
68 
70 {
71  delete d;
72  // KClientSocketBase's destructor closes the socket
73 }
74 
76 {
77  return d->timeout;
78 }
79 
81 {
82  if (state() != Connecting) {
83  return timeout();
84  }
85  if (timeout() <= 0) {
86  return 0;
87  }
88 
89  return timeout() - d->startTime.elapsed();
90 }
91 
93 {
94  d->timeout = msecs;
95 
96  if (state() == Connecting) {
97  d->timer.start(msecs);
98  }
99 }
100 
101 bool KStreamSocket::bind(const QString &node, const QString &service)
102 {
103  if (state() != Idle) {
104  return false;
105  }
106 
107  if (!node.isNull()) {
108  localResolver().setNodeName(node);
109  }
110  if (!service.isNull()) {
111  localResolver().setServiceName(service);
112  }
113  return true;
114 }
115 
117 {
118  return KClientSocketBase::bind(entry);
119 }
120 
121 bool KStreamSocket::connect(const QString &node, const QString &service,
122  OpenMode mode)
123 {
124  Q_UNUSED(mode);
125  if (state() == Connected) {
126  return true; // already connected
127  }
128 
129  if (state() > Connected) {
130  return false; // can't do much here
131  }
132 
133  if (!node.isNull()) {
134  peerResolver().setNodeName(node);
135  }
136  if (!service.isNull()) {
137  peerResolver().setServiceName(service);
138  }
139 
140  if (state() == Connecting && !blocking()) {
141  setError(InProgress);
142  emit gotError(InProgress);
143  return true; // we're already connecting
144  }
145 
146  if (state() < HostFound) {
147  // connection hasn't started yet
148  if (!blocking()) {
149  QObject::connect(this, SIGNAL(hostFound()), SLOT(hostFoundSlot()));
150  return lookup();
151  }
152 
153  // blocking mode
154  if (!lookup()) {
155  return false; // lookup failure
156  }
157  }
158 
159  /*
160  * lookup results are available here
161  */
162 
163  if (timeout() > 0) {
164  if (!blocking() && !d->timer.isActive()) {
165  d->timer.setSingleShot(true);
166  d->timer.start(timeout());
167  } else {
168  // blocking connection with timeout
169  // this must be handled as a special case because it requires a
170  // non-blocking socket
171 
172  d->timer.stop(); // no need for a timer here
173 
174  socketDevice()->setBlocking(false);
175  while (true) {
176  connectionEvent();
177  if (state() < Connecting) {
178  return false; // error connecting
179  }
180  if (state() == Connected) {
181  return true; // connected!
182  }
183 
184  if (remainingTimeout() <= 0) {
185  // we've timed out
186  timeoutSlot();
187  return false;
188  }
189 
190  if (socketDevice()->error() == InProgress) {
191  bool timedout;
192  socketDevice()->poll(remainingTimeout(), &timedout);
193  if (timedout) {
194  timeoutSlot();
195  return false;
196  }
197  }
198  }
199  }
200  }
201 
202  connectionEvent();
203  return error() == NoError;
204 }
205 
207 {
208  return KClientSocketBase::connect(entry, mode);
209 }
210 
211 void KStreamSocket::hostFoundSlot()
212 {
213  QObject::disconnect(this, SLOT(hostFoundSlot()));
214  if (timeout() > 0) {
215  d->timer.setSingleShot(true);
216  d->timer.start(timeout());
217  }
218  QTimer::singleShot(0, this, SLOT(connectionEvent()));
219 }
220 
221 void KStreamSocket::connectionEvent()
222 {
223  if (state() != HostFound && state() != Connecting) {
224  return; // nothing to do
225  }
226 
227  const KResolverResults &peer = peerResults();
228  if (state() == HostFound) {
229  d->startTime.start();
230 
231  setState(Connecting);
232  emit stateChanged(Connecting);
233  d->peer = peer.begin();
234  d->local = localResults().begin(); // just to be on the safe side
235  }
236 
237  while (d->peer != peer.end()) {
238  const KResolverEntry &r = *d->peer;
239 
240  if (socketDevice()->socket() != -1) {
241  // we have an existing file descriptor
242  // this means that we've got activity in it (connection result)
243  if (socketDevice()->connect(r) && socketDevice()->error() == NoError) {
244  // yes, it did connect!
245  connectionSucceeded(r);
246  return;
247  } else if (socketDevice()->error() == InProgress)
248  // nope, still in progress
249  {
250  return;
251  }
252 
253  // no, the socket failed to connect
254  copyError();
255  socketDevice()->close();
256  ++d->peer;
257  continue;
258  }
259 
260  // try to bind
261  if (!bindLocallyFor(r)) {
262  // could not find a matching family
263  ++d->peer;
264  continue;
265  }
266 
267  {
268  bool skip = false;
269  emit aboutToConnect(r, skip);
270  if (skip) {
271  ++d->peer;
272  continue;
273  }
274  }
275 
276  if (socketDevice()->connect(r) || socketDevice()->error() == InProgress) {
277  // socket is attempting to connect
278  if (socketDevice()->error() == InProgress) {
280  QObject::connect(n, SIGNAL(activated(int)),
281  this, SLOT(connectionEvent()));
282  n->setEnabled(true);
283 
284  n = socketDevice()->writeNotifier();
285  QObject::connect(n, SIGNAL(activated(int)),
286  this, SLOT(connectionEvent()));
287  n->setEnabled(true);
288 
289  return; // wait for activity
290  }
291 
292  // socket has connected
293  connectionSucceeded(r);
294  return;
295  }
296 
297  // connection failed
298  // try next
299  copyError();
300  socketDevice()->close();
301  ++d->peer;
302  }
303 
304  // that was the last item
306  setState(Idle);
307  emit stateChanged(Idle);
308  emit gotError(error());
309  return;
310 }
311 
312 void KStreamSocket::timeoutSlot()
313 {
314  if (state() != Connecting) {
315  return;
316  }
317 
318  // halt the connections
319  socketDevice()->close(); // this also kills the notifiers
320 
321  setError(Timeout);
322  setState(HostFound);
323  emit stateChanged(HostFound);
324 
325  QPointer<KStreamSocket> that = this;
326  emit gotError(Timeout);
327 
328  if (!that.isNull()) {
329  emit timedOut();
330  }
331 }
332 
333 bool KStreamSocket::bindLocallyFor(const KResolverEntry &peer)
334 {
335  const KResolverResults &local = localResults();
336 
337  if (local.isEmpty())
338  // user doesn't want to bind to any specific local address
339  {
340  return true;
341  }
342 
343  bool foundone = false;
344  // scan the local resolution for a matching family
345  for (d->local = local.begin(); d->local != local.end(); ++d->local)
346  if ((*d->local).family() == peer.family()) {
347  // found a suitable address!
348  foundone = true;
349 
350  if (socketDevice()->bind(*d->local)) {
351  return true;
352  }
353  }
354 
355  if (!foundone) {
356  // found nothing
357  setError(NotSupported);
358  emit gotError(NotSupported);
359  } else {
360  copyError();
361  }
362  return false;
363 }
364 
365 void KStreamSocket::connectionSucceeded(const KResolverEntry &peer)
366 {
367  QObject::disconnect(socketDevice()->readNotifier(), nullptr, this, SLOT(connectionEvent()));
368  QObject::disconnect(socketDevice()->writeNotifier(), nullptr, this, SLOT(connectionEvent()));
369 
370  resetError();
372  setState(Connected);
374  d->timer.stop();
375  emit stateChanged(Connected);
376 
377  if (!localResults().isEmpty()) {
378  emit bound(*d->local);
379  }
380  emit connected(peer);
381 }
382 
bool bind(const KResolverEntry &address) override
Binds this socket to the given address.
virtual bool connect(const QString &node=QString(), const QString &service=QString(), OpenMode mode=ReadWrite)=0
Attempts to connect to a given hostname and service, or use the default ones if none are given...
bool blocking() const
Retrieves this socket&#39;s blocking mode.
KSocketDevice * socketDevice() const
Retrieves the socket implementation used on this socket.
SocketState state() const
Returns the current state for this socket.
int remainingTimeout() const
Retrieves the remaining timeout time (in milliseconds).
int timeout() const
Retrieves the timeout value (in milliseconds).
virtual bool bind(const QString &node=QString(), const QString &service=QString()) override
Binds this socket to the given nodename and service, or use the default ones if none are given...
virtual bool connect(const QString &node=QString(), const QString &service=QString(), OpenMode mode=ReadWrite) override
Reimplemented from KClientSocketBase.
One resolution entry.
Definition: k3resolver.h:72
void resetError()
Resets the socket error code and the I/O Device&#39;s status.
QSocketNotifier * writeNotifier() const
Returns a socket notifier for output on this socket.
void setFamily(int families)
Sets the allowed socket families.
Definition: k3resolver.cpp:381
virtual bool bind(const QString &node=QString(), const QString &service=QString())=0
Binds this socket to the given nodename and service, or use the default ones if none are given...
virtual bool lookup()
Starts the lookup for peer and local hostnames as well as their services.
virtual bool connect(const KResolverEntry &address, OpenMode mode=ReadWrite) override
Connect to a remote host.
typedef OpenMode
KResolver & localResolver() const
Returns the internal KResolver object used for looking up the local host name and service...
SocketError error() const
Retrieves the socket error code.
int socket() const
Returns the file descriptor for this socket.
void gotError(int code)
This signal is emitted when this object finds an error.
void hostFound()
This signal is emitted when the lookup is successfully completed.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
Name and service resolution results.
Definition: k3resolver.h:212
void setServiceName(const QString &service)
Sets the service name to be resolved.
Definition: k3resolver.cpp:346
bool isNull() const const
A namespace to store all networking-related (socket) classes.
QSocketNotifier * readNotifier() const
Returns a socket notifier for input on this socket.
void timedOut()
This signal is emitted when a connection timeout occurs.
qint64 skip(qint64 maxSize)
void copyError()
Convenience function to set this object&#39;s error code to match that of the socket device.
void close() override
Closes the socket.
bool isNull() const const
bool isEmpty() const const
const KResolverResults & peerResults() const
Returns the internal list of resolved results for the peer address.
bool setSocketOptions(int opts) override
This implementation sets the options on the socket.
void aboutToConnect(const KNetwork::KResolverEntry &remote, bool &skip)
This signal is emitted when the socket is about to connect to an address (but before doing so)...
virtual ~KStreamSocket()
Destructor.
void setState(SocketState state)
Sets the socket state to state.
void stateChanged(int newstate)
This signal is emitted whenever the socket state changes.
void setTimeout(int msecs)
Sets the timeout value.
void setError(SocketError error)
Sets the socket&#39;s error code.
QList::iterator end()
void bound(const KNetwork::KResolverEntry &local)
This signal is emitted when the socket successfully binds to an address.
void setNodeName(const QString &nodename)
Sets the nodename for the resolution.
Definition: k3resolver.cpp:335
void setEnabled(bool enable)
int family() const
Retrieves the family associated with this socket address.
Definition: k3resolver.cpp:145
Abstract client socket class.
bool setSocketOptions(int opts) override
Sets the socket options.
virtual bool setBlocking(bool enable)
Sets this socket&#39;s blocking mode.
const KResolverResults & localResults() const
Returns the internal list of resolved results for the local address.
KStreamSocket(const QString &node=QString(), const QString &service=QString(), QObject *parent=nullptr)
Default constructor.
virtual int socketOptions() const
Retrieves the socket options that have been set.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
KResolver & peerResolver() const
Returns the internal KResolver object used for looking up the peer host name and service.
void connected(const KNetwork::KResolverEntry &remote)
This socket is emitted when the socket successfully connects to a remote address. ...
QList::iterator begin()
virtual bool poll(bool *input, bool *output, bool *exception=nullptr, int timeout=-1, bool *timedout=nullptr)
Executes a poll in the socket, via select(2) or poll(2).
bool open(OpenMode mode) override
Reimplemented from QIODevice.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sat Jul 4 2020 22:58:56 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.