Отправка структуры С# на С++ через сокет

Я хочу отправить структуру на С# на С++ с помощью сокетов.

Например, я использую эту структуру:

[StructLayout(LayoutKind.sequential, Pack = 1)]
struct pos {
    public int i;
    public float x;
};

Если я каким-то образом преобразую его в байты и отправлю по сети, я смогу преобразовать его в это на С++:

struct pos {
    int i;
    float x;
};

... Я думаю.

1) как разбить экземпляр структуры на С#, чтобы отправить его по сети?

2) могу ли я безопасно преобразовать его в структуру С++, как только я его получу?

Спасибо


person KaiserJohaan    schedule 08.04.2011    source источник
comment
Лично я бы смотрел на известную сериализацию, а не ловил базовые байты; в частности, в противном случае струны станут настоящей головной болью.   -  person Marc Gravell    schedule 08.04.2011
comment
Я просто буду использовать числа с плавающей запятой и целые числа, если это поможет   -  person KaiserJohaan    schedule 08.04.2011


Ответы (2)


Маршаллер помогает выполнять преобразование между структурами .NET и необработанными байтами. В этом ответе Я разместил простое решение, которое сводится к Marshal.StructureToPtr и Marshal.PtrToStructure. В отличие от более продвинутых решений, предложенных Иоганном дю Туа, это, на мой взгляд, лучшее, что вы можете сделать, если все, что вы хотите сделать, это протолкнуть некоторые структуры через поток байтов.

Если вы сделаете это, вы можете безопасно привести к структуре C++, если длина правильная, и ваша структура C++ объявлена ​​с той же упаковкой, что и структура C# (т. е. #pragma pack в VC++ или __attribute__((packed)) в GCC).

Обратите внимание, что это также работает со строками C фиксированной длины, но не позаботится о порядке байтов больших значений. Я нашел простое решение для предоставления геттеров и сеттеров для последней проблемы, которые просто меняют местами байты соответственно (с BitConverter).


Немного уточнения упаковки: Возьмем следующую структуру:

struct MyStruct {
    uint8_t a;
    float b;
};

При объявлении C# со StructLayout, Pack = 1 эта структура будет иметь размер пять байтов. Структура C++, однако, может иметь восемь байтов (или даже больше), в зависимости от упаковки по умолчанию компилятора, который может с радостью вставить несколько байтов заполнения, чтобы выровнять значение с плавающей запятой по 32-битной границе (просто пример). Из-за этого вы должны применить одну и ту же упаковку как к структуре C#, так и к структуре C++. В Визуальном С++:

#pragma pack(push, 1)
// ... struct declarations...
#pragma pack(pop)

Это означает, что все структуры, объявленные между двумя прагмами, будут иметь упаковку, равную единице. В ССЗ:

struct x {
// ...
} __attribute__((packed));

Это сделает то же самое. Вы можете #define __attribute__(x) на платформах Windows и #ifdef _WIN32 вокруг прагм, чтобы сделать код совместимым с обоими мирами.

person OregonGhost    schedule 08.04.2011
comment
Можете ли вы объяснить последнюю часть больше? Я не уверен, что понимаю, что там делать - person KaiserJohaan; 08.04.2011
comment
Об упаковке или абзаце, который я только что добавил минуту назад? :) - person OregonGhost; 08.04.2011
comment
часть упаковки: p также, почему у вас есть maxlength в вашей функции? - person KaiserJohaan; 08.04.2011
comment
Ясно, но если я не возражаю против заполнения, могу ли я просто удалить Pack = 1, а затем безопасно использовать C++? - person KaiserJohaan; 08.04.2011
comment
Если я правильно помню, maxlength был о структурах переменной длины. У меня также была перегрузка проходов Marshal.SizeOf(value) как maxlength :) - person OregonGhost; 08.04.2011
comment
Нет, просто убрать Pack=1 нельзя, если только вы не уверены абсолютно, что оба компилятора сделают абсолютно одинаковую упаковку. Вы можете попробовать большие значения, но проще всего просто упаковать их плотно. - person OregonGhost; 08.04.2011
comment
Это Visual Studio 2010 для C++/C# в качестве компилятора. Почему он будет отличаться от компиляторов? Float и Int - это 4 байта в С++/С#, не так ли? - person KaiserJohaan; 08.04.2011
comment
По крайней мере, для int стандарт С++ не гарантирует, что длина равна 32 битам. Скомпилируйте для x64 и вы обречены. Это не имеет отношения к упаковке, тем не менее, вы все равно должны использовать типы фиксированной длины в C++ для этих структур (например, int32_t), которые вы получаете, включая <cstdint>. Однако на самом деле упаковка может быть 8 байт. Я не знаю. Попробуйте без упаковки и сделайте sizeof в C++ и Marshal.SizeOf в C# и сравните размеры. Но не удивляйтесь, если в будущей версии произойдет сбой. Лучше сделайте это явно. - person OregonGhost; 08.04.2011
comment
Хорошо, я собираюсь попробовать. Что делает Pack = 1 в любом случае? Что делают разные значения? :п - person KaiserJohaan; 08.04.2011
comment
Я не специалист по упаковке, но значение — это граница, по которой будут выровнены члены структуры. Указание 1 означает отсутствие заполнения, 2 означает выравнивание по 16-битным границам, 4 по 32-битным границам. В архитектурах x86 неправильное выравнивание приводит только к снижению производительности, но в некоторых архитектурах, таких как ARM, вы не можете, например, иметь указатель на неправильно выровненное значение (что приводит к сбою, если вы попытаетесь). Обе проблемы связаны с тем, что заполнение/выравнивание существует в первую очередь. - person OregonGhost; 08.04.2011

Вы можете либо закодировать его в формате, подобном JSON (существует множество парсеров JSON, проверьте json.org для списка), XML или просто создайте свой собственный. Вы также можете попробовать уже созданные библиотеки, такие как Protobuf, которые позволяют сериализовать ваши структуры так, как вы создать файл в формате .proto (и использовать Protobuf-Net для C#). Другим вариантом может быть Thrift, который предоставляет способ сериализации, но также предоставляет готовую к использованию систему RCP. Он поддерживает C#, C++ и множество других языков по умолчанию.

Так что все зависит от вкуса, выбирай :D

person Johann du Toit    schedule 08.04.2011
comment
Вы должны использовать библиотеку 3rdpart? Я просто хочу что-то легкое и простое - person KaiserJohaan; 08.04.2011
comment
Нет, вам не нужно, это просто проще, поскольку они выполняют всю работу по смазке за вас, чтобы вы могли сосредоточиться на своем приложении. - person Johann du Toit; 08.04.2011
comment
+1 за некоторые хорошие указатели в этом ответе, хотя для многих приложений это может быть излишним. - person OregonGhost; 08.04.2011