В книге Албахари "C# 6.0 in a Nutshell" написано следующее:
В действительности класс StreamReader
абсолютно запрещено применять
вместе с NetworkStream
, даже если вы планируете вызывать только
метод ReadLine
. Причина в том, что класс StreamReader имеет буфер
опережающего чтения, который может привести к чтению большего
числа байтов, чем доступно в текущий момент, и бесконечному блокированию
(или до возникновения тайм-аута сокета). Другие потоки, такие
как FileStream
, не страдают подобной несовместимостью с классом
StreamReader
, потому что они поддерживают определенный признак
окончания, при достижении которого метод Read
немедленно завершается,
возвращая значение 0
.
Какая ошибка, например, в этом классе?
public class AsyncSocket : IDisposable
{
Socket socket;
StreamReader sr;
public AsyncSocket()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sr = new StreamReader(new NetworkStream(socket));
}
// ... остальные члены убраны
public async Task<string> ReadLineAsync()
{
return await sr.ReadLineAsync();
}
public void Dispose()
{
if (sr != null) sr.Close();
if (socket != null) socket.Dispose();
}
}
Допустим, пришла строка "123\r\nqwe". StreamReader
считает всю её в свой внутренний буфер при вызове ReadLineAsync()
вернет "123". Данные из внутреннего буфера не удаляются же? При втором вызове ReadLineAsync()
завершится, когда придет конец строки и в результатом вернется "qwe".
Албахари пишет про бесконечное блокирование, так к нему может привести и обычный метод Read
, если никакие данные не будут отправлены сокету.
Объясните почему нельзя использовать класс StreamReader
вместе с NetworkStream
?
"Буфер опережающего чтения" означает, что StreamReader
попытается вычитать из потока сразу килобайт (bufferSize
) данных. "Про запас". Это улучшает производительность в случае чтения с диска, но может приводить к непредсказуемым последствиям при чтении.
Особенность NetworkStream.Read
в том, что он может блокироваться при чтении (в случае, если сокет еще открыт, но данных нет).
Албахари пишет о теоретической ситуации, когда
StreamReader
мог бы вернуть вам данные (т.к. они есть в буфере), NetworkStream.Read
NetworkStream.Read
заблокировалсяИ вы оказываетесь в ситуации, когда вроде как нужные вам данные пришли, но StreamReader
висит и не возвращает ничего, т.к. хочет еще больше данных из потока.
На практике (в текущей реализации StreamReader
и NetworkStream
) такая ситуация не возникает, т.к.
StreamReader
читает данные из входного потока только в случае, если в буфере недостаточно данных для завершения требуемой операции.NetworkStream.Read
не блокируется, если хоть какие-то данные есть.Т.е. блокировка произойдет только в случае, когда нужные данные действительно не пришли.
Но реализация — это одно, нет никаких гарантий что она не поменяется.
На самом деле проблема решается тем, что сочетание NetworkStream
+ StreamReader
не имеет практической ценности. StreamReader
приспособлен для работы с чисто текстовыми данными. Например, он пытается искать в читаемом потоке преамбулу (BOM), и вообще — использует сами данные для определения концов строк, что уже дико неудобно при работе с сетью.
По сети обычно передаются смешанные данные. Например, те же строки принято передавать в виде <длина><текст>, чтобы не попадать в ловушку "непонятно сколько данных вычитали и сколько еще ждать". Для такой работы со стримами удобнее использовать BinaryReader
.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Использую стандартный treeViewУзлы добавил через свойства компонента Nodes -> Edit Nodes
Как проверить сколько TCP соединений может нормально держать сервер? Как проверить производительность TCP сервера?
Подскажите как изменить внешний вид шапки с наименьшими затратами, чтобы у нее поставить свой цвет и чтобы у нее был плоский вид
Я задавал похожий а вопрос, но я спрашивал про компиляцию c++ вместе с c#Немного разобравшись стало понятно, что компилирование двух кодов просто...