UDP: Read data from all network interfaces
Asked Answered
G

3

7

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
Gate answered 7/3, 2013 at 7:39 Comment(6)
What are your interfaces (ethernet, eth/wlan)? And what IP's do you have on these interfaces? It would be more helpful if you could analyse how you are sending your multicast message.Rolan
@Rolan : Thanks for you comment, I added some details on my initial question(Edit 4)Gate
And can you show us the output of your route print command?Rolan
@PCoder: Anything that could help, I'm really desesperate here(and I'm not a networking expert :( ). I edited the question with the resultGate
I agree that your problem is indeed tricky, and I too have no solution :(. I found a very similar problem to yours, perhaps that can be helpful. http://linux.derkeiler.com/Newsgroups/comp.os.linux.networking/2009-09/msg00223.htmlRolan
@Rolan : Thanks for the help anyway. I think that the c# equivalent is the SocketOPtion SocketOptionName.MulticastInterface, but it didn't changed anythingGate
G
6

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 ;)

Gate answered 8/3, 2013 at 9:57 Comment(0)
R
7

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.

Reformed answered 4/4, 2013 at 10:42 Comment(1)
Thnks, I found this helpfulDhar
G
6

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 ;)

Gate answered 8/3, 2013 at 9:57 Comment(0)
R
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.

Rhodic answered 7/3, 2013 at 10:16 Comment(6)
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?Gate
You have to iterate over the IP addresses of the local interfaces and join via each one of them.Rhodic
I think you didn't see my second code quote, it's exactly what I am doing.Gate
@Gate 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.Rhodic
Check my second code edit, I'm already iterating on all my local IPs. The difference is that now I'm using the Async methodsGate
@Gate 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.Rhodic

© 2022 - 2024 — McMap. All rights reserved.