Добрый день!
Пишу класс DataReceiver, получающий UDP пакеты и передающий их в очередь для дальнейшей обработки. Получение реализовано с использованием асинхронных методов BeginReceive, EndReceive класса UdpClient.
public class DataReceiver : IDisposable
{
private UdpClient _client;
private bool _disposed;
public void Start()
{
_client.BeginReceive(ReceiveCallback, null);
}
public void Stop()
{
_client.Close();
}
private void ReceiveCallback(IAsyncResult ar)
{
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
try
{
byte[] data = _client.EndReceive(ar, ref remoteEP);
// Передача на обработку подписчику ...
_client.BeginReceive(ReceiveCallback, null);
}
catch (ObjectDisposedException)
{ /* Stopped? */ }
catch (SocketException)
{ /* Stopped? */ }
}
/* IDisposable Members implementation */
}
Корректна ли данная реализация? Как правильно остановить получение данных?
В приведенном коде меня смущает необходимость перехватывать ObjectDisposedException и SocketException внутри ReceiveCallback. Эти исключения возникают, если ReceiveCallback срабатывает в результате или после вызова _client.Close. Поэтому в принципе, видится нормальным их перехват: DataReceiver полагает, что остановлен и не делает новых запросов на получение пакетов. Однако SocketException может возникнуть и "штатно", DataReceiver в этом случае поведет себя неправильно: прекратит дальнейшее получение пакетов.
Фактически, здесь проблема использования ресурса из разных потоков. UdpClient используется из основного потока, вызывающего Start, Stop, и потоками из пула, в контексте которых срабатывает ReceiveCallback. Возможно, стоит ввести флаг "остановлен" и синхронизировать доступ к нему и UdpClient? А как быть с блокировкой, которая появится в реализации Dispose?