QUdpSocket: Can't make multicast work on loopback interface (localhost)
-
Please, help setup sockets to receive and send multicast datagrams in following scenario: user selects network interface (because it may have multiple networks on its computer, or even have no networks at all, in which case it must be able to select loopback interface and work locally with multiple application instances), application instance must not receive its own loopbacked packets (or it must be able to filter them out).
Seems to be very easy, but I've spent a lot of time and broken my mind trying to make it work. I've already researched low-level sockets api, its options, examined a lot of similar issues on the web, tried all possible combinations, but no success !
Here is result of my research, which SEEMS to work, but I don't beleive it will work on any other combination of platform and network configuration, other than my computer have:
@
void initNetwork() {
//...
/* It will be needed to filter out own loopbacked datagrams /
local_addresses = QNetworkInterface::allAddresses();
/ Interface, selected by user /
QNetworkInterface multicast_netif = <user selected>;
Q_ASSERT(multicast_netif.isValid());
/ Yes, I already accept the fact, that I need two separate sockets (there are more chances to make it work than when using bidirectional one) /
udpSocketIn = new QUdpSocket(this);
udpSocketOut = new QUdpSocket(this);
/ It's important to bind to Any. No other combinations work (including LocalHost (in case if user selected loopback interface), MULTICAST_ADDR) /
result = udpSocketIn->bind(QHostAddress::Any, MULTICAST_PORT, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
Q_ASSERT(result);
/ It required to only make application know real(!) udpSocketOut->localPort() in order to be able filter own datagrams /
result = udpSocketOut->bind();
Q_ASSERT(result);
/ One of rare things, I'm sure it's correct and must be done /
result = udpSocketIn->joinMulticastGroup(QHostAddress(MULTICAST_ADDR), multicast_netif);
Q_ASSERT(result);
/ It doesn't matter, but it will fail if socket not binded /
//result = udpSocketOut->joinMulticastGroup(QHostAddress(MULTICAST_ADDR), multicast_netif);
//Q_ASSERT(result);
/ No, you can't ! If socket binded previously and loopback interface selected, datagrams will not be transfered. I don't know why. And this is major thing, which makes me think, that this configuration isn't reliable, because stupid windows will select default interface for outgoing datagrams ! /
//udpSocketOut->setMulticastInterface(multicast_netif);
/ It doesn't matter, because it set by default. If set to 0, datagrams will not be transferred on loopback interface. I don't know why ! */
//udpSocketIn->setSocketOption(QAbstractSocket::MulticastLoopbackOption, QVariant(1));
//udpSocketOut->setSocketOption(QAbstractSocket::MulticastLoopbackOption, QVariant(1));
//...
}void sendDatagram() {
//...
/* It almost always return ok, regardless of datagram being sent actually or not.
One exception is when I turn off real network interface to which it was binded by udpSocketOut->bind() call (it selected by OS, although user selected loopback interface !)
*/
result = udpSocketOut->writeDatagram(datagram, QHostAddress((MULTICAST_ADDR), MULTICAST_PORT);
Q_ASSERT(result == datagram.size());
//...
}void readPendingDatagrams() {
//...
udpSocketIn->readDatagram(datagram, &senderHost, &senderPort);
/* Thanks to udpSocketOut->bind() we are able to filter out own packets sent from udpSocketOut */
if ((local_addresses.contains(senderHost)) && (senderPort == udpSocketOut->localPort())) {
// Ignore loopbacked datagram
return;
}
//...
}
@I've tried use single socket, and it also SEEMS to work, but in this case I'm not be able to filter out own datagrams, because port numbers are same everywhere.
Qt: 4.8.4
OS: Windows 7 x64 -