Для вашего массива длина массива равна 3 (например, name.length = 3). Но поскольку он хранит элемент, начинающийся с индекса 0, он имеет максимальный индекс 2.
Итак, вместо 'i ** & lt; = name.length' вы должны написать 'i & lt; ** name.length' чтобы избежать «ArrayIndexOutOfBoundsException».
Обычная практика, которую Вы видите в платформе.NET, и что я рекомендую использовать здесь, указывает смещение и длину. Поэтому заставьте свою функцию Синтаксического анализа также принять, что смещение в переданном массиве и число элементов используют.
, Конечно, те же правила применяются, как будто необходимо было передать указатель как в C++ - массив не должен быть изменен, или иначе он может привести к неопределенному поведению, если Вы не уверены, когда точно данные будут использоваться. Но это не проблема, если Вы больше не собираетесь быть изменением массива.
Я передал бы ArraySegment<byte>
в этом случае.
Вы изменили бы Ваш Parse
метод к этому:
// Changed TCPHeader to TcpHeader to adhere to public naming conventions.
TcpHeader Parse(ArraySegment<byte> buffer)
И затем Вы изменили бы вызов на это:
// Create the array segment.
ArraySegment<byte> seg = new ArraySegment<byte>(packet, 20, 20);
// Call parse.
TcpHeader header = Parse(seg);
Используя эти ArraySegment<T>
не скопирует массив, и он сделает проверку границ Вас в конструкторе (так, чтобы Вы не указывали неправильные границы). Затем Вы изменяете Ваш Parse
метод для работы с границами, указанными в сегменте, и необходимо быть в порядке.
можно даже создать перегрузку удобства, которая примет полный массив байтов:
// Accepts full array.
TcpHeader Parse(byte[] buffer)
{
// Call the overload.
return Parse(new ArraySegment<byte>(buffer));
}
// Changed TCPHeader to TcpHeader to adhere to public naming conventions.
TcpHeader Parse(ArraySegment<byte> buffer)
Если можно изменить синтаксический анализ () метод, изменить его для принятия смещения, где обработка должна начаться. Синтаксический анализ TCPHeader (байт [] буфер, международное смещение);
Я не думаю, что можно сделать что-то как этот в C#. Вы могли или сделать Синтаксический анализ (), функция использует смещение или создает 3 массива байтов для начала; один для Заголовка IP, один для Заголовка TCP и один для Полезной нагрузки.
Вы могли использовать LINQ, чтобы сделать что-то как:
tcpbuffer.Skip(20).Take(20);
, Но Система. Буфер. BlockCopy / Система. Массив. Копия, вероятно, более эффективна.
Это - то, как я решил его прибывающий из того, чтобы быть c программистом c# программисту. Мне нравится использовать MemoryStream для преобразования его в поток и затем BinaryReader для разбивания двоичного блока данных. Должны были добавить две функции помощника для преобразования от сетевого порядка до прямого порядка байтов. Также для создания байта [] для отправки видят , там путь, бросает объект назад к нему исходный тип без specifing каждый случай? , который имеет функцию, которые допускают преобразование из массива объектов к байту [].
Hashtable parse(byte[] buf, int offset )
{
Hashtable tcpheader = new Hashtable();
if(buf.Length < (20+offset)) return tcpheader;
System.IO.MemoryStream stm = new System.IO.MemoryStream( buf, offset, buf.Length-offset );
System.IO.BinaryReader rdr = new System.IO.BinaryReader( stm );
tcpheader["SourcePort"] = ReadUInt16BigEndian(rdr);
tcpheader["DestPort"] = ReadUInt16BigEndian(rdr);
tcpheader["SeqNum"] = ReadUInt32BigEndian(rdr);
tcpheader["AckNum"] = ReadUInt32BigEndian(rdr);
tcpheader["Offset"] = rdr.ReadByte() >> 4;
tcpheader["Flags"] = rdr.ReadByte() & 0x3f;
tcpheader["Window"] = ReadUInt16BigEndian(rdr);
tcpheader["Checksum"] = ReadUInt16BigEndian(rdr);
tcpheader["UrgentPointer"] = ReadUInt16BigEndian(rdr);
// ignoring tcp options in header might be dangerous
return tcpheader;
}
UInt16 ReadUInt16BigEndian(BinaryReader rdr)
{
UInt16 res = (UInt16)(rdr.ReadByte());
res <<= 8;
res |= rdr.ReadByte();
return(res);
}
UInt32 ReadUInt32BigEndian(BinaryReader rdr)
{
UInt32 res = (UInt32)(rdr.ReadByte());
res <<= 8;
res |= rdr.ReadByte();
res <<= 8;
res |= rdr.ReadByte();
res <<= 8;
res |= rdr.ReadByte();
return(res);
}
Нет никакого способа использовать верифицируемый код, чтобы сделать это. Если Ваш метод Синтаксического анализа может иметь дело с наличием IEnumerable< byte> затем можно использовать выражение
TCPHeader tcp = Parse(packet.Skip(20));
LINQ Почему бы не зеркально отразить проблему и создать классы, которые накладывают буфер для вытаскивания битов?
// member variables
IPHeader ipHeader = new IPHeader();
TCPHeader tcpHeader = new TCPHeader();
// passing in the buffer, an offset and a length allows you
// to move the header over the buffer
ipHeader.SetBuffer( buffer, 0, 20 );
if( ipHeader.Protocol == TCP )
{
tcpHeader.SetBuffer( buffer, ipHeader.ProtocolOffset, 20 );
}
При реальной необходимости в подобных управление необходимо посмотреть unsafe
функция C#. Это позволяет Вам иметь указатель и прикреплять его так, чтобы GC не перемещал его:
fixed(byte* b = &bytes[20]) {
}
Однако эта практика не предлагается для работы с управляемым, только кодируют, при отсутствии проблем производительности. Вы могли передать смещение и длину как в Stream
класс.
Если IEnumerable<byte>
приемлемо как вход, а не byte[]
, и Вы используете C# 3.0, то Вы могли записать:
tcpbuffer.Skip(20).Take(20);
Примечание, что это все еще выделяет экземпляры перечислителя под покрытиями, таким образом, Вы не выходите из выделения в целом, и таким образом, для небольшого количества байтов это может на самом деле быть медленнее, чем выделение нового массива и копирование байтов в него.
я не волновался бы слишком много о выделении и GC небольших временных массивов, чтобы быть честным все же..NET собрала "мусор", среда чрезвычайно эффективна в этом типе шаблона выделения, особенно если массивы являются недолгими, поэтому если Вы не представили его и нашли, что GC проблема затем, я записал бы это самым интуитивным способом и согласовал бы проблемы производительности, когда Вы знаете, что у Вас есть они.