MythTV  master
mythsocket.cpp
Go to the documentation of this file.
1 // Qt
2 #include <QNetworkInterface> // for QNetworkInterface::allAddresses ()
3 #include <QCoreApplication>
4 #include <QWaitCondition>
5 #include <QSharedPointer>
6 #include <QByteArray>
7 #include <QTcpSocket>
8 #include <QHostInfo>
9 #include <QThread>
10 #include <QMetaType>
11 
12 // setsockopt -- has to be after Qt includes for Q_OS_WIN definition
13 #if defined(Q_OS_WIN)
14 #include <winsock2.h>
15 #include <Ws2tcpip.h>
16 #include <cstdio>
17 #else
18 #include <sys/socket.h>
19 #endif
20 #include <unistd.h> // for usleep (and socket code on Q_OS_WIN)
21 #include <algorithm> // for min/max
22 using std::max;
23 #include <vector> // for vector
24 using std::vector;
25 
26 // MythTV
27 #include "mythsocket.h"
28 #include "mythtimer.h"
29 #include "mythevent.h"
30 #include "mythversion.h"
31 #include "mythlogging.h"
32 #include "mythcorecontext.h"
33 #include "portchecker.h"
34 
35 #define SLOC(a) QString("MythSocket(%1:%2): ") \
36  .arg((intptr_t)(a), 0, 16) \
37  .arg((a)->GetSocketDescriptor())
38 #define LOC SLOC(this)
39 
42 
43 const int MythSocket::kSocketReceiveBufferSize = 128 * 1024;
44 
46 QHash<QString, QHostAddress::SpecialAddress> MythSocket::s_loopbackCache;
47 
49 MThread *MythSocket::s_thread = nullptr;
51 
52 Q_DECLARE_METATYPE ( const QStringList * );
53 Q_DECLARE_METATYPE ( QStringList * );
54 Q_DECLARE_METATYPE ( const char * );
55 Q_DECLARE_METATYPE ( char * );
56 Q_DECLARE_METATYPE ( bool * );
57 Q_DECLARE_METATYPE ( int * );
58 Q_DECLARE_METATYPE ( QHostAddress );
59 static int x0 = qRegisterMetaType< const QStringList * >();
60 static int x1 = qRegisterMetaType< QStringList * >();
61 static int x2 = qRegisterMetaType< const char * >();
62 static int x3 = qRegisterMetaType< char * >();
63 static int x4 = qRegisterMetaType< bool * >();
64 static int x5 = qRegisterMetaType< int * >();
65 static int x6 = qRegisterMetaType< QHostAddress >();
67  x0 + x1 + x2 + x3 + x4 + x5 + x6;
68 
69 static QString to_sample(const QByteArray &payload)
70 {
71  QString sample("");
72  for (uint i = 0; (i<60) && (i<(uint)payload.length()); i++)
73  {
74  sample += QChar(payload[i]).isPrint() ?
75  QChar(payload[i]) : QChar('?');
76  }
77  sample += (payload.length() > 60) ? "..." : "";
78  return sample;
79 }
80 
82  qt_socket_fd_t socket, MythSocketCBs *cb, bool use_shared_thread) :
83  ReferenceCounter(QString("MythSocket(%1)").arg(socket)),
84  m_tcpSocket(new QTcpSocket()),
85  m_callback(cb),
86  m_useSharedThread(use_shared_thread)
87 {
88  LOG(VB_SOCKET, LOG_INFO, LOC + QString("MythSocket(%1, 0x%2) ctor")
89  .arg(socket).arg((intptr_t)(cb),0,16));
90 
91  if (socket != -1)
92  {
93  m_tcpSocket->setSocketDescriptor(
94  socket, QAbstractSocket::ConnectedState,
95  QAbstractSocket::ReadWrite);
97  {
98  m_tcpSocket->abort();
99  m_connected = false;
100  m_useSharedThread = false;
101  return;
102  }
103  ConnectHandler(); // already called implicitly above?
104  }
105 
106  // Use direct connections so m_tcpSocket can be used
107  // in the handlers safely since they will be running
108  // in the same thread as all other m_tcpSocket users.
109 
110  connect(m_tcpSocket, SIGNAL(connected()),
111  this, SLOT(ConnectHandler()),
112  Qt::DirectConnection);
113  connect(m_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
114  this, SLOT(ErrorHandler(QAbstractSocket::SocketError)),
115  Qt::DirectConnection);
116  connect(m_tcpSocket, SIGNAL(aboutToClose()),
117  this, SLOT(AboutToCloseHandler()));
118  connect(m_tcpSocket, SIGNAL(disconnected()),
119  this, SLOT(DisconnectHandler()),
120  Qt::DirectConnection);
121  connect(m_tcpSocket, SIGNAL(readyRead()),
122  this, SLOT(ReadyReadHandler()),
123  Qt::DirectConnection);
124 
125  connect(this, SIGNAL(CallReadyRead()),
126  this, SLOT(CallReadyReadHandler()),
127  Qt::QueuedConnection);
128 
129  if (!use_shared_thread)
130  {
131  m_thread = new MThread(QString("MythSocketThread(%1)").arg(socket));
132  m_thread->start();
133  }
134  else
135  {
136  QMutexLocker locker(&s_thread_lock);
137  if (!s_thread)
138  {
139  s_thread = new MThread("SharedMythSocketThread");
140  s_thread->start();
141  }
142  m_thread = s_thread;
143  s_thread_cnt++;
144  }
145 
146  m_tcpSocket->moveToThread(m_thread->qthread());
147  moveToThread(m_thread->qthread());
148 }
149 
151 {
152  LOG(VB_SOCKET, LOG_INFO, LOC + QString("MythSocket dtor : cb 0x%2")
153  .arg((intptr_t)(m_callback),0,16));
154 
155  if (IsConnected())
157 
158  if (!m_useSharedThread)
159  {
160  if (m_thread)
161  {
162  m_thread->quit();
163  m_thread->wait();
164  delete m_thread;
165  }
166  }
167  else
168  {
169  QMutexLocker locker(&s_thread_lock);
170  s_thread_cnt--;
171  if (0 == s_thread_cnt)
172  {
173  s_thread->quit();
174  s_thread->wait();
175  delete s_thread;
176  s_thread = nullptr;
177  }
178  }
179  m_thread = nullptr;
180 
181  delete m_tcpSocket;
182  m_tcpSocket = nullptr;
183 }
184 
186 {
187  {
188  QMutexLocker locker(&m_lock);
189  m_connected = true;
190  m_socketDescriptor = m_tcpSocket->socketDescriptor();
191  m_peerAddress = m_tcpSocket->peerAddress();
192  m_peerPort = m_tcpSocket->peerPort();
193  }
194 
195  m_tcpSocket->setSocketOption(QAbstractSocket::LowDelayOption, QVariant(1));
196  m_tcpSocket->setSocketOption(QAbstractSocket::KeepAliveOption, QVariant(1));
197 
198  int reuse_addr_val = 1;
199 #if defined(Q_OS_WIN)
200  int ret = setsockopt(m_tcpSocket->socketDescriptor(), SOL_SOCKET,
201  SO_REUSEADDR, (char*) &reuse_addr_val,
202  sizeof(reuse_addr_val));
203 #else
204  int ret = setsockopt(m_tcpSocket->socketDescriptor(), SOL_SOCKET,
205  SO_REUSEADDR, &reuse_addr_val,
206  sizeof(reuse_addr_val));
207 #endif
208  if (ret < 0)
209  {
210  LOG(VB_SOCKET, LOG_INFO, LOC + "Failed to set SO_REUSEADDR" + ENO);
211  }
212 
213  int rcv_buf_val = kSocketReceiveBufferSize;
214 #if defined(Q_OS_WIN)
215  ret = setsockopt(m_tcpSocket->socketDescriptor(), SOL_SOCKET,
216  SO_RCVBUF, (char*) &rcv_buf_val,
217  sizeof(rcv_buf_val));
218 #else
219  ret = setsockopt(m_tcpSocket->socketDescriptor(), SOL_SOCKET,
220  SO_RCVBUF, &rcv_buf_val,
221  sizeof(rcv_buf_val));
222 #endif
223  if (ret < 0)
224  {
225  LOG(VB_SOCKET, LOG_INFO, LOC + "Failed to set SO_RCVBUF" + ENO);
226  }
227 
228  if (m_callback)
229  {
230  LOG(VB_SOCKET, LOG_DEBUG, LOC +
231  "calling m_callback->connected()");
232  m_callback->connected(this);
233  }
234 }
235 
236 void MythSocket::ErrorHandler(QAbstractSocket::SocketError err)
237 {
238  // Filter these out, we get them because we call waitForReadyRead with a
239  // small timeout so we can print our own debugging for long timeouts.
240  if (err == QAbstractSocket::SocketTimeoutError)
241  return;
242 
243  if (m_callback)
244  {
245  LOG(VB_SOCKET, LOG_DEBUG, LOC +
246  "calling m_callback->error() err: " + m_tcpSocket->errorString());
247  m_callback->error(this, (int)err);
248  }
249 }
250 
252 {
253  {
254  QMutexLocker locker(&m_lock);
255  m_connected = false;
256  m_socketDescriptor = -1;
257  m_peerAddress.clear();
258  m_peerPort = -1;
259  }
260 
261  if (m_callback)
262  {
263  LOG(VB_SOCKET, LOG_DEBUG, LOC +
264  "calling m_callback->connectionClosed()");
266  }
267 }
268 
270 {
271  LOG(VB_SOCKET, LOG_DEBUG, LOC + "AboutToClose");
272 }
273 
275 {
276  m_dataAvailable.fetchAndStoreOrdered(1);
277  if (m_callback && m_disableReadyReadCallback.testAndSetOrdered(0,0))
278  {
279  emit CallReadyRead();
280  }
281 }
282 
284 {
285  // Because the connection to this is a queued connection the
286  // data may have already been read by the time this is called
287  // so we check that there is still data to read before calling
288  // the callback.
289  if (IsDataAvailable())
290  {
291  LOG(VB_SOCKET, LOG_DEBUG, LOC +
292  "calling m_callback->readyRead()");
293  m_callback->readyRead(this);
294  }
295 }
296 
298  const QHostAddress &address, quint16 port)
299 {
300  bool ret = false;
301  QMetaObject::invokeMethod(
302  this, "ConnectToHostReal",
303  (QThread::currentThread() != m_thread->qthread()) ?
304  Qt::BlockingQueuedConnection : Qt::DirectConnection,
305  Q_ARG(QHostAddress, address),
306  Q_ARG(quint16, port),
307  Q_ARG(bool*, &ret));
308  return ret;
309 }
310 
311 bool MythSocket::WriteStringList(const QStringList &list)
312 {
313  bool ret = false;
314  QMetaObject::invokeMethod(
315  this, "WriteStringListReal",
316  (QThread::currentThread() != m_thread->qthread()) ?
317  Qt::BlockingQueuedConnection : Qt::DirectConnection,
318  Q_ARG(const QStringList*, &list),
319  Q_ARG(bool*, &ret));
320  return ret;
321 }
322 
323 bool MythSocket::ReadStringList(QStringList &list, uint timeoutMS)
324 {
325  bool ret = false;
326  QMetaObject::invokeMethod(
327  this, "ReadStringListReal",
328  (QThread::currentThread() != m_thread->qthread()) ?
329  Qt::BlockingQueuedConnection : Qt::DirectConnection,
330  Q_ARG(QStringList*, &list),
331  Q_ARG(uint, timeoutMS),
332  Q_ARG(bool*, &ret));
333  return ret;
334 }
335 
337  QStringList &strlist, uint min_reply_length, uint timeoutMS)
338 {
339  if (m_callback && m_disableReadyReadCallback.testAndSetOrdered(0,0))
340  {
341  // If callbacks are enabled then SendReceiveStringList() will conflict
342  // causing failed reads and socket disconnections - see #11777
343  // SendReceiveStringList() should NOT be used with an event socket, only
344  // the control socket
345  LOG(VB_GENERAL, LOG_EMERG, QString("Programmer Error! "
346  "SendReceiveStringList(%1) used on "
347  "socket with callbacks enabled.")
348  .arg(strlist.isEmpty() ? "empty" : strlist[0]));
349  }
350 
351  if (!WriteStringList(strlist))
352  {
353  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to send command.");
354  return false;
355  }
356 
357  if (!ReadStringList(strlist, timeoutMS))
358  {
359  LOG(VB_GENERAL, LOG_ERR, LOC + "No response.");
360  return false;
361  }
362 
363  if (min_reply_length && ((uint)strlist.size() < min_reply_length))
364  {
365  LOG(VB_GENERAL, LOG_ERR, LOC + "Response too short.");
366  return false;
367  }
368 
369 #if 0
370  if (!strlist.empty() && strlist[0] == "BACKEND_MESSAGE")
371  {
372  LOG(VB_GENERAL, LOG_ERR, LOC + "Got MythEvent on non-event socket");
373  return false;
374  }
375 #endif
376 
377  return true;
378 }
379 
384 bool MythSocket::ConnectToHost(const QString &host, quint16 port)
385 {
386  QHostAddress hadr;
387 
388  // attempt direct assignment
389  if (!hadr.setAddress(host))
390  {
391  // attempt internal lookup through MythCoreContext
392  if (!gCoreContext ||
393  !hadr.setAddress(gCoreContext->GetBackendServerIP(host)))
394  {
395  // attempt external lookup from hosts/DNS
396  QHostInfo info = QHostInfo::fromName(host);
397  if (!info.addresses().isEmpty())
398  {
399  hadr = info.addresses().first();
400  }
401  else
402  {
403  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unable to lookup: %1")
404  .arg(host));
405  return false;
406  }
407  }
408  }
409 
410  return MythSocket::ConnectToHost(hadr, port);
411 }
412 
413 bool MythSocket::Validate(uint timeout_ms, bool error_dialog_desired)
414 {
415  if (m_isValidated)
416  return true;
417 
418  QStringList strlist(QString("MYTH_PROTO_VERSION %1 %2")
419  .arg(MYTH_PROTO_VERSION)
420  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN)));
421 
422  WriteStringList(strlist);
423 
424  if (!ReadStringList(strlist, timeout_ms) || strlist.empty())
425  {
426  LOG(VB_GENERAL, LOG_ERR, "Protocol version check failure.\n\t\t\t"
427  "The response to MYTH_PROTO_VERSION was empty.\n\t\t\t"
428  "This happens when the backend is too busy to respond,\n\t\t\t"
429  "or has deadlocked due to bugs or hardware failure.");
430  return m_isValidated;
431  }
432 
433  if (strlist[0] == "REJECT" && (strlist.size() >= 2))
434  {
435  LOG(VB_GENERAL, LOG_ERR,
436  QString("Protocol version or token mismatch "
437  "(frontend=%1/%2,backend=%3/\?\?)\n")
438  .arg(MYTH_PROTO_VERSION)
439  .arg(QString::fromUtf8(MYTH_PROTO_TOKEN))
440  .arg(strlist[1]));
441 
442  QObject *GUIcontext = gCoreContext->GetGUIContext();
443  if (error_dialog_desired && GUIcontext)
444  {
445  QStringList list(strlist[1]);
446  QCoreApplication::postEvent(
447  GUIcontext, new MythEvent("VERSION_MISMATCH", list));
448  }
449  }
450  else if (strlist[0] == "ACCEPT")
451  {
452  LOG(VB_GENERAL, LOG_NOTICE, QString("Using protocol version %1 %2")
453  .arg(MYTH_PROTO_VERSION).arg(QString::fromUtf8(MYTH_PROTO_TOKEN)));
454  m_isValidated = true;
455  }
456  else
457  {
458  LOG(VB_GENERAL, LOG_ERR,
459  QString("Unexpected response to MYTH_PROTO_VERSION: %1")
460  .arg(strlist[0]));
461  }
462 
463  return m_isValidated;
464 }
465 
466 bool MythSocket::Announce(const QStringList &new_announce)
467 {
468  if (!m_isValidated)
469  {
470  LOG(VB_GENERAL, LOG_ERR, LOC +
471  "refusing to announce unvalidated socket");
472  return false;
473  }
474 
475  if (m_isAnnounced)
476  {
477  LOG(VB_GENERAL, LOG_ERR, LOC + "refusing to re-announce socket");
478  return false;
479  }
480 
481  WriteStringList(new_announce);
482 
483  QStringList tmplist;
485  {
486  LOG(VB_GENERAL, LOG_ERR, LOC +
487  QString("\n\t\t\tCould not read string list from server %1:%2")
488  .arg(m_tcpSocket->peerAddress().toString())
489  .arg(m_tcpSocket->peerPort()));
490  m_announce.clear();
491  m_isAnnounced = false;
492  }
493  else
494  {
495  m_announce = new_announce;
496  m_isAnnounced = true;
497  }
498 
499  return m_isAnnounced;
500 }
501 
502 void MythSocket::SetAnnounce(const QStringList &new_announce)
503 {
504  m_announce = new_announce;
505  m_isAnnounced = true;
506 }
507 
509 {
510  if (QThread::currentThread() != m_thread->qthread() &&
512  {
513  LOG(VB_GENERAL, LOG_ERR, LOC +
514  QString("Programmer error, QEventLoop isn't running and deleting "
515  "MythSocket(0x%1)").arg(reinterpret_cast<intptr_t>(this),0,16));
516  return;
517  }
518  QMetaObject::invokeMethod(
519  this, "DisconnectFromHostReal",
520  (QThread::currentThread() != m_thread->qthread()) ?
521  Qt::BlockingQueuedConnection : Qt::DirectConnection);
522 }
523 
524 int MythSocket::Write(const char *data, int size)
525 {
526  int ret = -1;
527  QMetaObject::invokeMethod(
528  this, "WriteReal",
529  (QThread::currentThread() != m_thread->qthread()) ?
530  Qt::BlockingQueuedConnection : Qt::DirectConnection,
531  Q_ARG(const char*, data),
532  Q_ARG(int, size),
533  Q_ARG(int*, &ret));
534  return ret;
535 }
536 
537 int MythSocket::Read(char *data, int size, int max_wait_ms)
538 {
539  int ret = -1;
540  QMetaObject::invokeMethod(
541  this, "ReadReal",
542  (QThread::currentThread() != m_thread->qthread()) ?
543  Qt::BlockingQueuedConnection : Qt::DirectConnection,
544  Q_ARG(char*, data),
545  Q_ARG(int, size),
546  Q_ARG(int, max_wait_ms),
547  Q_ARG(int*, &ret));
548  return ret;
549 }
550 
552 {
553  QMetaObject::invokeMethod(
554  this, "ResetReal",
555  (QThread::currentThread() != m_thread->qthread()) ?
556  Qt::BlockingQueuedConnection : Qt::DirectConnection);
557 }
558 
560 
561 bool MythSocket::IsConnected(void) const
562 {
563  QMutexLocker locker(&m_lock);
564  return m_connected;
565 }
566 
568 {
569  if (QThread::currentThread() == m_thread->qthread())
570  return m_tcpSocket->bytesAvailable() > 0;
571 
572  if (m_dataAvailable.testAndSetOrdered(0,0))
573  return false;
574 
575  bool ret = false;
576 
577  QMetaObject::invokeMethod(
578  const_cast<MythSocket*>(this), "IsDataAvailableReal",
579  Qt::BlockingQueuedConnection,
580  Q_ARG(bool*, &ret));
581 
582  return ret;
583 }
584 
586 {
587  QMutexLocker locker(&m_lock);
588  return m_socketDescriptor;
589 }
590 
591 QHostAddress MythSocket::GetPeerAddress(void) const
592 {
593  QMutexLocker locker(&m_lock);
594  return m_peerAddress;
595 }
596 
597 int MythSocket::GetPeerPort(void) const
598 {
599  QMutexLocker locker(&m_lock);
600  return m_peerPort;
601 }
602 
604 
605 void MythSocket::IsDataAvailableReal(bool *ret) const
606 {
607  *ret = (m_tcpSocket->bytesAvailable() > 0);
608  m_dataAvailable.fetchAndStoreOrdered((*ret) ? 1 : 0);
609 }
610 
611 void MythSocket::ConnectToHostReal(const QHostAddress& _addr, quint16 port, bool *ret)
612 {
613  if (m_tcpSocket->state() == QAbstractSocket::ConnectedState)
614  {
615  LOG(VB_SOCKET, LOG_ERR, LOC +
616  "connect() called with already open socket, closing");
617  m_tcpSocket->close();
618  }
619 
620  QHostAddress addr = _addr;
621  addr.setScopeId(QString());
622 
623  s_loopbackCacheLock.lock();
624  bool usingLoopback = s_loopbackCache.contains(addr.toString());
625  s_loopbackCacheLock.unlock();
626 
627  if (usingLoopback)
628  {
629  addr = QHostAddress(s_loopbackCache.value(addr.toString()));
630  }
631  else
632  {
633  QList<QHostAddress> localIPs = QNetworkInterface::allAddresses();
634  for (int i = 0; i < localIPs.count() && !usingLoopback; ++i)
635  {
636  QHostAddress local = localIPs[i];
637  local.setScopeId(QString());
638 
639  if (addr == local)
640  {
641  QHostAddress::SpecialAddress loopback = QHostAddress::LocalHost;
642  if (addr.protocol() == QAbstractSocket::IPv6Protocol)
643  loopback = QHostAddress::LocalHostIPv6;
644 
645  QMutexLocker locker(&s_loopbackCacheLock);
646  s_loopbackCache[addr.toString()] = loopback;
647  addr = QHostAddress(loopback);
648  usingLoopback = true;
649  }
650  }
651  }
652 
653  if (usingLoopback)
654  {
655  LOG(VB_SOCKET, LOG_INFO, LOC +
656  "IP is local, using loopback address instead");
657  }
658 
659  LOG(VB_SOCKET, LOG_INFO, LOC + QString("attempting connect() to (%1:%2)")
660  .arg(addr.toString()).arg(port));
661 
662  bool ok = true;
663 
664  // Sort out link-local address scope if applicable
665  if (!usingLoopback)
666  {
667  QString host = addr.toString();
668  if (PortChecker::resolveLinkLocal(host, port))
669  addr.setAddress(host);
670  }
671 
672  if (ok)
673  {
674  m_tcpSocket->connectToHost(addr, port, QAbstractSocket::ReadWrite);
675  ok = m_tcpSocket->waitForConnected(5000);
676  }
677 
678  if (ok)
679  {
680  LOG(VB_SOCKET, LOG_INFO, LOC + QString("Connected to (%1:%2)")
681  .arg(addr.toString()).arg(port));
682  }
683  else
684  {
685  LOG(VB_GENERAL, LOG_ERR, LOC +
686  QString("Failed to connect to (%1:%2) %3")
687  .arg(addr.toString()).arg(port)
688  .arg(m_tcpSocket->errorString()));
689  }
690 
691  *ret = ok;
692 }
693 
695 {
696  m_tcpSocket->disconnectFromHost();
697 }
698 
699 void MythSocket::WriteStringListReal(const QStringList *list, bool *ret)
700 {
701  if (list->empty())
702  {
703  LOG(VB_GENERAL, LOG_ERR, LOC +
704  "WriteStringList: Error, invalid string list.");
705  *ret = false;
706  return;
707  }
708 
709  if (m_tcpSocket->state() != QAbstractSocket::ConnectedState)
710  {
711  LOG(VB_GENERAL, LOG_ERR, LOC +
712  "WriteStringList: Error, called with unconnected socket.");
713  *ret = false;
714  return;
715  }
716 
717  QString str = list->join("[]:[]");
718  if (str.isEmpty())
719  {
720  LOG(VB_GENERAL, LOG_ERR, LOC +
721  "WriteStringList: Error, joined null string.");
722  *ret = false;
723  return;
724  }
725 
726  QByteArray utf8 = str.toUtf8();
727  int size = utf8.length();
728  int written = 0;
729  int written_since_timer_restart = 0;
730 
731  QByteArray payload;
732  payload = payload.setNum(size);
733  payload += " ";
734  payload.truncate(8);
735  payload += utf8;
736  size = payload.length();
737 
738  if (VERBOSE_LEVEL_CHECK(VB_NETWORK, LOG_INFO))
739  {
740  QString msg = QString("write -> %1 %2")
741  .arg(m_tcpSocket->socketDescriptor(), 2).arg(payload.data());
742 
743  if (logLevel < LOG_DEBUG && msg.length() > 128)
744  {
745  msg.truncate(127);
746  msg += "…";
747  }
748  LOG(VB_NETWORK, LOG_INFO, LOC + msg);
749  }
750 
751  MythTimer timer; timer.start();
752  unsigned int errorcount = 0;
753  while (size > 0)
754  {
755  if (m_tcpSocket->state() != QAbstractSocket::ConnectedState)
756  {
757  LOG(VB_GENERAL, LOG_ERR, LOC +
758  "WriteStringList: Error, socket went unconnected." +
759  QString("\n\t\t\tWe wrote %1 of %2 bytes with %3 errors")
760  .arg(written).arg(written+size).arg(errorcount) +
761  QString("\n\t\t\tstarts with: %1").arg(to_sample(payload)));
762  *ret = false;
763  return;
764  }
765 
766  int temp = m_tcpSocket->write(payload.data() + written, size);
767  if (temp > 0)
768  {
769  written += temp;
770  written_since_timer_restart += temp;
771  size -= temp;
772  if ((timer.elapsed() > 500) && written_since_timer_restart != 0)
773  {
774  timer.restart();
775  written_since_timer_restart = 0;
776  }
777  }
778  else
779  {
780  errorcount++;
781  if (timer.elapsed() > 1000)
782  {
783  LOG(VB_GENERAL, LOG_ERR, LOC + "WriteStringList: Error, " +
784  QString("No data written on write (%1 errors)")
785  .arg(errorcount) +
786  QString("\n\t\t\tstarts with: %1")
787  .arg(to_sample(payload)));
788  *ret = false;
789  return;
790  }
791  usleep(1000);
792  }
793  }
794 
795  m_tcpSocket->flush();
796 
797  *ret = true;
798 }
799 
801  QStringList *list, uint timeoutMS, bool *ret)
802 {
803  list->clear();
804  *ret = false;
805 
806  MythTimer timer;
807  timer.start();
808  int elapsed = 0;
809 
810  while (m_tcpSocket->bytesAvailable() < 8)
811  {
812  elapsed = timer.elapsed();
813  if (elapsed >= (int)timeoutMS)
814  {
815  LOG(VB_GENERAL, LOG_ERR, LOC + "ReadStringList: " +
816  QString("Error, timed out after %1 ms.").arg(timeoutMS));
817  m_tcpSocket->close();
818  m_dataAvailable.fetchAndStoreOrdered(0);
819  return;
820  }
821 
822  if (m_tcpSocket->state() != QAbstractSocket::ConnectedState)
823  {
824  LOG(VB_GENERAL, LOG_ERR, LOC + "ReadStringList: Connection died.");
825  m_dataAvailable.fetchAndStoreOrdered(0);
826  return;
827  }
828 
829  m_tcpSocket->waitForReadyRead(50);
830  }
831 
832  QByteArray sizestr(8 + 1, '\0');
833  if (m_tcpSocket->read(sizestr.data(), 8) < 0)
834  {
835  LOG(VB_GENERAL, LOG_ERR, LOC +
836  QString("ReadStringList: Error, read return error (%1)")
837  .arg(m_tcpSocket->errorString()));
838  m_tcpSocket->close();
839  m_dataAvailable.fetchAndStoreOrdered(0);
840  return;
841  }
842 
843  QString sizes = sizestr;
844  qint64 btr = sizes.trimmed().toInt();
845 
846  if (btr < 1)
847  {
848  int pending = m_tcpSocket->bytesAvailable();
849  LOG(VB_GENERAL, LOG_ERR, LOC +
850  QString("Protocol error: '%1' is not a valid size "
851  "prefix. %2 bytes pending.")
852  .arg(sizestr.data()).arg(pending));
853  ResetReal();
854  return;
855  }
856 
857  QByteArray utf8(btr + 1, 0);
858 
859  qint64 readoffset = 0;
860  int errmsgtime = 0;
861  timer.start();
862 
863  while (btr > 0)
864  {
865  if (m_tcpSocket->bytesAvailable() < 1)
866  {
867  if (m_tcpSocket->state() == QAbstractSocket::ConnectedState)
868  {
869  m_tcpSocket->waitForReadyRead(50);
870  }
871  else
872  {
873  LOG(VB_GENERAL, LOG_ERR, LOC +
874  "ReadStringList: Connection died.");
875  m_dataAvailable.fetchAndStoreOrdered(0);
876  return;
877  }
878  }
879 
880  qint64 sret = m_tcpSocket->read(utf8.data() + readoffset, btr);
881  if (sret > 0)
882  {
883  readoffset += sret;
884  btr -= sret;
885  if (btr > 0)
886  {
887  timer.start();
888  }
889  }
890  else if (sret < 0)
891  {
892  LOG(VB_GENERAL, LOG_ERR, LOC + "ReadStringList: Error, read");
893  m_tcpSocket->close();
894  m_dataAvailable.fetchAndStoreOrdered(0);
895  return;
896  }
897  else if (!m_tcpSocket->isValid())
898  {
899  LOG(VB_GENERAL, LOG_ERR, LOC +
900  "ReadStringList: Error, socket went unconnected");
901  m_tcpSocket->close();
902  m_dataAvailable.fetchAndStoreOrdered(0);
903  return;
904  }
905  else
906  {
907  elapsed = timer.elapsed();
908  if (elapsed > 10000)
909  {
910  if ((elapsed - errmsgtime) > 10000)
911  {
912  errmsgtime = elapsed;
913  LOG(VB_GENERAL, LOG_ERR, LOC +
914  QString("ReadStringList: Waiting for data: %1 %2")
915  .arg(readoffset).arg(btr));
916  }
917  }
918 
919  if (elapsed > 100000)
920  {
921  LOG(VB_GENERAL, LOG_ERR, LOC +
922  "Error, ReadStringList timeout (readBlock)");
923  m_dataAvailable.fetchAndStoreOrdered(0);
924  return;
925  }
926  }
927  }
928 
929  QString str = QString::fromUtf8(utf8.data());
930 
931  if (VERBOSE_LEVEL_CHECK(VB_NETWORK, LOG_INFO))
932  {
933  QByteArray payload;
934  payload = payload.setNum(str.length());
935  payload += " ";
936  payload.truncate(8);
937  payload += utf8.data();
938 
939  QString msg = QString("read <- %1 %2")
940  .arg(m_tcpSocket->socketDescriptor(), 2)
941  .arg(payload.data());
942 
943  if (logLevel < LOG_DEBUG && msg.length() > 128)
944  {
945  msg.truncate(127);
946  msg += "…";
947  }
948  LOG(VB_NETWORK, LOG_INFO, LOC + msg);
949  }
950 
951  *list = str.split("[]:[]");
952 
953  m_dataAvailable.fetchAndStoreOrdered(
954  (m_tcpSocket->bytesAvailable() > 0) ? 1 : 0);
955 
956  *ret = true;
957 }
958 
959 void MythSocket::WriteReal(const char *data, int size, int *ret)
960 {
961  *ret = m_tcpSocket->write(data, size);
962 }
963 
964 void MythSocket::ReadReal(char *data, int size, int max_wait_ms, int *ret)
965 {
966  MythTimer t; t.start();
967  while ((m_tcpSocket->state() == QAbstractSocket::ConnectedState) &&
968  (m_tcpSocket->bytesAvailable() < size) &&
969  (t.elapsed() < max_wait_ms))
970  {
971  m_tcpSocket->waitForReadyRead(max(2, max_wait_ms - t.elapsed()));
972  }
973  *ret = m_tcpSocket->read(data, size);
974 
975  if (t.elapsed() > 50)
976  {
977  LOG(VB_NETWORK, LOG_INFO,
978  QString("ReadReal(?, %1, %2) -> %3 took %4 ms")
979  .arg(size).arg(max_wait_ms).arg(*ret)
980  .arg(t.elapsed()));
981  }
982 
983  m_dataAvailable.fetchAndStoreOrdered(
984  (m_tcpSocket->bytesAvailable() > 0) ? 1 : 0);
985 }
986 
988 {
989  vector<char> trash;
990 
991  m_tcpSocket->waitForReadyRead(30);
992  do
993  {
994  uint avail = m_tcpSocket->bytesAvailable();
995  if (avail)
996  {
997  trash.resize(max((uint)trash.size(),avail));
998  m_tcpSocket->read(trash.data(), avail);
999  }
1000 
1001  LOG(VB_NETWORK, LOG_INFO, LOC + "Reset() " +
1002  QString("%1 bytes available").arg(avail));
1003 
1004  m_tcpSocket->waitForReadyRead(30);
1005  }
1006  while (m_tcpSocket->bytesAvailable() > 0);
1007 
1008  m_dataAvailable.fetchAndStoreOrdered(0);
1009 }
static QMutex s_loopbackCacheLock
Definition: mythsocket.h:118
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
int restart(void)
Returns milliseconds elapsed since last start() or restart() and resets the count.
Definition: mythtimer.cpp:62
static int x6
Definition: mythsocket.cpp:65
void SetAnnounce(const QStringList &new_announce)
Definition: mythsocket.cpp:502
bool m_useSharedThread
Definition: mythsocket.h:106
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
void DisconnectFromHostReal(void)
Definition: mythsocket.cpp:694
virtual void connectionClosed(MythSocket *)=0
bool Announce(const QStringList &new_announce)
Definition: mythsocket.cpp:466
void AboutToCloseHandler(void)
Definition: mythsocket.cpp:269
int GetSocketDescriptor(void) const
Definition: mythsocket.cpp:585
static const uint kLongTimeout
Definition: mythsocket.h:72
void ReadyReadHandler(void)
Definition: mythsocket.cpp:274
#define kMythSocketLongTimeout
Definition: mythsocket_cb.h:8
#define LOC
Definition: mythsocket.cpp:38
static void error(const char *str,...)
Definition: vbi.c:42
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
bool Validate(uint timeout_ms=kMythSocketLongTimeout, bool error_dialog_desired=false)
Definition: mythsocket.cpp:413
General purpose reference counter.
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
void CallReadyReadHandler(void)
Definition: mythsocket.cpp:283
static int s_thread_cnt
Definition: mythsocket.h:123
int Read(char *, int size, int max_wait_ms)
Definition: mythsocket.cpp:537
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
int GetPeerPort(void) const
Definition: mythsocket.cpp:597
virtual void error(MythSocket *, int)
Definition: mythsocket_cb.h:16
qt_socket_fd_t m_socketDescriptor
Definition: mythsocket.h:102
qintptr qt_socket_fd_t
Definition: mythqtcompat.h:4
static void(* m_callback)(void *, QString &)
MythSocket(qt_socket_fd_t socket=-1, MythSocketCBs *cb=nullptr, bool use_shared_thread=false)
Definition: mythsocket.cpp:81
QAtomicInt m_disableReadyReadCallback
Definition: mythsocket.h:107
static int x4
Definition: mythsocket.cpp:63
static bool resolveLinkLocal(QString &host, int port, int timeLimit=30000)
Convenience method to resolve link-local address.
void DisconnectFromHost(void)
Definition: mythsocket.cpp:508
void IsDataAvailableReal(bool *ret) const
Definition: mythsocket.cpp:605
QStringList m_announce
Definition: mythsocket.h:114
This class is used as a container for messages.
Definition: mythevent.h:16
bool m_isAnnounced
Definition: mythsocket.h:113
static int x2
Definition: mythsocket.cpp:61
int m_peerPort
Definition: mythsocket.h:104
static MThread * s_thread
Definition: mythsocket.h:122
QHostAddress m_peerAddress
Definition: mythsocket.h:103
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:24
int Write(const char *, int size)
Definition: mythsocket.cpp:524
QObject * GetGUIContext(void)
QMutex m_lock
Definition: mythsocket.h:101
bool IsDataAvailable(void) const
Definition: mythsocket.cpp:567
unsigned char t
Definition: ParseText.cpp:329
QAtomicInt m_dataAvailable
This is used internally as a hint that there might be data available for reading.
Definition: mythsocket.h:111
#define MYTH_PROTO_TOKEN
Definition: mythversion.h:49
#define MYTH_PROTO_VERSION
Increment this whenever the MythTV network protocol changes.
Definition: mythversion.h:48
MThread * m_thread
Definition: mythsocket.h:100
Q_DECLARE_METATYPE(const QStringList *)
void ConnectHandler(void)
Definition: mythsocket.cpp:185
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
static const int kSocketReceiveBufferSize
Definition: mythsocket.h:116
void WriteReal(const char *, int size, int *ret)
Definition: mythsocket.cpp:959
void Reset(void)
Definition: mythsocket.cpp:551
int elapsed(void) const
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
int s_dummy_meta_variable_to_suppress_gcc_warning
Definition: mythsocket.cpp:66
bool ReadStringList(QStringList &list, uint timeoutMS=kShortTimeout)
Definition: mythsocket.cpp:323
bool m_isValidated
Definition: mythsocket.h:112
void ConnectToHostReal(const QHostAddress &addr, quint16 port, bool *ret)
Definition: mythsocket.cpp:611
void ReadStringListReal(QStringList *list, uint timeoutMS, bool *ret)
Definition: mythsocket.cpp:800
static int x5
Definition: mythsocket.cpp:64
static QMutex s_thread_lock
Definition: mythsocket.h:121
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static int x0
Definition: mythsocket.cpp:59
MythSocketCBs * m_callback
Definition: mythsocket.h:105
LogLevel_t logLevel
Definition: logging.cpp:95
virtual void readyRead(MythSocket *)=0
bool ConnectToHost(const QString &hostname, quint16 port)
connect to host
Definition: mythsocket.cpp:384
void CallReadyRead(void)
static const uint kShortTimeout
Definition: mythsocket.h:71
bool IsConnected(void) const
Definition: mythsocket.cpp:561
void ReadReal(char *, int size, int max_wait_ms, int *ret)
Definition: mythsocket.cpp:964
virtual void connected(MythSocket *)=0
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
Definition: mthread.cpp:244
static QHash< QString, QHostAddress::SpecialAddress > s_loopbackCache
Definition: mythsocket.h:119
bool SendReceiveStringList(QStringList &list, uint min_reply_length=0, uint timeoutMS=kLongTimeout)
Definition: mythsocket.cpp:336
QTcpSocket * m_tcpSocket
Definition: mythsocket.h:99
bool WriteStringList(const QStringList &list)
Definition: mythsocket.cpp:311
static QString to_sample(const QByteArray &payload)
Definition: mythsocket.cpp:69
bool m_connected
Definition: mythsocket.h:108
void ResetReal(void)
Definition: mythsocket.cpp:987
void WriteStringListReal(const QStringList *list, bool *ret)
Definition: mythsocket.cpp:699
#define kMythSocketShortTimeout
-*- Mode: c++ -*-
Definition: mythsocket_cb.h:7
QHostAddress GetPeerAddress(void) const
Definition: mythsocket.cpp:591
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
static int x3
Definition: mythsocket.cpp:62
void ErrorHandler(QAbstractSocket::SocketError)
Definition: mythsocket.cpp:236
void DisconnectHandler(void)
Definition: mythsocket.cpp:251
void quit(void)
calls exit(0)
Definition: mthread.cpp:306
static int x1
Definition: mythsocket.cpp:60
bool CheckSubnet(const QAbstractSocket *socket)
Check if a socket is connected to an approved peer.