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 в зависимости от типа данных в вашем объекте (это лучше всего работает, когда у вас есть много струн).
Это займет больше времени процессора для отправки и чтения сообщений, поэтому это компромисс, если вам нужно снизить требования к пропускной способности.