简体   繁体   中英

How can I keep a multicast socket from receiving unicast data

Is there any way to prevent a socket joined to a multicast group from receiving unicast data directed at the same port?

A socket option or ioctl maybe? I saw https://msdn.microsoft.com/en-us/library/windows/desktop/ms738712%28v=vs.85%29.aspx but that seemed to only be for filtering which hosts could send you multicasts, not who could send unicasts at all.

I'm working with a Qt application, so if there's a way to do this from within Qt's socket framework then great, if not then reverting back to regular old berkley sockets is not a problem.

The only way I've found so far is quite hacky - creating a second UDP socket and binding it to the port before binding the multicast socket does work. I'm guessing there must be something better though I'm yet to find it.

It is even worse: You cannot even be sure that you receive only the multicast traffic directed at the address you joined. You may also receive multicast traffic from groups which other processes which run on the same machine joined, although they have nothing to do with your program.

The best (because most portable) solution is not to try to solve this with socket options, but to inspect the received packets to find out what they were targeted at and discard the ones you do not want to receive.

You can use setsockopt(IP_PKTINFO) and recvmsg() to enable receiving all kinds of useful meta-information on received UDP packets, including the usually very interesting destination IP address which in your case is a multicast address.

Bind the socket to the multicast address, add it to the multicast group, thus setting the correct interface. This approach has been tested on linux and OSX.

auto fd = socket(AF_INET, SOCK_DGRAM,0);

struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(MULTICAST_PORT);
inet_pton(AF_INET, INTERFACE_ADDRESS, &sa.sin_addr);

struct sockaddr_in multicast;
multicast.sin_family = AF_INET;
multicast.sin_port = htons(MULTICAST_PORT);
inet_pton(AF_INET, MULTICAST_ADDRESS, &multicast.sin_addr);

struct ip_mreqn req = {};
req.imr_ifindex = INTERFACE_INDEX;
req.imr_multiaddr = multicast.sin_addr;
req.imr_address = sa.sin_addr;

setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, sizeof(req))
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &req, sizeof(req))

bind(fd,(struct sockaddr*)&multicast, sizeof(multicast))

You can replace ip_mreqn by ip_mreq , it is more portable, and you don't need the interface index. But note that for ipv6 the interface index is required to join a multicast group.

Using Qt, from a quick look at https://doc.qt.io/qt-5/qtnetwork-multicastreceiver-receiver-cpp.html , I think that all you'd need to do is set the QHostAddress of the bind call to groupAddress4 . No guarantees as to which device the traffic will come from.

Your solution with another socket will not be portable, because AFAIK there is no defined way that unicast traffic on one port is dispatched to multiple sockets. (ie: might be round robin, might be first bound takes all...)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM