Получение неправильной дейтаграммы с помощью udpclient.endreceive()
Я написал программу, которая принимает многоадресный udp. Это в основном работает, но когда есть несколько пакетов, прибывающих в быстрой последовательности, я получаю некоторые "дубликаты" и пропускаю некоторые полностью.
Я запустил Wireshark, и машина получает каждый пакет только один раз, и получает их все. Так например если машина получает пакеты 1 2 3 4 5 6 7 8 9 10 в быстрой последовательности, а затем по данным, которые я извлекаю в своем коде, я получаю пакеты 3 4 5 5 5 5 9 10 10 10. Таким образом, я получаю правильное количество вызовов моей функции обратного вызова, но либо я делаю что-то глупое, либо буфер дает мне неправильную дейтаграмму.
Это C#, VS 2017, DotNet 4.6.1.
Я написал упрощенное тестовое приложение, которое имеет те же результаты:
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; } } }
Может ли кто-нибудь увидеть, что я делаю неправильно? Это похоже на то, как я перезаписываю переменную со 2-м потоком, прежде чем я обработал первый. Это было то, что ReceiveBusy должен был остановить, но (а) не уверен, что это необходимо, так как переменные являются локальными, а не статическими, и (б) я не уверен, что он достигнет того, чего я хочу в любом случае?
Простите меня, я относительно новичок во всем этом.
Вся помощь оценена по достоинству
Крис
Что я уже пробовал:
Я гуглил в течение нескольких дней, и я переписал базовый код выше, чтобы убрать сложность моего основного кода. Ничто из того, что я делаю, кажется, не имеет никакого значения (или смысла).
Richard Deeming
Я был бы склонен переместить BeginReceive
вызов вне цикла, просто на случай, если у вас есть несколько совпадающих интерфейсов.
Поскольку вы используете последнюю версию .NET, я также был бы склонен использовать ReceiveAsync[^] вместо BeginReceive
, и использовать async
способ обработки полученных данных. Напр.:
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
Спасибо. Он находится в цикле, потому что там вполне может быть более 1 интерфейса, и я хочу слушать их все. Я попробую асинхронный метод, хотя, конечно, стоит попробовать! Спасибо.
Chris Voce
Привет. Сегодня я попробовал асинхронный метод и получил тот же результат. Для меня это просто не имеет смысла. Хотя Спасибо за предложение