MythTV  master
mmulticastsocketdevice.cpp
Go to the documentation of this file.
1 // Program Name: mmulticastsocketdevice.cpp
3 // Created : Oct. 1, 2005
4 //
5 // Purpose : Multicast QSocketDevice Implmenetation
6 //
7 // Copyright (c) 2005 David Blain <dblain@mythtv.org>
8 //
9 // Licensed under the GPL v2 or later, see COPYING for details
10 //
12 
13 #include <cerrno>
14 #include "mythconfig.h"
15 
16 #ifdef _WIN32
17 # include <winsock2.h>
18 # include <ws2tcpip.h>
19 # define GET_SOCKET_ERROR WSAGetLastError()
20 #else
21 # include <sys/socket.h>
22 # include <netinet/in.h>
23 # include <arpa/inet.h>
24 # define GET_SOCKET_ERROR errno
25 #endif
26 
27 #include <chrono> // for milliseconds
28 #include <thread> // for sleep_for
29 
30 // Qt headers
31 #include <QStringList>
32 
33 // MythTV headers
34 #include "mmulticastsocketdevice.h"
35 #include "mythlogging.h"
36 
37 #define LOC QString("MMulticastSocketDevice(%1:%2): ") \
38  .arg(m_address.toString()).arg(socket())
39 
41  const QString& sAddress, quint16 nPort, u_char ttl) :
42  MSocketDevice(MSocketDevice::Datagram),
43  m_address(sAddress), m_port(nPort)
44 {
45 #if 0
46  ttl = UPnp::GetConfiguration()->GetValue( "UPnP/TTL", 4 );
47 #endif
48 
49  if (ttl == 0)
50  ttl = 4;
51 
52  setProtocol(IPv4);
53  setSocket(createNewSocket(), MSocketDevice::Datagram);
54 
55  m_imr.imr_multiaddr.s_addr = inet_addr(sAddress.toLatin1().constData());
56  m_imr.imr_interface.s_addr = htonl(INADDR_ANY);
57 
58  if (setsockopt(socket(), IPPROTO_IP, IP_ADD_MEMBERSHIP,
59  (const char *)&m_imr, sizeof( m_imr )) < 0)
60  {
61  LOG(VB_GENERAL, LOG_ERR, LOC + "setsockopt - IP_ADD_MEMBERSHIP " + ENO);
62  }
63 
64  if (setsockopt(socket(), IPPROTO_IP, IP_MULTICAST_TTL,
65  (const char *)&ttl, sizeof(ttl)) < 0)
66  {
67  LOG(VB_GENERAL, LOG_ERR, LOC + "setsockopt - IP_MULTICAST_TTL " + ENO);
68  }
69 
70  setAddressReusable(true);
71 
72  if (!bind(m_address, m_port))
73  LOG(VB_GENERAL, LOG_ERR, LOC + "bind failed");
74 }
75 
77 {
78  if (!m_address.isNull() &&
79  (setsockopt(socket(), IPPROTO_IP, IP_DROP_MEMBERSHIP,
80  (char*)(&m_imr), sizeof(m_imr)) < 0))
81  {
82  // This isn't really an error, we will drop out of
83  // the group anyway when we close the socket.
84  LOG(VB_GENERAL, LOG_DEBUG, LOC + "setsockopt - IP_DROP_MEMBERSHIP " +
85  ENO);
86  }
87 }
88 
90  const char *data, quint64 len,
91  const QHostAddress & host, quint16 port)
92 {
93 #ifdef IP_MULTICAST_IF
94  if (host.toString() == "239.255.255.250")
95  {
96  QList<QHostAddress>::const_iterator it = m_local_addresses.begin();
97  int retx = 0;
98  for (; it != m_local_addresses.end(); ++it)
99  {
100  if ((*it).protocol() != QAbstractSocket::IPv4Protocol)
101  continue; // skip IPv6 addresses
102 
103  QString addr = (*it).toString();
104  if (addr == "127.0.0.1")
105  continue; // skip localhost address
106 
107  uint32_t interface_addr = (*it).toIPv4Address();
108  if (setsockopt(socket(), IPPROTO_IP, IP_MULTICAST_IF,
109  (const char *)&interface_addr,
110  sizeof(interface_addr)) < 0)
111  {
112  LOG(VB_GENERAL, LOG_DEBUG, LOC +
113  "setsockopt - IP_MULTICAST_IF " + ENO);
114  }
115  retx = MSocketDevice::writeBlock(data, len, host, port);
116 #if 0
117  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("writeBlock on %1 %2")
118  .arg((*it).toString()).arg((retx==(int)len)?"ok":"err"));
119 #endif
120  std::this_thread::sleep_for(std::chrono::milliseconds(5 + (random() % 5)));
121  }
122  return retx;
123  }
124 #endif
125 
126  return MSocketDevice::writeBlock(data, len, host, port);
127 }
virtual int GetValue(const QString &sSetting, int Default)=0
quint16 port() const override
QList< QHostAddress > m_local_addresses
static Configuration * GetConfiguration()
Definition: upnp.cpp:71
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
virtual qint64 writeBlock(const char *data, quint64 len, const QHostAddress &host, quint16 port) override
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static long int random(void)
Definition: compat.h:149
#define LOC