C# отправить объекты структуры через сокет

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

Как передать объекты структуры через tcp / ip вместо просто строк?

мое приложение представляет собой сетевую игру с возможностями чата. поэтому вместо того, чтобы просто передавать текст, я хотел бы имплотировать структуру данных или структуру классов, которая будет иметь два поля: i. тип пакета ii. данные для пакета тип

и я бы передал это, когда это необходимо во время выполнения приложения, и декодировать объект данных на принимающей стороне и поместить его туда, где он принадлежит.

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

Я читал о сериализации/де-сериализации, что он путь?

спасибо.


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


6 ответов


вы можете создать сетевой поток на основе сокета и использовать любой механизм потока для транспортировки данных. Это переводит ваш вопрос в: как я могу читать / писать структуру из / в поток.

вы можете использовать сериализацию, но также BinaryWriter / BinaryReader. Для небольшой структуры (как вы описываете) я бы написал некоторые пользовательские методы:

var netStream = new NetworkStream(clientSocket, true);
var writer = new BinaryWriter(netStream);

writer.Write(data.Value1);
writer.Write(data.Value2);

для больших структур я бы рассмотрел вариант маршалинга Чизо.


в конечном счете да: вы говорите о сериализации. Это может принимать множество форм, особенно в .NET, но в конечном итоге вам нужно выбрать между:

  • текст против двоичного; прямой двоичный как правило быть меньше текста, так как он обычно включает в себя меньше синтаксического анализа и т. д.; Текст (xml, json и т. д.) Обычно представлен в потоке как UTF8 (хотя любая кодировка возможна). Они широко читаются человеком, и, несмотря на то, что они более многословны, обычно могут быть сжаты достаточно хороший.
  • контракт против метаданных; сериализаторы на основе контрактов сосредоточены на представлении сведения - предполагается, что другой конец трубы понимает структуру, но не предполагается, что они разделяют реализацию. Это имеет ограничения в том, что вы не можете внезапно ввести какой-то совершенно неожиданный подкласс, но делает его независимым от платформы. Напротив, сериализаторы на основе метаданных отправляют тип информация о потоке (т. е. "это а My.Namespace.FooBar экземпляр). Это упрощает работу, но редко работает между разными платформами (и часто не между версиями) - и вся эта информация типа может быть многословной
  • manual vs automatic; факт: ручные сериализаторы часто могут быть наиболее эффективными с точки зрения пропускной способности, так как вы можете вручную настроить черт из потока-но это занимает много усилий, и вам нужно понять серии сериализации. Автоматические сериализаторы намного лучше для общего назначения использует (в реальности: большинство сценариев). Избегайте ручного управления, если у вас нет выбора. Автоматические сериализаторы обрабатывают все сложности беспокойства о различных типах данных и т. д.

ручной сериализатор подходы включают в себя (просто упоминая ключевое слово" сериализатор"):TextWriter, XmlWriter, IXmlSerializable, BinaryWriter, ISerializable. Ты не хочешь этого делать...

фокусировка больше на автоматических сериализаторах:

               | Contract               | Metadata
===============+========================+===========================
  Text         | XmlSerializer          | SoapFormatter
               | DataContractSerializer | NetDataContractSerializer
               | Json.NET               |
---------------+------------------------+---------------------------
  Binary       | protobuf-net           | BinaryFormatter

так как вы говорите raw streams, мое предпочтение было бы бинарным сериализатором на основе контрактов - но тогда я написал protobuf-чистая, так что я могу быть предвзятым ; - p

для сравнения с общими стеками RPC:

  • "remoting" использует BinaryFormatter
  • веб-службы" asmx " (включая WSE*) используют XmlSerializer
  • WCF может использовать многие, чаще всего DataContractSerializer или NetDataContractSerializer, а иногда XmlSerializer (его можно также установить для использования, например, протобуф-нет)

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


сериализация-самый простой способ, так как система поддерживает его напрямую. Однако есть некоторые проблемы с производительностью с большими и сложными объектами. В вашем случае похоже, что сериализация - это путь. Если вы хотите что-то более низкого уровня, вы можете проверить BinaryWriter/BinaryReader, который позволяет вам делать работу самостоятельно.


Если вам не нужно богатство сериализации - если вы просто хотите написать структуру в массив байтов, рассмотрите класс Marshal.

например, рассмотрим приложение tar в C#. Формат tar основан на 512-байтовых блоках, а первый блок в серии имеет регулярную структуру. В идеале приложение хочет просто Блитт данные с диска, в структуре. The Маршал.PtrToStructure способ это. Вот структура.

    [StructLayout(LayoutKind.Sequential, Size=512)]
    internal struct HeaderBlock
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
        public byte[]   name;    // name of file. 

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[]   mode;    // file mode

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[]   uid;     // owner user ID

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[]   gid;     // owner group ID

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
        public byte[]   size;    // length of file in bytes

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
        public byte[]   mtime;   // modify time of file

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[]   chksum;  // checksum for header

        // ... more like that... up to 512 bytes. 

тогда вот общий класс, который делает blitting.

internal class RawSerializer<T>
{
    public T RawDeserialize( byte[] rawData )
    {
        return RawDeserialize( rawData , 0 );
    }    

    public T RawDeserialize( byte[] rawData , int position )
    {
        int rawsize = Marshal.SizeOf( typeof(T) );
        if( rawsize > rawData.Length )
            return default(T);

        IntPtr buffer = Marshal.AllocHGlobal( rawsize );
        Marshal.Copy( rawData, position, buffer, rawsize );
        T obj = (T) Marshal.PtrToStructure( buffer, typeof(T) );
        Marshal.FreeHGlobal( buffer );
        return obj;
    }

    public byte[] RawSerialize( T item )
    {
        int rawSize = Marshal.SizeOf( typeof(T) );
        IntPtr buffer = Marshal.AllocHGlobal( rawSize );
        Marshal.StructureToPtr( item, buffer, false );
        byte[] rawData = new byte[ rawSize ];
        Marshal.Copy( buffer, rawData, 0, rawSize );
        Marshal.FreeHGlobal( buffer );
        return rawData;
    }
}

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

Как только вы имеете массив байтов, вы можете передать его через NetworkStream или etc, а затем де-сериализовать, используя тот же класс на другом конце.


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


вы на правильном пути с использованием сериализации объектов.

одна вещь, которую я бы подумал о том, что я еще не видел, - это то, что двоичные сериализаторы обычно создают меньше байтов для отправки сокета, однако, если вы используете XML или JSON-сериализатор, а затем сжимаете результаты с помощью CompressionStream (GZipStream?) перед отправкой по сетевому потоку вы можете получить даже размеры smalelr в зависимости от типа данных в вашем объекте (это лучше всего работает, когда у вас есть много струн).

Это займет больше времени процессора для отправки и чтения сообщений, поэтому это компромисс, если вам нужно снизить требования к пропускной способности.