ArnLib  4.0.x
Active Registry Network
ArnDiscoverConnect.cpp
Go to the documentation of this file.
1 // Copyright (C) 2010-2022 Michael Wiklund.
2 // All rights reserved.
3 // Contact: arnlib@wiklunden.se
4 //
5 // This file is part of the ArnLib - Active Registry Network.
6 // Parts of ArnLib depend on Qt and/or other libraries that have their own
7 // licenses. Usage of these other libraries is subject to their respective
8 // license agreements.
9 //
10 // GNU Lesser General Public License Usage
11 // This file may be used under the terms of the GNU Lesser General Public
12 // License version 2.1 as published by the Free Software Foundation and
13 // appearing in the file LICENSE_LGPL.txt included in the packaging of this
14 // file. In addition, as a special exception, you may use the rights described
15 // in the Nokia Qt LGPL Exception version 1.1, included in the file
16 // LGPL_EXCEPTION.txt in this package.
17 //
18 // GNU General Public License Usage
19 // Alternatively, this file may be used under the terms of the GNU General Public
20 // License version 3.0 as published by the Free Software Foundation and appearing
21 // in the file LICENSE_GPL.txt included in the packaging of this file.
22 //
23 // Other Usage
24 // Alternatively, this file may be used in accordance with the terms and conditions
25 // contained in a signed written agreement between you and Michael Wiklund.
26 //
27 // This program is distributed in the hope that it will be useful, but WITHOUT ANY
28 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
29 // PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
30 //
31 
33 #include "private/ArnDiscoverConnect_p.hpp"
34 #include "ArnInc/ArnZeroConf.hpp"
35 #include "ArnInc/ArnClient.hpp"
36 #include "ArnInc/ArnLib.hpp"
37 #include <QElapsedTimer>
38 #include <QTime>
39 #include <QMetaObject>
40 
41 
43 
44 ArnDiscoverConnectorPrivate::ArnDiscoverConnectorPrivate()
45 {
46  _discoverHostPrio = 1;
47  _directHostPrio = 2;
48  _resolveRefreshTimeout = 30;
49  _directHosts = new QObject;
50  _directHosts->setObjectName("dirHosts");
51  _hasBeenSetupClient = false;
52  _resolveRefreshBlocked = false;
53  _isResolved = false;
54  _externalClientConnect = false;
55  _resolveRefreshTime = arnNullptr;
56  _resolver = arnNullptr;
57  _arnDisHostService = arnNullptr;
58  _arnDisHostServicePv = arnNullptr;
59  _arnDisHostAddress = arnNullptr;
60  _arnDisHostPort = arnNullptr;
61 }
62 
63 
64 ArnDiscoverConnectorPrivate::~ArnDiscoverConnectorPrivate()
65 {
66  delete _directHosts;
67 }
68 
69 
71  : QObject( &client)
72  , d_ptr( new ArnDiscoverConnectorPrivate)
73 {
75 
76  d->_client = &client;
77  d->_id = id;
78 }
79 
80 
81 ArnDiscoverConnector::ArnDiscoverConnector( ArnDiscoverConnectorPrivate& dd, QObject* parent,
82  ArnClient& client, const QString& id)
83  : QObject( parent)
84  , d_ptr( &dd)
85 {
87 
88  d->_client = &client;
89  d->_id = id;
90 }
91 
92 
94 {
95  delete d_ptr;
96 }
97 
98 
100 {
102 
103  d->_client->clearArnList( d->_directHostPrio);
104 }
105 
106 
107 void ArnDiscoverConnector::addToDirectHosts( const QString& arnHost, quint16 port)
108 {
110 
111  d->_client->addToArnList( arnHost, port, d->_directHostPrio);
112 }
113 
114 
116 {
118 
119  if (d->_resolver) {
120  delete d->_resolver;
121  d->_resolver = arnNullptr;
122  }
123  if (!resolver) return; // No use of resolver
124 
125  if (!d->_resolveRefreshTime) // first time
126  d->_resolveRefreshTime = new QElapsedTimer;
127 
128  d->_resolver = resolver;
129  d->_resolver->setParent( this);
130  d->_resolver->setDefaultStopState( ArnDiscoverInfo::State::HostIp); // Need IP ...
131 
132  if (d->_hasBeenSetupClient)
133  postSetupResolver();
134 }
135 
136 
138 {
139  Q_D(const ArnDiscoverConnector);
140 
141  return d->_id;
142 }
143 
144 
146 {
147  Q_D(const ArnDiscoverConnector);
148 
149  return d->_resolveRefreshTimeout;
150 }
151 
152 
153 void ArnDiscoverConnector::setResolveRefreshTimeout( int resolveRefreshTimeout)
154 {
156 
157  d->_resolveRefreshTimeout = resolveRefreshTimeout;
158 }
159 
160 
162 {
163  Q_D(const ArnDiscoverConnector);
164 
165  return d->_discoverHostPrio;
166 }
167 
168 
169 void ArnDiscoverConnector::setDiscoverHostPrio( int discoverHostPrio)
170 {
172 
173  d->_discoverHostPrio = discoverHostPrio;
174 }
175 
176 
178 {
179  Q_D(const ArnDiscoverConnector);
180 
181  return d->_directHostPrio;
182 }
183 
184 
186 {
188 
189  d->_directHostPrio = directHostPrio;
190 }
191 
192 
194 {
195  Q_D(const ArnDiscoverConnector);
196 
197  return d->_externalClientConnect;
198 }
199 
200 
201 void ArnDiscoverConnector::setExternalClientConnect( bool externalClientConnect)
202 {
204 
205  d->_externalClientConnect = externalClientConnect;
206 }
207 
208 
210 {
211  Q_D(const ArnDiscoverConnector);
212 
213  return d->_service;
214 }
215 
216 
217 void ArnDiscoverConnector::setService( const QString& service)
218 {
220 
221  d->_service = service;
222 
223  if (d->_resolver && d->_arnDisHostService)
224  *d->_arnDisHostService = service; // Request service change
225 }
226 
227 
229 {
231 
232  connect( d->_client, SIGNAL(tcpConnected(QString,quint16)),
233  this, SLOT(doClientConnectChange(QString,quint16)));
234 
235  QMetaObject::invokeMethod( this,
236  "postSetupClient",
237  Qt::QueuedConnection);
238 }
239 
240 
241 void ArnDiscoverConnector::doClientConnectChanged( int stat, int curPrio)
242 {
244 
245  ArnClient::ConnectStat cs = ArnClient::ConnectStat::fromInt( stat);
246 
247  if (Arn::debugDiscover) qDebug() << "Discover ClientConnectChanged 1: stat=" << stat
248  << " prio=" << curPrio;
249  if (!d->_resolver || (cs == cs.Connecting)) return;
250  if ((cs == cs.Connected) || (cs == cs.Stopped) || (cs == cs.Negotiating)) {
251  d->_resolveRefreshBlocked = false;
252  return;
253  }
254  if (d->_isResolved || (cs != cs.TriedAll)) { // Resolv ok or still more to try, consider outdated resolv
255  if (curPrio != d->_discoverHostPrio) return; // Not for resolved host
256  if ((cs != cs.Error) && (cs != cs.Disconnected)) return; // Skip any non error
257  if (Arn::debugDiscover) qDebug() << "Discover ClientConnectChanged 2:";
258 
259  if (d->_resolveRefreshTime->elapsed() >= d->_resolveRefreshTimeout * 1000)
260  d->_resolveRefreshBlocked = false;
261  if (d->_resolveRefreshBlocked) return;
262  }
263 
264  d->_resolveRefreshBlocked = true; // Block for further refresh within lockout time
265  d->_resolveRefreshTime->start();
266 
267  if (Arn::debugDiscover) qDebug() << "Discover ClientConnectChanged 3 resolve: service=" << d->_arnDisHostService->toString();
268  bool forceUpdate = d->_isResolved;
269  d->_resolver->resolve( d->_arnDisHostService->toString(), forceUpdate); // Do a resolve refresh / retry
270 }
271 
272 
273 void ArnDiscoverConnector::postSetupClient()
274 {
276 
277  if (d->_resolver)
278  postSetupResolver();
279 
280  QString path;
281  QString connectIdPath = Arn::pathDiscoverConnect + d->_id + "/";
282 
283  path = connectIdPath + "Status/";
284  ArnItem* arnConnectStatus = new ArnItem( path + "value", this);
285  *arnConnectStatus = d->_client->connectStatus();
286  connect( d->_client, SIGNAL(connectionStatusChanged(int,int)), arnConnectStatus, SLOT(setValue(int)));
287  ArnM::setValue( path + "set", ArnClient::ConnectStat::txt().getEnumSet( ArnClient::ConnectStat::NsHuman));
288 
289  path = connectIdPath + "Request/";
290  ArnItem* arnConnectReqPV = new ArnItem( path + "value!", this);
291  *arnConnectReqPV = "0";
292  connect( arnConnectReqPV, SIGNAL(changed(int)), this, SLOT(doClientConnectRequest(int)));
293  ArnM::setValue( path + "set", QString("0=Idle 1=Start_connect"));
294 
295  ArnClient::HostList arnHosts = d->_client->arnList( d->_directHostPrio);
296  int i = 0;
297  foreach (ArnClient::HostAddrPort host, arnHosts) {
298  path = connectIdPath + "DirectHosts/Host-" + QString::number(i) + "/";
299  ArnItem* hostAddr = new ArnItem( path + "value", d->_directHosts);
300  ArnItem* hostPort = new ArnItem( path + "Port/value", d->_directHosts);
301  *hostAddr = host.addr; // Default addr
302  *hostPort = host.port; // Default port
303  hostAddr->addMode( Arn::ObjectMode::Save); // Save mode after default set, will not save default value
304  hostPort->addMode( Arn::ObjectMode::Save);
305  connect( hostAddr, SIGNAL(changed()), this, SLOT(doClientDirHostChanged()));
306  connect( hostPort, SIGNAL(changed()), this, SLOT(doClientDirHostChanged()));
307  ++i;
308  }
309  doClientDirHostChanged(); // Any loaded persistent values will be used
310 
311  connect( d->_client, SIGNAL(connectionStatusChanged(int,int)), this, SLOT(doClientConnectChanged(int,int)));
312  d->_hasBeenSetupClient = true;
313 
314  if (!d->_resolver)
315  doClientReadyToConnect( d->_client, d->_id);
316 }
317 
318 
319 void ArnDiscoverConnector::doClientConnectChange( const QString& arnHost, quint16 port)
320 {
322 
323  QString path = Arn::pathDiscoverConnect + d->_id + "/UsingHost/";
324  ArnM::setValue( path + "value", arnHost);
325  ArnM::setValue( path + "Port/value", port ? QString::number( port) : QString());
326 }
327 
328 
329 void ArnDiscoverConnector::doClientDirHostChanged()
330 {
332 
333  QObjectList dirHostOList = d->_directHosts->children();
334  int dirHostOListSize = dirHostOList.size();
335  Q_ASSERT((dirHostOListSize & 1) == 0);
336 
338  d->_client->clearArnList( d->_directHostPrio);
339  for (int i = 0; i < dirHostOListSize / 2; ++i) {
340  ArnItem* hostAddr = qobject_cast<ArnItem*>( dirHostOList.at( 2 * i + 0));
341  Q_ASSERT(hostAddr);
342  ArnItem* hostPort = qobject_cast<ArnItem*>( dirHostOList.at( 2 * i + 1));
343  Q_ASSERT(hostPort);
344  d->_client->addToArnList( hostAddr->toString(), quint16( hostPort->toInt()), d->_directHostPrio);
345  }
346 }
347 
348 
349 void ArnDiscoverConnector::doClientConnectRequest(int reqCode)
350 {
352 
353  if (reqCode) {
354  doClientConnectChange("", 0);
355  d->_client->connectToArnList();
356  }
357 }
358 
359 
360 void ArnDiscoverConnector::postSetupResolver()
361 {
363 
364  QString path;
365  QString connectIdPath = Arn::pathDiscoverConnect + d->_id + "/";
366 
367  path = connectIdPath + "DiscoverHost/";
368  d->_arnDisHostServicePv = new ArnItem( path + "Service/value!", d->_resolver);
369  d->_arnDisHostService = new ArnItem( path + "Service/value", d->_resolver);
370  d->_arnDisHostAddress = new ArnItem( path + "Host/value", d->_resolver);
371  d->_arnDisHostPort = new ArnItem( path + "Host/Port/value", d->_resolver);
372  d->_arnDisHostStatus = new ArnItem( path + "Status/value", d->_resolver);
373  typedef ArnZeroConf::Error Err;
374  ArnM::setValue( path + "Status/set",
375  QString("%1=Resolved %2=Resolving %3=Bad_request_sequence %4=Resolv_timeout "
376  "%5=Unicast_DNS_Fail")
377  .arg(Err::Ok).arg(Err::Running).arg(Err::BadReqSeq).arg(Err::Timeout)
378  .arg(Err::UDnsFail));
379 
380  *d->_arnDisHostServicePv = d->_resolver->defaultService(); // Use this default if no active persistent service
381  d->_arnDisHostService->addMode( Arn::ObjectMode::Save); // Save mode after default set, will not save default value
382  if (d->_service.isEmpty()) // Non empty _service is always used
383  d->_service = d->_arnDisHostService->toString(); // Otherwise persistent/deafult value will be used as request
384  *d->_arnDisHostService = d->_service;
385 
386  connect( d->_arnDisHostServicePv, SIGNAL(changed()), this, SLOT(doClientServicetChanged()));
387  connect( d->_resolver, SIGNAL(infoUpdated(int,ArnDiscoverInfo::State)),
388  this, SLOT(doClientResolvChanged(int,ArnDiscoverInfo::State)));
389 
390  doClientServicetChanged(); // Perform request
391 }
392 
393 
394 void ArnDiscoverConnector::doClientServicetChanged()
395 {
397 
398  Q_ASSERT(d->_arnDisHostServicePv);
399  QString serviceName = d->_arnDisHostServicePv->toString();
400  if (serviceName.isEmpty())
401  serviceName = d->_resolver->defaultService();
402  d->_service = serviceName;
403  *d->_arnDisHostServicePv = serviceName; // Current service must be set before resolving
404 
405  d->_resolver->resolve( serviceName, true); // Force new resolve
406 }
407 
408 
409 void ArnDiscoverConnector::doClientResolvChanged( int index, ArnDiscoverInfo::State state)
410 {
412 
413  Q_ASSERT(d->_arnDisHostService);
414  Q_ASSERT(d->_arnDisHostAddress);
415  Q_ASSERT(d->_arnDisHostPort);
416 
417  const ArnDiscoverInfo& info = d->_resolver->infoByIndex( index);
418  if (info.serviceName() != d->_arnDisHostService->toString()) return; // Not the current service
419 
420  *d->_arnDisHostAddress = info.hostWithInfo();
421  *d->_arnDisHostPort = info.hostPortString();
422  *d->_arnDisHostStatus = info.resolvCode();
423 
424  if (state <= state.ServiceName) { // New resolv has started
425  if (Arn::debugDiscover) qDebug() << "ArnDiscoverConnector New resolv started: service=" << info.serviceName();
426  d->_isResolved = false;
427  d->_client->clearArnList( d->_discoverHostPrio);
428  }
429  else if (state == state.HostIp) {
430  d->_isResolved = true;
431  d->_client->clearArnList( d->_discoverHostPrio);
432  d->_client->addToArnList( info.hostWithInfo(), info.hostPort(), d->_discoverHostPrio);
433  if (d->_client->connectStatus() == ArnClient::ConnectStat::Init) {
434  doClientReadyToConnect( d->_client, d->_id);
435  }
436  }
437  else if (info.isError()) {
438  d->_client->clearArnList( d->_discoverHostPrio);
439  if (d->_client->connectStatus() == ArnClient::ConnectStat::Init) {
440  doClientReadyToConnect( d->_client, d->_id);
441  }
442  }
443 }
444 
445 
446 void ArnDiscoverConnector::doClientReadyToConnect( ArnClient* arnClient, const QString& id)
447 {
449 
450  if (d->_externalClientConnect)
451  emit clientReadyToConnect( arnClient, id);
452  else {
453  if (Arn::debugDiscover) qDebug() << "Discover Connecting client: id=" << id;
454  arnClient->connectToArnList();
455  }
456 }
void clearDirectHosts()
Clear the direct host connection list.
int directHostPrio() const
Return the priority for direct hosts
No data flow within set timeout (still connected)
Definition: ArnClient.hpp:63
int discoverHostPrio() const
Return the priority for discovered hosts
Unsuccessfull when trying to connect to an Arn host.
Definition: ArnClient.hpp:65
static void setValue(const QString &path, int value)
Assign an integer to an Arn Data Object at path
Definition: ArnM.cpp:473
QString id() const
Return the identifier for this connector.
Successfully connected to an Arn host.
Definition: ArnClient.hpp:61
bool debugDiscover
Definition: ArnLib.cpp:48
void setResolver(ArnDiscoverResolver *resolver)
Set the ArnDiscoverResolver to be used.
State of Arn discover browse data. Can be tested by relative order.
Definition: ArnDiscover.hpp:79
void clientReadyToConnect(ArnClient *arnClient, const QString &id)
Signal for external client connection.
QList< HostAddrPort > HostList
Definition: ArnClient.hpp:121
Unsuccessfully tried to connect to all hosts in the Arn connection List.
Definition: ArnClient.hpp:69
int resolvCode() const
Return the latest resolv error code for this service.
void setService(const QString &service)
Set the service name for the connection.
TCP connection is broken (has been successfull)
Definition: ArnClient.hpp:67
Got service name and domain (from browsing)
Definition: ArnDiscover.hpp:84
void addMode(Arn::ObjectMode mode)
Add general mode settings for this Arn Data Object
Definition: ArnItem.hpp:159
Negotiating terms and compatibility with an Arn host.
Definition: ArnClient.hpp:59
void addToDirectHosts(const QString &arnHost, quint16 port=0)
Add an Arn Server to the direct host connection list.
const QString pathDiscoverConnect
Definition: Arn.cpp:49
QString service() const
Returns the service name for this connection.
bool externalClientConnect() const
Return the external client connect mode.
void connectToArnList()
Connect to an Arn Server in the Arn connection list.
Definition: ArnClient.cpp:301
Class for connecting to an Arn Server.
Definition: ArnClient.hpp:104
QString hostWithInfo() const
Get the the HostWithInfo string.
void setDirectHostPrio(int directHostPrio)
Set the priority for direct hosts
An automatic client discover connector.
Trying to connect to an Arn host.
Definition: ArnClient.hpp:57
QString hostPortString() const
Return the printable host port for this service.
QString toString(bool *isOk=arnNullptr) const
Definition: ArnItem.hpp:380
QString serviceName() const
Return the service name for this service.
void setDiscoverHostPrio(int discoverHostPrio)
Set the priority for discovered hosts
void setResolveRefreshTimeout(int resolveRefreshTimeout)
Set the resolv refresh period.
void setDefaultStopState(ArnDiscoverInfo::State defaultStopState)
Set the default stop state for this service discover browser.
void start()
Start connector.
int resolveRefreshTimeout() const
Return the resolv refresh period.
void setExternalClientConnect(bool externalClientConnect)
Set the external client connect mode.
Initialized, not yet any result of trying to connect ...
Definition: ArnClient.hpp:55
Also got HostIp (from DNS lookup)
Definition: ArnDiscover.hpp:92
bool isError() const
Is in an error state for this service.
Class for holding current discover info of one service.
Definition: ArnDiscover.hpp:72
Resolv an Arn service.
ArnDiscoverConnector(ArnClient &client, const QString &id)
quint16 hostPort() const
Return the port for this service.
int toInt(bool *isOk=arnNullptr) const
Definition: ArnItem.hpp:352
Data is persistent and will be saved.
Definition: Arn.hpp:130
Errors of ZeroConfig, other values are defined in dns_sd.h.
Definition: ArnZeroConf.hpp:53
Handle for an Arn Data Object.
Definition: ArnItem.hpp:72