Как я могу объединить байтовые [] буферы в список <byte>? - PullRequest
1 голос
/ 04 марта 2009

Итак, я получаю данные через сокет, используя буфер (byte []) размером 1024, и я хочу объединить чтения вместе, чтобы сформировать весь пакет в случае, если они больше 1024 байтов. Я выбрал List для хранения всего пакета, и я хочу добавить к нему каждый прочитанный буфер по мере его поступления. Я хотел бы сделать:

List.AddRange(Buffer);

Но в случае, если буфер не заполнен, куча пустых байтов будет заполнена до конца. Поэтому, естественно, я бы хотел добавить в список только определенный диапазон байтов, но такого метода не существует. Я всегда мог создать временный байтовый массив с точным количеством полученных байтов, а затем использовать AddRange () и получить желаемый результат, но для меня это просто глупо. Не говоря уже о том, что это будет создавать, а затем выбрасывать массив при каждом чтении данных, что не будет хорошо для производительности на масштабируемом многопользовательском сервере.

Есть ли способ сделать это с помощью списка? Или есть какая-то другая структура данных, которую я могу использовать?

Ответы [ 6 ]

6 голосов
/ 04 марта 2009

Если вы используете C # 3.5 (LINQ)

list.AddRange(buffer.Take(count));
2 голосов
/ 04 марта 2009

Вам на самом деле нужен результат, чтобы быть List<byte>? Что ты будешь делать с этим потом? Если вам действительно нужен только IEnumerable<byte>, я бы предложил создать что-то вроде этого:

using System;
using System.Collections;
using System.Collections.Generic;

public class ArraySegmentConcatenator<T> : IEnumerable<T>
{
    private readonly List<ArraySegment<T>> segments =
        new List<ArraySegment<T>>();

    public IEnumerator<T> GetEnumerator()
    {
        foreach (ArraySegment<T> segment in segments)
        {
            for (int i=0; i < segment.Count; i++)
            {
                yield return segment.Array[i+segment.Offset];
            }
        }
    }

    public void Add(ArraySegment<T> segment)
    {
        segments.Add(segment);
    }

    public void Add(T[] array)
    {
        segments.Add(new ArraySegment<T>(array));
    }

    public void Add(T[] array, int count)
    {
        segments.Add(new ArraySegment<T>(array, 0, count));
    }

    public void Add(T[] array, int offset, int count)
    {
        segments.Add(new ArraySegment<T>(array, offset, count));
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

Тогда вы можете просто добавлять соответствующие сегменты каждый раз. Конечно, у вас может быть много потерянной памяти, и вам нужно будет каждый раз создавать новый буфер (вместо того, чтобы снова читать поверх оригинала), но это будет эффективно другими способами.

1 голос
/ 04 марта 2009

Для .Net3.5 вы можете использовать метод расширения .Take(), чтобы возвращать только фактическое количество полученных вами байтов.

0 голосов
/ 04 марта 2009

Вы можете использовать Array.Copy () и использовать только массивы для построения целевого буфера:

byte[] recvBuffer = new byte[1024];
byte[] message = new byte[0];
int nReaded;

while ((nReaded = ....Read(recvBuffer, 1024) > 0)
{
  byte[] tmp = new byte[message.Length + nReaded];
  Buffer.BlockCopy(message, 0, tmp, 0, message.Length);
  Buffer.BlockCopy(recvBuffer, 0, tmp, message.Length, nReaded);
  message = tmp;
}

РЕДАКТИРОВАТЬ: Заменены Array.Copy () на Buffer.BlockCopy (), как это было предложено Квинтином Робинсоном в комментариях.

0 голосов
/ 04 марта 2009

Вы можете реализовать свою собственную реализацию IEnumerable, которая извлекает из массива только те байты, которые вам нужны. Тогда вы можете сделать:

List.AddRange (новый BufferEnumerator (Buffer));

Редактировать

Вы также можете посмотреть на:

новый System.ArraySegment (Буфер, 0, numBytesRectained)

Я не уверен, что ArraySegment будет работать, я помню, как читал некоторые его недостатки, но не помню особенностей.

0 голосов
/ 04 марта 2009

Я не знаю, какой протокол вы используете, или если вы реализуете собственный протокол, но если вы определите размер, вы можете использовать Buffer.BlockCopy, чтобы напрямую скопировать байты в новый массив, чтобы добавить его в список.

Трудно быть более кратким, если у вас нет подробностей.

...