Чтение из NetworkStream. Что лучше ReadLine() по сравнению с чтением в ByteArray?

Я использую TcpClient для связи с сервером, который отправляет информацию в виде строк с разделителями "\n". Поток данных довольно высок, и как только канал установлен, в потоке всегда будет информация для чтения. Сообщения могут иметь переменный размер.

Теперь мой вопрос: будет ли лучше использовать метод ReadLine() для чтения сообщений из потока, поскольку они уже разделены "\n", или будет целесообразно читать byteArray некоторого фиксированного размера и извлекать строки сообщений из их с помощью Split("\n") или что-то в этом роде? (Да, я понимаю, что могут быть случаи, когда в байтовый массив попадает только часть сообщения, и для этого тоже пришлось бы реализовывать логику.)

Здесь необходимо учитывать следующие моменты:

  • Представление.

  • Потери данных. Будут ли некоторые данные потеряны, если клиент читает не так быстро, как поступают данные?

  • Многопоточная настройка. Что, если эту настройку необходимо реализовать в многопоточной среде, где каждый поток будет иметь отдельный канал связи, но будет совместно использовать одни и те же ресурсы на клиенте.

person Danish Khan    schedule 03.12.2010    source источник


Ответы (4)


Если вас больше всего беспокоит производительность, я бы предпочел метод Read, а не ReadLine. Ввод-вывод — это одна из самых медленных операций, которую может выполнять программа, поэтому вы хотите свести к минимуму количество времени, затрачиваемое на подпрограммы ввода-вывода, предварительно прочитав как можно больше данных.

Потеря данных на самом деле не проблема, если вы используете TCP. Протокол TCP гарантирует доставку и устраняет проблемы с перегрузкой, которые приводят к потере пакетов.

Для потоковой части вопроса нам понадобится немного больше информации. Какие ресурсы являются общими, разделяют ли они TcpClient и т. д. ...

person JaredPar    schedule 03.12.2010
comment
Спасибо за помощь. Да, производительность является серьезной проблемой, но как вы думаете, чтение в byteArray, преобразование в строку, проверка части сообщения и разбиение сообщения не будут дополнительными накладными расходами и по-прежнему будут работать лучше? - person Danish Khan; 03.12.2010
comment
С общими ресурсами проблема в основном связана с объемом памяти на клиентской машине. - person Danish Khan; 03.12.2010
comment
@Danish, небольшие манипуляции со строками почти наверняка быстрее, чем несколько вызовов ввода-вывода. - person JaredPar; 03.12.2010

Я бы посоветовал использовать пул буферов и выполнять чтение вручную (Read() в сокете), если вам нужна высокая производительность. Объединение буферов в пул позволит избежать генерации мусора, поскольку я полагаю, что ReadLine() будет генерировать некоторые из них.

Поскольку вы используете TCP, потеря данных не должна быть проблемой.

В многопоточной настройке вы должны быть конкретными, но в целом совместное использование ресурсов проблематично, поскольку может привести к гонке данных.

person Kel    schedule 03.12.2010
comment
Спасибо за ваше предложение, однако я немного не понимаю, как вы рекомендуете использовать пул буферов, не могли бы вы рассказать об этом немного подробнее, пример фрагмента кода был бы просто отличным ... еще раз спасибо. - person Danish Khan; 03.12.2010

Почему бы не использовать BufferedStream, чтобы обеспечить оптимальное чтение из потока:

var req = (HttpWebRequest)WebRequest.Create("http://www.stackoverflow.com");
using(var resp = (HttpWebResponse)req.GetResponse())
using(var stream = resp.GetResponseStream())
using(var bufferedStream = new BufferedStream(stream))
using(var streamReader = new StreamReader(bufferedStream))
{
    while(!streamReader.EndOfStream)
    {
        string currentLine = streamReader.ReadLine();
        Console.WriteLine(currentLine);
    }
}

Конечно, если вы хотите масштабироваться, переход на асинхронность будет необходим. Таким образом, ReadLine не может быть и речи, и вы вернулись к манипуляциям с массивом байтов.

person spender    schedule 03.12.2010
comment
Я не думаю, что использование BufferedStream удовлетворит мои требования. Как я упоминал ранее, сетевой поток будет иметь большой поток входящих пакетов данных, и он не завершится, если клиент не отправит запрос на завершение. Использование BufferedStream означало бы бесконечную блокировку, чтобы сначала прочитать все входящие данные в него, что здесь невозможно. - person Danish Khan; 03.12.2010
comment
Насколько я понимаю, BufferedStream просто гарантирует, что чтение входящих данных происходит фрагментами размером с буфер, а не со скоростью, с которой вы его запрашиваете. Например, если вы читаете данные из потока в размерах, меньших, чем размер буфера потока, то вы читаете неэффективно. Buffered stream исправляет это, выравнивая операции чтения по размеру буфера. Таким образом, он никогда не будет хранить намного больше данных, чем стоит буфер, но сделает ваше чтение более эффективным. Он не будет буферизовать весь поток. - person spender; 03.12.2010
comment
Ну, даже у меня были подобные мысли, но когда я попытался реализовать это, задав размер буфера, это привело к ошибке, поиск не поддерживается потоком. Если я не устанавливал размер буфера, он ждал бесконечно. - person Danish Khan; 03.12.2010

Я бы прочитал массив байтов.... единственный недостаток: ограниченный размер. Вам нужно знать определенный предел количества байтов или иногда сбрасывать массив байтов в коллекцию байтов вручную, а затем преобразовывать коллекцию обратно в массив байтов, также преобразовывая ее в строку с помощью bitConverter и, наконец, разделяя ее на настоящие сообщения :p

У вас будет много накладных расходов на сброс массива в коллекцию... НО для сброса в строку потребуется больше ресурсов, так как байты должны быть декодированы, КАК вы их сбрасываете в строку.... так что это зависит от вас. ... вы можете выбрать простоту со строкой или эффективность с байтами, в любом случае это не будет серьезным повышением производительности друг от друга, но я бы лично выбрал коллекцию байтов, чтобы избежать неявного преобразования байтов.

Важно: это исходит из личного опыта предыдущего использования сокетов TCP (материал Quake RCON), а не из какой-то книги или чего-то еще :) Поправьте меня, если я ошибаюсь, пожалуйста.

person Machinarius    schedule 03.12.2010
comment
Я думаю, я бы прочитал массив байтов размером 2048, преобразовал его в строку, а затем разделил на отдельные строки сообщений. Использование коллекции байтов добавило бы большей сложности без особого выигрыша ИМХО. Спасибо за помощь, кстати. - person Danish Khan; 03.12.2010