Click here to Skip to main content
15,916,951 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have written a program that is receiving multicast udp. It is largely working but when there are several packets arriving in quick succession, I get some "duplicates" and miss some entirely.

I have run Wireshark and the machine is receiving each packet only once, and is receiving all of them. So for example if the machine receives packets 1 2 3 4 5 6 7 8 9 10 in quick succession, then by the data I am extracting in my code, I am receiving packets 3 4 5 5 5 5 9 10 10 10. So I am getting the right number of calls to my callback function, but either I am doing something stupid or the buffer is giving me the wrong datagram.

This is C#, VS 2017, DotNet 4.6.1.

I've written a simplified test app which has the same results:

C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TestLWMulticastGPIO
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        delegate void WriteToListboxCallback(string text);
        private void WriteToListbox(string text)
        {
            if (this.listBox1.InvokeRequired)
            {
                WriteToListboxCallback d = new WriteToListboxCallback(WriteToListbox);
                this.Invoke(d, new object[] { text });
            }
            else
                this.listBox1.Items.Add(text);
        }


        public static readonly System.Net.IPAddress DefaultMulticastGroup = System.Net.IPAddress.Parse(***MULTICASTADDRESS***);
        private System.Net.Sockets.UdpClient _MulticastClient;

        private void Form1_Load(object sender, EventArgs e)
        {
            initGPIOClient(ref _MulticastClient, ***MULTICASTPORT***);
        }

        private void initGPIOClient(ref System.Net.Sockets.UdpClient clnt, int port)
        {
            IPEndPoint mCastLocalIP = new IPEndPoint(IPAddress.Any, port);
            clnt = new System.Net.Sockets.UdpClient();

            clnt.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            clnt.Client.Bind(mCastLocalIP);

            // 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;
                }

                clnt.JoinMulticastGroup(DefaultMulticastGroup, ipAddress);
                clnt.BeginReceive(ReceiveMulticastCallback, clnt);
            }
        }

        bool ReceiveBusy = false;
        private void ReceiveMulticastCallback(IAsyncResult ar)
        {
            WriteToListbox("Data Received");

            while (ReceiveBusy)
                Thread.Sleep(1);
            ReceiveBusy = true;

            WriteToListbox("  Data Being Processed...");

            UdpClient udp = (UdpClient)ar.AsyncState;
            if (udp.Client == null)
                return;

            IPEndPoint remoteEp = null;

            byte[] data = udp.EndReceive(ar, ref remoteEp);

            WriteToListbox("    Received Message " + BitConverter.ToString(data));

            udp.BeginReceive(new AsyncCallback(ReceiveMulticastCallback), udp);

            ReceiveBusy = false;
        }
    }
}


Can anyone see what I am doing wrong? It's like I am overwriting a variable with a 2nd thread before I have processed the first. That was what ReceiveBusy was supposed to stop, but (a) not sure it is needed as the variables are local ones and not static and (b) I'm not sure it would achieve what I want anyway?

Forgive me, I am relatively novice at all of this.

All help appreciated

Chris

What I have tried:

I've googled for days, and I have rewritten the basic code above to take out the complexity of my main code. Nothing I do seems to make any difference (or sense).
Posted
Updated 3-Feb-19 14:46pm
v3
Comments
Richard Deeming 4-Feb-19 11:47am    
I'd be inclined to move the BeginReceive call outside of the loop, just in case you have multiple matching interfaces.

Since you're using a recent version of .NET, I'd also be inclined to use ReceiveAsync[^] instead of BeginReceive, and use an async method to process the received data. Eg:
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private Task _receiveTask;

private void Form1_Load(object sender, EventArgs e)
{
    initGPIOClient(***MULTICASTPORT***);
}

private void Form1_Closed(object sender, FormClosedEventArgs e)
{
    _cancellationTokenSource.Cancel();
    if (_receiveTask != null)
    {
        _receiveTask.Wait();
    }
}

private void initGPIOClient(int port)
{
    IPEndPoint mCastLocalIP = new IPEndPoint(IPAddress.Any, port);
    UdpClient clnt = UdpClient();

    ...

    _receiveTask = ReceiveMulticastData(clnt, _cancellationTokenSource.Token);
}

private async Task ReceiveMulticastData(UdpClient client, CancellationToken cancellationToken)
{
    while (!cancellationToken.IsCancellationRequested)
    {
        UdpReceiveResult result = await client.ReceiveAsync();
        this.listBox1.Items.Add("    Received Message " + BitConverter.ToString(result.Buffer));
    }
}
Chris Voce 5-Feb-19 3:35am    
Thanks. It is in the loop because there may well be more than 1 interface and I want to listen on all of them. I'll try the async method though, it's certainly worth a try! Thanks.
Chris Voce 6-Feb-19 17:26pm    
Hi. I tried the async method today and get the same result. It just makes no sense to me. Thanks for the suggestion though

1 solution

In my experience, when data starts arriving, you have to determine when you have a complete "message".

You're simply reading along and assuming every read is a new message, when in fact it maybe only part of the message.

Somewhere along the line is the "total message length" you need to capture, and the "parts" as you're reading them. Put them all together and you get a complete message (before you can "tag" the next message).

(Or it may say that there are 0-N bytes left to read).
 
Share this answer
 
v2
Comments
Chris Voce 4-Feb-19 1:33am    
Thanks for the reply. They are very small payloads though, and Wireshark is showing each payload arriving in a single packet. Also my callback is only getting called the 10 times (in the example above). So I don't think it is that. I'm getting exactly the same array passed back to me each time.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900