#c# #sockets #stream #bytearray
#c# #сокеты #поток #массивы
Вопрос:
В настоящее время находится в процессе написания некоторого кода сокета TCP и сталкивается с небольшой проблемой.
В основном, меня смущает следующая пара строк кода.
NetworkStream clientStream = tcpClient.GetStream();
List<Byte> fullMessage = new List<Byte>();
Byte[] message = new Byte[4096];
Byte[] currentMessage = new Byte[4096];
Int32 bytesRead = 0;
if (clientStream.CanRead)
{
do
{
bytesRead = clientStream.Read(message, 0, 4096);
Array.Resize<Byte>(ref currentMessage, bytesRead);
Array.Copy(message, currentMessage, bytesRead);
fullMessage.AddRange(currentMessage);
} while (clientStream.DataAvailable);
}
В частности, что касается наилучшего способа обработки факта, даже если массив байтов сообщения объявлен в 4096 байтах, объем извлекаемых данных произволен и не может быть вычислен.
Итак, считается ли то, как я обрабатываю ответ, разумным решением или есть способ получше? (ТО есть: создает массив нового размера на основе значения bytesRead)
Ответ №1:
Использовать поток памяти:
NetworkStream clientStream = tcpClient.GetStream();
MemoryStream messageStream = new MemoryStream();
byte[] inbuffer = new byte[65535];
if (clientStream.CanRead)
{
do
{
var bytesRead = clientStream.Read(inbuffer, 0, buffer.Length);
messageStream.Write(inbuffer, 0, bytesRead);
} while (clientStream.DataAvailable);
}
messageStream.Position = 0;
var completeMessage = new byte[messageStream.Length];
messageStream.Write(completeMessage, 0, messageStream.Length);
Комментарии:
1. Так-то лучше. Это не приходило мне в голову. Намного приятнее! Я полагаю, что также будут некоторые преимущества в производительности?
2. ДА. это должно быть намного быстрее, поскольку обработка внутреннего буфера MemoryStream немного более оптимальна
3. Отличный пример, но он ненадежен для данных, которые охватывают несколько пакетов. Проблема в том, что цикл может выполняться быстрее, чем принимаются данные, что приводит к clientStream. Данные, доступные неправильно, приравниваются к false, когда на самом деле еще не поступило больше данных. Есть несколько способов обойти это: реализовать крошечный режим ожидания в каждом цикле (ошибка), реализовать известные байты «конца потока» (все еще ошибка) или присвоить первым четырем байтам потока значение uint, которое указывает размер полезной нагрузки, и прекратить цикл после получения всех байтов (и при необходимости дождаться получения большего количества).
4. @jgauffin: Это именно то, что я только что сказал.
Ответ №2:
Если вы знаете, что сообщение не будет превышать 4096 байт, то вы можете написать что-то вроде этого:
int totalBytesRead = 0;
int bytesRead;
while ((bytesRead = clientStream.Read(message, totalBytesRead, message.Length - totalBytesRead)) !=0)
{
totalBytesRead = bytesRead;
}
totalBytesRead
используется для указания clientStream.Read
, куда поместить данные, которые он копирует.
clientStream.Read
возвращает 0, если нет доступных данных.
Обратите внимание, что при такой настройке вы не сможете прочитать больше message.Length
байтов. Если ваши пакеты могут быть больше, тогда я предлагаю увеличить ваш буфер. Я бы не рекомендовал постоянно изменять размер массива, потому что это приведет к фрагментации кучи больших объектов (если сообщения станут больше 80 КБ), и в какой-то момент вам придется установить максимальный размер, которым может быть сообщение, — даже если вы обрабатываете «сколь угодно большие» сообщения.