2010-04-16 3 views
2

Я пишу клиент для серверной программы, написанной на C++. Как ни странно, весь сетевой протокол находится в формате, в котором пакеты могут быть легко memcopied в/из структуры C++ (1 байтовый пакетный код, а затем различные устройства для типа пакета).Сериализация объектов в сети - с существующим протоколом

Я мог бы сделать то же самое в C#, но есть ли более простой способ, особенно учитывая, что многие данные являются массивами символов фиксированной длины, с которыми я хочу играть как строки? Или я должен просто сосать его и преобразовывать типы по мере необходимости? Я посмотрел на интерфейс ISerializable, но он не выглядит настолько низким, насколько это необходимо.

ответ

2

Я написал сообщение об этом в 2004 году, в котором перечислены некоторые параметры, доступные для преобразования двоичного потока в структуру памяти .NET. Я переписал его в своем новом блоге, поскольку старый блог-сайт больше не существует.

http://taylorza.blogspot.com/2010/04/archive-structure-from-binary-data.html

В основном у вас есть три варианта

  1. Использование C++ указателей памяти стиля в C#, который требует/небезопасного переключателя
  2. Используйте .NET маршалинг выделить неуправляемый блок памяти, копия байты в неуправляемую память, а затем использовать Marshal.PtrToStructure для маршалирования данных обратно в управляемую кучу, отображающую ее в вашу структуру.
  3. Используйте BinaryReader для ручного чтения потока байтов и упаковки данных в структуру. Лично это был мой предпочтительный вариант.

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

В качестве примера я буду использовать заголовок IP в качестве примера, поскольку во время публикации я работал с Raw TCP-пакетами.

Вам необходимо определить вашу структуру .NET, к которой будут привязаны двоичные данные. Например, заголовок IP выглядит следующим образом.

[StructLayout(LayoutKind.Sequential, Pack = 1)] 
struct IpHeader 
{ 
    public byte VerLen; 
    public byte TOS; 
    public short TotalLength; 
    public short ID; 
    public short Offset; 
    public byte TTL; 
    public byte Protocol; 
    public short Checksum; 
    public int SrcAddr; 
    public int DestAddr; 
} 

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

Таким образом, в C/C++, указав на блок памяти, содержащий байты данных, которые сопоставляются с структурой C/C++, вы можете использовать следующий бит кода, чтобы просмотреть блок данных в виде структурной части память, где пакет является байтом * в памяти.

IpHeader *pHeader = (IpHeader*)packet; 

Выполнение этого же действия с использованием/небезопасной опции C#, а структура, определенная выше, вы используете следующий код.

IpHeader iphdr; 
unsafe 
{ 
    fixed (byte *pData = packet) 
    {  
    iphdr = *(IpHeader*)pData; 
    } 
} 
//Use iphdr... 

Опция сортировочный будет выглядеть следующим образом

IntPtr pIP = Marshal.AllocHGlobal(len); 
Marshal.Copy(packet, 0, pIP, len); 
iphdr = (IpHeader)Marshal.PtrToStructure(pIP, typeof(IpHeader)); 
Marshal.FreeHGlobal(pIP); 

И, наконец, вы можете использовать BinaryReader сделать это полностью в управляемом коде.

MemoryStream stm = new MemoryStream(packet, 0, len); 
BinaryReader rdr = new BinaryReader(stm); 

iphdr.VerLen = rdr.ReadByte(); 
iphdr.TOS = rdr.ReadByte(); 
iphdr.TotalLength = rdr.ReadInt16(); 
iphdr.ID = rdr.ReadInt16(); 
iphdr.Offset = rdr.ReadInt16(); 
iphdr.TTL = rdr.ReadByte(); 
iphdr.Protocol = rdr.ReadByte(); 
iphdr.Checksum = rdr.ReadInt16(); 
iphdr.SrcAddr = rdr.ReadInt32(); 
iphdr.DestAddr = rdr.ReadInt32(); 

Как я уже говорил ранее, возможно, вам придется рассмотреть порядок байтов.Например, приведенный выше код не совсем корректен, поскольку IpHeader не использует тот же порядок байтов, что и предполагаемый ReadInt16. ReadInt32 и т. Д. Решение проблемы с помощью вышеупомянутого решения так же просто, как использование IPAddress.NetworkToHostOrder.

iphdr.VerLen = rdr.ReadByte(); 
iphdr.TOS = rdr.ReadByte(); 
iphdr.TotalLength = IPAddress.NetworkToHostOrder(rdr.ReadInt16()); 
iphdr.ID = IPAddress.NetworkToHostOrder(rdr.ReadInt16()); 
iphdr.Offset = IPAddress.NetworkToHostOrder(rdr.ReadInt16()); 
iphdr.TTL = rdr.ReadByte(); 
iphdr.Protocol = rdr.ReadByte(); 
iphdr.Checksum = IPAddress.NetworkToHostOrder(rdr.ReadInt16()); 
iphdr.SrcAddr = IPAddress.NetworkToHostOrder(rdr.ReadInt32()); 
iphdr.DestAddr = IPAddress.NetworkToHostOrder(rdr.ReadInt32()); 
+0

Ах спасибо! Я пошел и попробовал BinaryReader, и был полностью озадачен тем, почему цифры ошибочны. Увидев, что, похоже, нет никаких других управляемых альтернатив, я думаю, что я просто буду читать прямо из потока и заполнять классы. – cpf