Как использовать асинхронный прием для UdpClient в цикле?
Я строю многопоточное приложение. Один из потоков отвечает за обработку UDP-сообщений, поступающих от внешнего оборудования. В настоящее время я использую метод UdpClient.Receive в цикле. Я считаю, что метод Receive блокирует поток в ожидании следующего сообщения. У меня есть общая переменная (_isCancelled), которую я использую, если хочу закрыть все потоки и корректно остановить приложение. Однако, поскольку этот метод получения блокирует, я не могу корректно завершить работу приложения, если связь с внешним компонентом была потеряна. Вот текущий цикл кода
UdpClient _client = new UdpClient(8000);
IPEndPoint _endPoint = new IPEndPoint(IPAddress.Any, 8000);
while (!_isCancelled)
{
byte[] _bytes = _client.Receive(ref _endPoint);
...process data...
}
Я думаю, что вместо этого я должен использовать асинхронные методы получения. Но я не совсем уверен, что понимаю, как это работает и как я должен структурировать свой код. Я хочу иметь возможность непрерывно получать данные и обрабатывать их. И в любое время я хочу иметь возможность корректно закрыть поток, установив для _isCancelled значение true. И если по какой-то причине мы потеряем связь с внешним оборудованием или не получим больше сообщений, мы все равно сможем изящно закрыть поток.
3 ответа
Для более новых методов, использующих TAP вместо метода Begin/End, вы можете использовать следующее в.Net 4.5
Достаточно просто!
Асинхронный метод
private static void UDPListener()
{
Task.Run(async () =>
{
using (var udpClient = new UdpClient(11000))
{
string loggingEvent = "";
while (true)
{
//IPEndPoint object will allow us to read datagrams sent from any source.
var receivedResults = await udpClient.ReceiveAsync();
loggingEvent += Encoding.ASCII.GetString(receivedResults.Buffer);
}
}
});
}
Синхронный метод
Как приписать к asynchronous
метод выше, это также может быть реализовано в synchronous
метод очень похожим образом:
private static void UDPListener()
{
Task.Run(() =>
{
using (var udpClient = new UdpClient(11000))
{
string loggingEvent = "";
while (true)
{
//IPEndPoint object will allow us to read datagrams sent from any source.
var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
var receivedResults = udpClient.Receive(ref remoteEndPoint);
loggingEvent += Encoding.ASCII.GetString(receivedResults);
}
}
});
}
Я добавил некоторый код, чтобы продемонстрировать, как вы можете реализовать цикл с помощью BeginReceive/EndReceive. Это показывает вам урезанную версию, которую вы можете легко изменить или реализовать:
UdpClient local;
bool stop;
public void StartReceiving()
{
local = new UdpClient(serverIP);
Receive(); // initial start of our "loop"
}
public void StopReceiving()
{
stop = true;
local.Client.Close();
}
private void Receive()
{
local.BeginReceive(new AsyncCallback(MyReceiveCallback), null);
}
private void MyReceiveCallback(IAsyncResult result)
{
IPEndPoint ip = new IPEndPoint(IPAddress.Any, 0);
local.EndReceive(result, ref ip);
if (!stop)
{
Receive(); // <-- this will be our loop
}
}
Повеселись
Другой подход с некоторой поддержкой отмены. Я предпочитаю рекурсивное чувство while(true)
петля.
private async void Listen()
{
var resp = await _udpClient.ReceiveAsync().ConfigureAwait(false);
var eventHandler = PacketReceived;
if (eventHandler != null)
eventHandler(this, new UdpPacketReceivedEventArgs(resp));
if (_running)
Listen();
}