简体   繁体   中英

UDP: Listening to the same port for two different multicast streams

I need to listen to 2 different multicast groups using the same port. Program A will listen from 230.0.0.1 and Program B from 230.0.0.2 . Both multicast groups use the same port 2000 and I have no control over it.

When I run my programs I receive both multicast streams in each program, that is both the data packets broadcasted on 230.0.0.1 and 230.0.0.2 . I suspect the problem is due to the common port. This is the code I am using to subscribe to the multicast:

if( (sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 ) {
  perror("socket");
  return -1;
}

if( setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0 ) {
  perror("setsockopt SO_REUSEADDR");
  return -1;
}

memset(&in_addr, 0, sizeof(in_addr));
in_addr.sin_family = AF_INET;
in_addr.sin_addr.s_addr = htonl(INADDR_ANY);
in_addr.sin_port = htons(2000);
if( bind(sd, (struct sockaddr*)&in_addr, sizeof(in_addr)) < 0 ) {
  perror("bind");
  return -1;
}

memset(&req, 0, sizeof(req));
inet_aton(intfc_ip, &req.imr_interface);
inet_aton("230.0.0.1", &req.imr_multiaddr);
if( setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, sizeof(req)) < 0 ) {
  perror("setsockopt IP_ADD_MEMBERSHIP");
  return -1;
}

recv()...

How can I filter a specific multicast group in each program?

If you change

in_addr.sin_addr.s_addr = htonl(INADDR_ANY);

to

inet_aton(<your wanted IP address>, &in_addr.sin_addr.s_addr);

you could have more success.

(And if you change your program to work with getaddrinfo() , you make it future-proof.)

"connect" might be what you need after all. Typically, for connecting TCP sockets, the man page also suggests that it can be used for filtering out UDP packets from other addresses:

From the man page posted here :

If the socket sockfd is of type SOCK_DGRAM then addr is the address to which datagrams are sent by default, and the only address from which datagrams are received.

The problem with socket code is that "recvfrom" will only give you the source address from where the packet was sent from. It doesn't tell you the IP address of where the packet was sent to. You want to be able to inspect the destination address of the UDP packet so you can filter out packets that were sent to multicast IP addresses you are not interested in.

There is a socket option you can set followed by the use of "recvmsg" instead of recv or recvfrom to get the destination IP adddress the packet was sent to.

1) Use setsockopt with IP_PKTINFO to enable getting the destination IP address passed up to the app level for data received on the socket.

int enable = 1;
setsockopt(sock, IPPROTO_IP , IP_PKTINFO , &enable, sizeof(enable));

2) Use recvmsg instead of recvfrom (or recv) to get the destination address the UDP packet was sent to. I have a helper function called "recvfromex" that wraps recvmsg and mirrors the functionality of recvfrom - expect it has an extra parameter for the caller to get the destination IP of the packet.

It's a bit windy to post - but you can look at my C++ code from my github project and take what you need.

Look at the recvfromex function here

More code sample for the setsockopt call here (look for the function "EnablePktInfo" on how to use the setsockopt call with IP_PKTINFO). Also contains extensions for IPV6 and BSD.

(from answer of Receving multiple multicast feeds on the same port - C, Linux )

The ip(7) manpage describe a possible solution :

IP_MULTICAST_ALL (since Linux 2.6.31)
This option can be used to modify the delivery policy of multicast messages to sockets bound to the wildcard INADDR_ANY address. The argument is a boolean integer (defaults to 1). If set to 1, the socket will receive messages from all the groups that have been joined globally on the whole system. Otherwise, it will deliver messages only from the groups that have been explicitly joined (for example via the IP_ADD_MEMBERSHIP option) on this particular socket.

Then you can activate the filter to receive messages of joined groups using :

int mc_all = 0;
if ((setsockopt(sock, IPPROTO_IP, IP_MULTICAST_ALL, (void*) &mc_all, sizeof(mc_all))) < 0) {
    perror("setsockopt() failed");
}

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