Я хотел бы иметь возможность получить длину данных, доступных из сетевого потока TCP в C#, чтобы установить размер буфера перед чтением из сетевого потока. Существует свойство NetworkStream.Length
, но оно еще не реализовано, и я не хочу выделять огромный размер для буфера, так как это займет слишком много места. Единственный способ сделать это - это предшествовать передаче данных другим, указав размер, но это кажется немного грязным. Что было бы лучшим способом для меня сделать это.Получить длину данных, доступных в NetworkStream
ответ
При обращении к Stream
s вы обычно читаете и записываете данные в небольших кусках (например, килобайт или около того) или используете такой метод, как CopyTo
, который делает это за вас.
Это пример использования CopyTo
для копирования содержимого потока в другой поток и возврата его в виде byte[]
из метода с использованием буфера с автоматическим размером.
using (MemoryStream ms = new MemoryStream())
{
networkStream.CopyTo(ms);
return ms.ToArray();
}
Это код, который считывает данные таким же образом, но более вручную, что может быть лучше для вас, чтобы работать, в зависимости от того, что вы делаете с данными:
byte[] buffer = new byte[2048]; // read in chunks of 2KB
int bytesRead;
while((bytesRead = networkStream.Read(buffer, 0, buffer.Length)) > 0)
{
//do something with data in buffer, up to the size indicated by bytesRead
}
(основа для этих фрагментов кода исходила от Most efficient way of reading data from a stream)
Это замечательно! Я буду использовать это для записи в поток памяти, пока все данные не поступят. –
Я на самом деле немного изменил ваш код, чтобы убедиться, что у меня есть весь объект, прежде чем продолжить, по сути, первые 8 байтов пакета - это размер, и он продолжит читать то, что может, пока не будет иметь такое же количество байтов в памяти, как и ожидалось. –
Не существует внутренней длины сетевого потока. Вам придется либо отправить длину данных, чтобы следовать с другого конца, либо прочитать все входящие данные в другой поток, где вы можете получить доступ к информации о длине.
Дело в том, что вы действительно не можете быть уверены, что все данные считываются сокет, но в любое время может появиться больше данных. Это попытка, даже если вы как-то знаете, сколько данных ожидать, скажем, если у вас есть заголовок пакета, который содержит длину. весь пакет еще не получен.
Если вы читаете произвольные данные (например, файл), у вас должен быть буфер разумного размера (например, 1k-10k или что-то, что вы считаете оптимальным для вашего сценария), а затем записывать данные в файл как его чтение из потока.
var buffer = byte[1000];
var readBytes = 0;
using(var netstream = GetTheStreamSomhow()){
using(var fileStream = (GetFileStreamSomeHow())){
while(netstream.Socket.Connected) //determine if there is more data, here we read until the socket is closed
{
readBytes = netstream.Read(buffer,0,buffer.Length);
fileStrem.Write(buffer,0,buffer.Length);
}
}
}
Или просто использовать CopyTo
как Тим предложил :) Просто убедитесь, что все данные действительно были считаны, в том числе данные, которые не получили по сети еще.
Сначала вы можете отправить длину входящих данных. Например: У вас есть data = byte[16]
, которую вы хотите отправить. Поэтому сначала вы отправляете 16 и определяете на сервере, что эта длина всегда 2 (потому что 16 имеет два символа). Теперь вы знаете, что incomingLength = 16
. Теперь вы можете подождать данные по длине incomingLength
.
Обычный способ чтения потока состоит в том, чтобы делать это небольшими порциями за один раз (например, 512 байт), а когда '0' возвращается из метода' Read', это означает, что данных больше нет. Есть ли причина, по которой вы этого не делаете, или вы не знали об этом? –
Я не знал, не могли бы вы привести небольшой пример кода? Если вы опубликуете его как ответ, я, скорее всего, его приму. –
Другой метод заключается в том, чтобы сохранить длину сообщения перед данными сообщения. Затем зачитайте, например, длину в четыре байта, затем переведите четыре байта в целое число и прочитайте до этого количества байтов. –