Как использовать асинхронный прием для 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();
}
Другие вопросы по тегам