UDP: Read data from all network interfaces

11,330

Solution 1

I finally found how to do it!

In fact, if I keep exactly the same code, but using it with async methods, it work!!! I just can't understand why it doesn't work with sync method(if someone knows, you're welcome to tell me :) )

Since I've lost 3 days on this, I think it worth an example:

private static void ReceiveAsync(int port, string address, IEnumerable<IPAddress> localAddresses)
{
    IPAddress multicastAddress = IPAddress.Parse(address);
    foreach (IPAddress localAddress in localAddresses)
    {
        var udpClient = new UdpClient(AddressFamily.InterNetwork);
        udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        udpClient.Client.Bind(new IPEndPoint(localAddress, port));
        udpClient.JoinMulticastGroup(multicastAddress, localAddress);
        udpClient.BeginReceive(OnReceiveSink,
                               new object[]
                                   {
                                       udpClient, new IPEndPoint(localAddress, ((IPEndPoint) udpClient.Client.LocalEndPoint).Port)
                                   });
    }
}

And the async method:

private static void OnReceiveSink(IAsyncResult result)
{
    IPEndPoint ep = null;
    var args = (object[]) result.AsyncState;
    var session = (UdpClient) args[0];
    var local = (IPEndPoint) args[1];

    byte[] buffer = session.EndReceive(result, ref ep);
    //Do what you want here with the data of the buffer

    Console.WriteLine("Message received from " + ep + " to " + local);

    //We make the next call to the begin receive
    session.BeginReceive(OnReceiveSink, args);
}

I hope that helps ;)

Solution 2

I had the same problem that I wanted to receive multicasts from all my network interfaces. As EJP already said, you need to call JoinMulticastGroup(IPAddress multicastAddr, IPAddress localAddress) on the UdpClient for all network interfaces:

int port = 1036;
IPAddress multicastAddress = IPAddress.Parse("239.192.1.12");

client = new UdpClient(new IPEndPoint(IPAddress.Any, port));

// list of UdpClients to send multicasts
List<UdpClient> sendClients = new List<UdpClient>();

// join multicast group on all available network interfaces
NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();

foreach (NetworkInterface networkInterface in networkInterfaces)
{
    if ((!networkInterface.Supports(NetworkInterfaceComponent.IPv4)) ||
        (networkInterface.OperationalStatus != OperationalStatus.Up))
    {
        continue;
    }

    IPInterfaceProperties adapterProperties = networkInterface.GetIPProperties();
    UnicastIPAddressInformationCollection unicastIPAddresses = adapterProperties.UnicastAddresses;
    IPAddress ipAddress = null;

    foreach (UnicastIPAddressInformation unicastIPAddress in unicastIPAddresses)
    {
        if (unicastIPAddress.Address.AddressFamily != AddressFamily.InterNetwork)
        {
            continue;
        }

        ipAddress = unicastIPAddress.Address;
        break;
    }

    if (ipAddress == null)
    {
        continue;
    }

    client.JoinMulticastGroup(multicastAddress, ipAddress);

    UdpClient sendClient = new UdpClient(new IPEndPoint(ipAddress, port));
    sendClients.Add(sendClient);
}

I am also creating a list of UdpClients so I can send my multicasts on all network interfaces.

Solution 3

You need to join the multicast group via all available interfaces. By default, the outgoing IGMP JOIN message will be routed according to the unicast routing tables, which will send it out via the 'cheapest' route, using whichever NIC accesses that route. If your multicast group can be sourced via more than one of those routes, you need to iterate.

Share:
11,330
J4N
Author by

J4N

Updated on June 11, 2022

Comments

  • J4N
    J4N almost 2 years

    I've the following code to read multicast message coming from the network, for a specified IP+Port

    private static void ReceiveMessages(int port, string ip, CancellationToken token)
    {
        Task.Factory.StartNew(() =>
            {
                using (var mUdpClientReceiver = new UdpClient())
                {
                    var mReceivingEndPoint = new IPEndPoint(IPAddress.Any, port);
                    mUdpClientReceiver.ExclusiveAddressUse = false;
                    mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                    mUdpClientReceiver.ExclusiveAddressUse = false;
                    mUdpClientReceiver.Client.Bind(mReceivingEndPoint);
                    mUdpClientReceiver.JoinMulticastGroup(IPAddress.Parse(ip), 255);
    
                    while (!token.IsCancellationRequested)
                    {
                        byte[] receive = mUdpClientReceiver.Receive(ref mReceivingEndPoint);
    
                        Console.WriteLine("Message received from {0} ",mReceivingEndPoint);
                    }
                }
            });
    }
    

    I've two network adapter from which I've data coming on this multicast ip+port(confirmed by two instances of wireshark monitoring each network adapter). I see on wireshark a lot of traffic coming on those port+Ip) for both network cards.

    The problem is that on my console, I only see messages coming from one network card.

    I double checked with netstat, I don't have any other software listening on my port: enter image description here

    So why am I getting traffic from only one of my two network cards?

    EDIT:

    I even tried the following:

    private static void ReceiveMessages(int port, string ip, CancellationToken token, IEnumerable<IPAddress> ipAddresses)
    {
        foreach (IPAddress ipAddress in ipAddresses)
        {
            IPAddress ipToUse = ipAddress;
            Task.Factory.StartNew(() =>
            {
                using (var mUdpClientReceiver = new UdpClient())
                {
    
                    var mReceivingEndPoint = new IPEndPoint(ipToUse, port);
                    mUdpClientReceiver.ExclusiveAddressUse = false;
                    mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                    mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
                    mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
                    mUdpClientReceiver.ExclusiveAddressUse = false;
                    mUdpClientReceiver.Client.Bind(mReceivingEndPoint);
                    mUdpClientReceiver.JoinMulticastGroup(IPAddress.Parse(ip), 255);
                    Console.WriteLine("Starting to listen on "+ipToUse);
                    while (!token.IsCancellationRequested)
                    {
                        byte[] receive = mUdpClientReceiver.Receive(ref mReceivingEndPoint);
    
                        Console.WriteLine("Message received from {0} on {1}",  mReceivingEndPoint,ipToUse);
                    }
                }
            });
        }
    }
    

    I see the "Starting to listen on theCorrectIP" twice(for my two IPs), but it still display only data coming from one network card.

    EDIT 2

    I did notice something else that is strange too. If I disable the interface on which I receive all data, and then start the software, I now get data from the other interface. If I activate again the interface and restart the software, I still get the traffic on the non-deactivated card.

    And I know for sure that I've devices that respond to me, that are connected only to one network(not both)

    EDIT 3

    Another thing: if I send a message from me(localhost), on all network card that I've, I see them coming on my two network interfaces. BUT, if I start my program twice, only the first programm get messages, not the second one.

    Edit 4

    Additional info, following the first comment: I've two ethernet cards, one with the 10.10.24.78 ip, the other with the 10.9.10.234 ip. It's not me that send data, but network pieces(the port 5353 with this ip is a know multicast address used for mDNS, so I should receive traffic from things like printer, itunes, macs, and some other pieces of software we created). Data are multicasted on the ip 224.0.0.251 and port 5353.

    Here is a code that you could use to send data on severals IPs, but like I described, if you start it in local it almost works(except that only one local client receive the message).

    private static void SendManuallyOnAllCards(int port, string multicastAddress, IEnumerable<IPAddress> ipAddresses)
    {
        foreach (IPAddress remoteAddress in ipAddresses)
        {
            IPAddress ipToUse = remoteAddress;
            using (var mSendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
            {
                mSendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership,
                                            new MulticastOption(IPAddress.Parse(multicastAddress)));
                mSendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 255);
                mSendSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    
                var ipep = new IPEndPoint(ipToUse, port);
                //IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(multicastAddress), port);
                mSendSocket.Bind(ipep);
                mSendSocket.Connect(ipep);
    
    
                byte[] bytes = Encoding.ASCII.GetBytes("This is my welcome message");
                mSendSocket.Send(bytes, bytes.Length, SocketFlags.None);
            }
        }
    }
    

    EDIT 5 Here is the result of my route print(Didn't know that command), and on my two IPs, I always receive data on the 10.9.10.234 enter image description here

    Edit 6

    I tried several other things:

    1. Use a socket to receive instead of the UdpClient --> Didn't worked
    2. Set some addition socketOption on the reader(DontRoute =1, Broadcast=1) -->Didn't worked
    3. Specify the MulticastInterface that the reader Socket has to use(using socketOption MulticastInterface) --> Didn't work
  • J4N
    J4N about 11 years
    I'm not sure to understand, the mUdpClientReceiver.JoinMulticastGroup(IPAddress.Parse(ip), 255); which is done for all UdpClient is doing that, no? If no, how should I do this?
  • user207421
    user207421 about 11 years
    You have to iterate over the IP addresses of the local interfaces and join via each one of them.
  • J4N
    J4N about 11 years
    I think you didn't see my second code quote, it's exactly what I am doing.
  • user207421
    user207421 about 11 years
    @J4N Come off it. It's exactly what you are now doing, in the code in your answer, which was posted several hours after I posted this. The key is to iterate over the local IP addresses and use the form of JoinMulticastGroup() that takes a local address. You weren't and aren't, doing that in the code in your question, which is what I am answering.
  • J4N
    J4N about 11 years
    Check my second code edit, I'm already iterating on all my local IPs. The difference is that now I'm using the Async methods
  • user207421
    user207421 about 11 years
    @J4N No, the difference is that you are now doing what I told you to do in the first place, joining the multicast group via all addresses of all local interfaces, by using the form of JoinMulticastGroup() I mentioned above, that takes a local address as an argument. There is no other way to do that. Your second piece of code doesn't do that, and so doesn't constitute already doing what I said. Using the async API has nothing to do with it. You can't seriously believe that multi-homed multicast didn't work until the Async API came along. I was doing it ten years ago.
  • CybeX
    CybeX over 8 years
    Thnks, I found this helpful