Исключения и коды возврата NetworkStream
Я пытаюсь научить себя тонкостям чтения из NetworkStream и понять различные способы возникновения проблем. У меня есть следующий код:
public async Task ReceiveAll()
{
var ns = this.tcp.GetStream();
var readBuffer = new byte[1000];
while (true)
{
int bytesRead;
try
{
bytesRead = await ns.ReadAsync(readBuffer, 0, readBuffer.Length);
if (bytesRead == 0)
{
// Remote disconnection A?
break;
}
}
catch (IOException)
{
// Remote disconnection B?
break;
}
catch (ObjectDisposedException)
{
// Local disconnection?
break;
}
/*Do something with readBuffer */
}
}
Я отметил три точки в коде, где программа говорит: "что-то пошло не так, нет смысла продолжать".
"Локальное отключение" не является чем-то неправильным, оно произойдет, когда я локально закрою сокет, который является единственным способом выйти из цикла при нормальных обстоятельствах. Я не думаю, что что-то еще может вызвать это, поэтому я думаю, что я могу просто проглотить исключение.
В двух пунктах "Удаленное отключение" я не уверен. Я знаю, что ReadAsync вернет 0, если соединение будет прервано удаленно (A), но IOException также, похоже, срабатывает при некоторых обстоятельствах. Если мой удаленный клиент является консолью C#, то закрытие сокета, по-видимому, вызывает "A", а закрытие окна консоли - "B". Я не уверен, что понимаю, в чем разница между этими сценариями?
Наконец, немного общего вопроса, но есть ли что-то явно не так с этим фрагментом кода или моими предположениями?
Благодарю.
РЕДАКТИРОВАТЬ: В ответ на мое использование ObjectDisposedException для прерывания из цикла:
Вот как выглядит мой метод Stop (из того же класса, что и выше):
public void Stop()
{
this.tcp.Close();
}
Это приводит к тому, что ожидающий ReadAsync исключает исключение ObjectDisposedException. AFAIK, нет другого способа прервать это. Меняя это на:
public void Stop()
{
this.tcp.Client.Shutdown(SocketShutdown.Both);
}
Похоже, что на самом деле ничего не делает с ожидающим вызовом, он просто продолжает ждать.
1 ответ
Когда NetworkStream
возвращает 0, это означает, что сокет получил пакет отключения от удаленной стороны. Так должны заканчиваться сетевые подключения.
Правильный способ завершить соединение (особенно если вы находитесь в дуплексном режиме) - это позвонить socket.Shutdown(SocketShutdown.Send)
и дайте удаленной стороне некоторое время, чтобы закрыть свой канал отправки. Это гарантирует, что вы получите все ожидающие данные вместо того, чтобы закрыть соединение. ObjectDisposedException
никогда не должен быть частью нормального потока приложения.
Любые исключения указывают на то, что что-то пошло не так, и я думаю, можно с уверенностью сказать, что вы больше не можете полагаться на текущее соединение.
TL; DR
Я не вижу ничего плохого в вашем коде, но (особенно в полнодуплексной связи) я бы закрыл канал отправки и дождался 0-байтового пакета, чтобы предотвратить прием ObjectDisposedException
по умолчанию:
использование
tcp.Shutdown(SocketShutdown.Send)
сказать удаленной стороне, что вы хотите отключитьваш цикл все еще может получать данные, которые отправляла удаленная сторона
ваш цикл будет, если все прошло правильно, затем получит 0-байтовый пакет, указывающий, что удаленная сторона отключается
цикл завершается в правильном направлении
вы можете решить утилизировать сокет через определенное время, если вы не получили 0-байтовый пакет