Member 12606650 Ответов: 2

Чтение / запись структур C в / из C# по протоколу TCP/ip


Привет,

Может быть, я прошу слишком многого,но я попробую. Я работаю в C уже почти 30 лет
лет, C++ уже почти 15, но я новичок в C#.

У нас есть большое применение В С. Он поддерживает tcp / ip-сервер для
администрация. В настоящее время существует клиент графического интерфейса x-windows (на языке C), который
подключается к этому серверу для настройки и мониторинга приложения. Мы
хочу заменить старый клиент x-windows на что-то, что будет работать на ПК.
Нам сказали, что C# хорошо подходит.

Вся связь между серверным приложением и клиентским графическим интерфейсом осуществляется через
обмен сообщениями, причем формат сообщений высокого уровня:

< длина ><заголовок & gt; [необязательно < данные>]
2-байтовая фиксированная длина 0 или более фиксированной длины или строка

- "Длина" - это 16-битное целое число без знака с общим размером сообщения (не
включая саму "длину").

- ≪header> - Это структура в стиле C со смесью подписанных / неподписанных ints различных типов.
размеры (8, 16, 32 и 64-битные), некоторые C-style фиксированной длины, null-terminated
строки из 8-битных символов и некоторые битовые поля (unsigned int, где каждый
бит используется для обозначения отдельного флага)

- Поле<data & gt; (если оно существует) будет либо одной, либо несколькими структурами C-стиля, либо
строка символов, все они основаны на типе сообщения в заголовке <.
Существует более 100 различных структур, используемых в поле "Данные" (различные
элементы конфигурации, статистические элементы,...).

У меня возникли некоторые трудности с имитацией C-struct/c-strings в C#. Я
видел в интернете кучу вещей о маршаллинге, небезопасном коде, неуправляемом коде,
LPStr, UTF8,..., но не видел краткого примера:

а) чтение tcp/ip в C-структуре (с C-строками) и использование его в C#ec

б) выполнение обратного (перестройка C-структуры для отправки)

Вот вымышленная 40-октетная структура C, которую можно использовать в качестве заголовка в примерах (u16
это беззнаковый 16-битный инт, с16-это 16-разрядный инт ...):

typedef struct TestHeader
{
  u16  messageType;
  s16  num1;
  char text1[4];
  char text2[8];
  u64  num2;
  s32  num3;
  u8   num4;
  u8   num5;
  u16  flag1;   // each bit used as a flag
  char text3[4];
  char text4[4];
} TestHeader;


Что я уже пробовал:

маршаллинг, небезопасный код, неуправляемый код,
LPStr, UTF8, ...,

Patrice T

И в чем же вопрос ?

Dave Kreskowiak

Я не совсем уверен, с чем у вас проблема, но поскольку вы исходите из фона C/C++, вы можете немного переосмыслить это.

Google для " двоичной сериализации C#". Посмотрим, что это даст тебе.

2 Ответов

Рейтинг:
12

Jon McKee

Как Вы читаете из TCP / IP, зависит только от вас. Похоже,у вас уже был клиент/сервер, который работал.

Вариант №1:
Превратите основной клиентский код TCP/IP в DLL и PInvoke его в C#:

[DllImport("tcpipclient.dll", CallingConvention.Cdecl)]
public static extern TestHeader GetHeader();

Для этого используйте StructLayoutAttribute и MarshalAsAttribute Это позволит вам выровнять структуру в C# вручную и управлять управляемым<->неуправляемым маршаллингом. Учитывая ваш пример, версия C# может быть:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TestHeader
{
    [MarshalAs(UnmanagedType.U2)]
    public ushort messageType;
    [MarshalAs(UnmanagedType.I2)]
    public short num1;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] text1;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public byte[] text2;
    [MarshalAs(UnmanagedType.U8)]
    public ulong num2;
    [MarshalAs(UnmanagedType.I4)]
    public int num3;
    [MarshalAs(UnmanagedType.U1)]
    public byte num4;
    [MarshalAs(UnmanagedType.U1)]
    public byte num5;
    [MarshalAs(UnmanagedType.U2)]
    public ushort flag1;   // each bit used as a flag
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] text3;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] text4;
}

Причина, по которой я использую byte вместо char это потому, что char обычно равен 1 байту в C и 2 байтам в C#. Это просто простой способ загрузить его в byte а потом-конвертация. Обращение flag1 может быть достигнуто с помощью enum подобный этому:
[Flags]
public enum TestHeaderFlags : ushort
{
    None = 0x00,
    Flag1 = 0x01,
    Flag2 = 0x02,
    Flag3 = 0x04,
    Flag4 = 0x08,
    Flag5 = 0x10,
    Flag6 = 0x20,
    Flag7 = 0x40,
    Flag8 = 0x80
}

static void Main(string[] args)
{
    ushort test = 0x83;
    TestHeaderFlags testFlag = (TestHeaderFlags) Enum.ToObject(
        typeof(TestHeaderFlags), test);
    Console.WriteLine(testFlag);
    Console.ReadKey();
}   

//Output
Flag1, Flag2, Flag8

Тестируйте отдельные флаги, как вам заблагорассудится. Я предпочитаю использовать testFlag.HasFlag(TestHeaderFlags.Flag2).

Примечания к этому подходу:
Я не сделал настройки для проверки этого так что если это не совсем работает вы можете посмотреть LayoutKind.Явные[^]. Еще одна вещь, которую можно проверить, может быть использование fixed char text[4] в unsafe struct.

Вариант №2:
Используйте любой метод, который вы хотели бы прочитать. byte поток из TCP / IP, а затем использовать BitConverter отделить поток и сделать свой объект. Пример:
public TestHeader DeserializeHeader(byte[] stream)
{
    TestHeader header;
    header.messageType = BitConverter.ToUInt16(stream, 0);
    header.num1 = BitConverter.ToInt16(stream, 2);
    header.num2 = BitConverter.ToUInt64(stream, 16);
    header.num3 = BitConverter.ToInt32(stream, 24);
    header.num4 = stream[28];
    header.num5 = stream[29];
    header.flag1 = BitConverter.ToUInt16(stream, 30);

    header.text1 = new byte[4];
    header.text3 = new byte[4];
    header.text4 = new byte[4];
    for (int i = 0; i < 4; i++)
    {
        header.text1[i] = stream[4 + i];
        header.text3[i] = stream[32 + i];
        header.text4[i] = stream[36 + i];
    }
    header.text2 = new byte[8];
    for (int i = 0; i < 8; i++)
        header.text2[i] = stream[8 + i];
    return header;
}

Поверните процесс вокруг, чтобы сериализовать. Я уверен, что есть и другие способы сделать это, но это то, что пришло мне на ум :)


Рейтинг:
0

Garth J Lancaster

Хотя я согласен с Дэйвом к, вы можете использовать двоичную сериализацию c#, вы также можете использовать Google protobuf, JSON или, возможно, Binary JSON в качестве "кодировки" - для меня это самая простая часть ...

"Трудная часть" заключается в том, что у вас есть "спецификация" с одной стороны, определяющая около 100 различных структур - что происходит, когда что-то нужно изменить ? у вас есть 2 места, которые вам нужно будет изменить и протестировать: сторона c++ и сторона c#.

Я предлагаю, исходя из опыта, вам

а) найдите любой метод сериализации( как общий метод), который вам удобен - потратьте время, чтобы сделать его правильным, учтите будущий рост и т. д.

б) "кодифицируйте" свои спецификации, чтобы вы могли генерировать свои структуры c++ и, конечно же, свои процедуры декодирования c#, и, надеюсь, все необходимое для вашего тестового жгута автоматически как часть процесса сборки - в какой-то степени protobuf может сделать вашу жизнь "проще" здесь, но есть много инструментов/подходов

кстати, если вы когда-либо имели дело с ISO 8583, вы поймете, почему я предлагаю такой подход