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