Multicast Sender / Receiver on different computers
-
Hello all,
Not sure if this has been covered but I can't find any information about it.
I am trying to multicast from one PC to another PC (on the same network).
I'm using these multicast examples:
https://code.qt.io/cgit/qt/qtbase.git/tree/examples/network/multicastsender?h=6.3
https://code.qt.io/cgit/qt/qtbase.git/tree/examples/network/multicastreceiver?h=6.3If I run the sender and receiver on the same PC it works fine.
But if I run the sender on one PC and the receiver on another, the receiver never gets anything. It does not matter which PC I run the sender on, the receiver on the other PC never gets the message.
I have an old Windows Visual Studio program that does something similar and it works with no problems. I'm in the process of updating that code to use Qt instead but can't get this part to work.
What am I missing?
Thanks
-
Hello,
I have tried running Wireshark.
Some additional information.
My laptop has multiple network interfaces. Specifically, this code:
QList<QNetworkInterface> list = QNetworkInterface::allInterfaces(); foreach (QNetworkInterface iface, list) { qDebug() << "Interface:" << iface.humanReadableName(); }
Give me this result on the 1st PC:
Interface: "Ethernet"
Interface: "VirtualBox Host-Only Network"
Interface: "Ethernet 2"
Interface: "Wi-Fi"
Interface: "Local Area Connection* 1"
Interface: "Local Area Connection* 2"
Interface: "Bluetooth Network Connection"
Interface: "Loopback Pseudo-Interface 1"When I run the sender / receiver on that PC, I am only seeing the multicast messages with WireShark on the "VirtualBox Host-Only Network" interface.
The other PC is running Ubuntu and has these interfaces:
Interface: "eno1"
Interface: "wlp0s20f3"When I run the sender / receiver on that PC I get the multicast messages on the "wlp0s20f3" interface which is connected to the "Ethernet" interface on the 1st PC.
So it looks like the problem may be that the 1st PC is not binding the sender to the correct interface. Is there a way to force it?
I really want the sender to send the message on all interfaces and the receiver to catch those messages.
I thought the Windows code was confusing but at least it worked....
Thanks
-
I may have found a solution, just not sure if it's correct.
I changed the code in the receive from this:
udpSocketRx.bind(QHostAddress::AnyIPv4, RX_PORT, QUdpSocket::ShareAddress); udpSocketRx.joinMulticastGroup(groupAddress);
to this:
udpSocketRx.bind(QHostAddress::AnyIPv4, RX_PORT, QUdpSocket::ShareAddress); QList<QNetworkInterface> list = QNetworkInterface::allInterfaces(); foreach (QNetworkInterface iface, list) udpSocketRx.joinMulticastGroup(groupAddress, iface);
Now both receivers get the messages from both senders.
Strangely no change was needed in the sender code.
-
OK, not quite fixed.
The sender / receiver is working on my 1st PC "Ethernet" / 2nd PC "wlp0s20f3" interface (10.168.0.xxx network).
But it I try to start a sender / receiver on my 2st PC "Ethernet 2" / some other PC interface (192.168.100.xxx network) it does not work.
-
Running the sender, WireShark only shows it joining the group on the "Ethernet" interface.
-
Running the receiver, WireShark only shows it joining the group on the "Ethernet 2" interface.
-
Starting the sender, WireShark only shows the send/receive messages on the "Ethernet" interface.
-
Running the receiver first, WireShark shows it joining the group on both the "Ethernet" and "Ethernet 2" interface.
-
Running the sender, WireShark does not show any joining of the group.
-
Starting the sender, WireShark only shows the send/receive messages on the "Ethernet" interface.
As a sanity check, I checked my old Windows code.
- Run the "receiver" first, WireShark shows it joining the group on the "Ethernet 2" interface.
- Run the "sender", WireShark shows it joining the group on both the "Ethernet" and "Ethernet 2" interface
- WireShark shows the send messages on both the "Ethernet" and "Ethernet 2" interfaces.
- WireShark shows the receive messages on the "Ethernet 2" interface.
Running the "sender" then the "receiver" also works.
Also, connecting another "receiver" on the "Ethernet" interface works. I get receive messages from both "Ethernet" and "Ethernet 2" interfaces.
Note, I am expecting messages in both directions. The sender is actually sending a query message as a multicast on a given port. All attached receivers then send a reply message on a different port.
My modified code based on the Qt Multicast Sender / Receiver example does the send query / receive response just fine. But only ON the "Ethernet" interface. I need it to work on all connected interfaces.
Any help is appreciated.
-
-
I'm still not getting this. :-(
Some background may help.
- My host machine can have multiple network interfaces (and it does).
- I have one or more "target" devices connected to my network. They could be connected to any interface of my host machine.
- The target device listens on port 13020 for multicast "query" commands
- The target device responds on port 13021 with a "reply" message that contains it's IP address and ID information.
- The host machine runs this "sender" app to send the query commands on all interfaces. It then receives the "reply" messages from all the targets and processes them.
This works kind of like a network printer discovery.
Currently my setup of the sender QUdpSocket send/receive looks like this:
#define TX_PORT 13020 // host query transmit / target query receive port #define RX_PORT 13021 // host reply receive / target transmit reply port Sender::Sender(QWidget* parent) : QDialog(parent), groupAddress(QStringLiteral("226.13.13.13")) { QUdpSocket::BindMode flags = QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint; udpSocketTx.bind(TX_PORT, flags); udpSocketRx.bind(QHostAddress::AnyIPv4, RX_PORT, flags); QList<QNetworkInterface> list = QNetworkInterface::allInterfaces(); foreach (QNetworkInterface iface, list) { if (iface.flags().testFlag(QNetworkInterface::IsRunning) && iface.flags().testFlag(QNetworkInterface::CanMulticast) && !iface.flags().testFlag(QNetworkInterface::IsLoopBack)) { if (udpSocketRx.joinMulticastGroup(groupAddress, iface)) qDebug() << iface.index() << iface.humanReadableName() << "joined multicast" << groupAddress; } } // setup the GUI ... // connect the signal/slots ... connect(&udpSocketRx, &QUdpSocket::readyRead, this, &Sender::processPendingDatagrams); }
The qDebug() output on my test machine is:
4 "Ethernet" joined multicast QHostAddress("226.13.13.13") 7 "VirtualBox Host-Only Network" joined multicast QHostAddress("226.13.13.13") 20 "Ethernet 2" joined multicast QHostAddress("226.13.13.13")
I'm only interested in the "Ethernet" and "Ethernet 2" interfaces. In WireShark I see both of them joining the multicast group:
190499 6861.248925 10.168.0.95 224.0.0.22 IGMPv3 54 Membership Report / Join group 226.13.13.13 for any sources 105338 6836.393405 192.168.100.100 224.0.0.22 IGMPv3 54 Membership Report / Join group 226.13.13.13 for any sources
When I start the sender I see the messages on the "Ethernet 2" interface:
105339 6836.393443 192.168.100.100 226.13.13.13 UDP 78 13020 → 13020 Len=36
But the sender messages do not show up on the "Ethernet" interface.
I do see the reply messages on the "Ethernet 2" interface:
105340 6836.393954 192.168.100.101 226.13.13.13 UDP 302 13021 → 13021 Len=260
But the readyRead signal is never generated so processPendingDatagrams() is never called.
I don't see any reply messages on the "Ethernet" interface due to the query messages never getting sent on that interface.
I did find something about using QNetworkDatagram::setInterfaceIndex() to select the interface that QUdpSocket::writedatagram() sends the datagram on. I modified my sendDatagram() method to use that but still only see the message on the "Ethernet 2" interface:
void Sender::sendDatagram() { QByteArray data = "? Query " + QByteArray::number(messageNo); QNetworkDatagram datagram(data, groupAddress, TX_PORT); QList<QNetworkInterface> list = QNetworkInterface::allInterfaces(); foreach (QNetworkInterface iface, list) { if (iface.flags().testFlag(QNetworkInterface::IsRunning) && iface.flags().testFlag(QNetworkInterface::CanMulticast) && !iface.flags().testFlag(QNetworkInterface::IsLoopBack)) { uint index = iface.index(); datagram.setInterfaceIndex(index); udpSocketTx.writeDatagram(datagram); qDebug() << index << datagram.interfaceIndex() << iface.humanReadableName(); } } qDebug() << data; ++messageNo; }
The qDebug() output is:
4 4 "Ethernet" 7 7 "VirtualBox Host-Only Network" 20 20 "Ethernet 2" "? Query 1"
So I have at least two problems still.
- The "query" messages are not being sent on all interfaces.
- The "reply" messages are not correctly being received (no readyRead signal generated).
Any help would be greatly appreciated.
-
OK. This is super annoying....
I have been trying to so this with Qt 5.12.4 since that is what I am most familiar with.
I just recompiled the same code with Qt 6.2.1 and it works.
I have spent the last week or more trying to get this to work.
Is this a known BUG with multicasting in Qt 5.12.4? Is there any way around it?
Regards
-
Now I'm really getting annoyed!
I have the stand-alone "sender" now working. It sends the query datagrams on all the interfaces on port 13020. Any target devices get that query datagram and respond with a reply datagram on the multicast port 13021. My "sender" receives those replies and handles them appropriately. Great!
I copied the QUdpSocket setup code from my stand-alone "sender" to my real application. I also copied the methods that send and receive the datagrams.
But my real application is only sending the query datagrams. I see the replies in WireShark but my application never generates the QUdpSocket::readyRead signal and I never receive the reply datagrams.
As far as I can tell everything is the same. Both are using Qt 6.2.1 and the socket stuff is identical. Ugh....
Good thing it's the end of the day. I'm about ready to toss Qt and just deal with Visual Studio and it's stupidness....
Maybe tomorrow I'll figure it out. Not getting much insight here....
Regards
-
I'm really stuck here. Any insight would really be appreciated.
Like I said above, my stand-alone app is working perfectly. But when I try to do it in my real app I never receive the reply datagrams (no QUdpSocket::readyRead signals).
My constructor in both creates a QList of all the interface transmit socket and a QList of all the interface receive sockets and joins them to the multicast group:
QUdpSocket::BindMode flags = QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint; // Create tx/rx sockets for all capable interfaces and join rx sockets to multicast group QList<QNetworkInterface> list = QNetworkInterface::allInterfaces(); foreach (QNetworkInterface iface, list) { if (iface.flags().testFlag(QNetworkInterface::IsRunning) && iface.flags().testFlag(QNetworkInterface::CanMulticast) && !iface.flags().testFlag(QNetworkInterface::IsLoopBack)) { QList<QNetworkAddressEntry> entries = iface.addressEntries(); foreach (QNetworkAddressEntry entry, entries) { if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) { QUdpSocket* socket = new QUdpSocket; socket->bind(QHostAddress(entry.ip()), MULTICAST_TX_PORT, flags); txSockets.append(socket); socket = new QUdpSocket; socket->bind(QHostAddress::AnyIPv4, MULTICAST_RX_PORT, flags); socket->joinMulticastGroup(groupAddress, iface); connect(socket, &QUdpSocket::readyRead, this, &Sender::processPendingDatagrams); rxSockets.append(socket); qDebug() << iface.humanReadableName() << entry.ip() << "joined multicast group" << groupAddress; } } } }
The "query" commands are then set on all the transmit sockets (once every second):
QByteArray data = "? Query " + QByteArray::number(messageNo); foreach (QUdpSocket* socket, txSockets) socket->writeDatagram(data, groupAddress, MULTICAST_TX_PORT); ++messageNo;
The QUdpSocket::readyRead signal then reads all the receive sockets for pending replies:
QByteArray datagram; foreach (QUdpSocket* socket, rxSockets) { while (socket->hasPendingDatagrams()) { int sz = socket->pendingDatagramSize(); datagram.resize(sz); socket->readDatagram(datagram.data(), datagram.size()); if (sz == sizeof(discovery)) { memcpy((void*)&discovery, datagram.constData(), sz); // handle the discovery response } } }
The only difference between the stand-alone app and my real app is how the QDialog widgets are constructed.
The stand-alone app creates all the widgets manually, like the example code.
My real app uses a ui file.Any ideas?
-
Ugh... Windows is so frustrating...
Ended up being a firewall issue on my app. I allowed it on the first run but Windows only allowed it on the "Public" network. I had to allow it on the "Domain" and "Private" networks for some reason.