Эффективная сериализация (пробел) для передачи по сети

#c# #serialization

#c# #сериализация

Вопрос:

Я пытаюсь написать некоторую инфраструктуру для облегчения обновления объектов между сервером и клиентом (ами). Вероятно, это будет использоваться в игре, однако я чувствую, что вопрос совсем не специфичен для игры (поэтому я задал его здесь).

По соображениям безопасности и эффективности я бы хотел, чтобы сервер выборочно обновлял свойства объекта. Например, определенное свойство объекта может быть полезным только для клиента, который управляет этим объектом, поэтому сервер обновит эту информацию только для «владельца». В качестве альтернативы, некоторые свойства, возможно, потребуется отправить всем клиентам. Для реализации этого я определил пользовательский атрибут, который определяет способ, которым сеть должна обрабатывать свойство:

     [AttributeUsage(AttributeTargets.Property)]
    public class NetworkParameterAttribute : System.Attribute
    {
        public enum NetworkParameterType
        {
            ServerToOwner,
            ServerToAll,
            ServerToOwnerView,
            OwnerToServer
        }

        private NetworkParameterType type;
        public NetworkParameterType Type
        {
            get
            {
                return type;
            }
        }

        public NetworkParameterAttribute(NetworkParameterType Type)
        {
            this.type = Type;
        }
    }
  

Теперь в классе объектов я могу определять свойства следующим образом:

     public class TestObject
    {
        [NetworkParameter(NetworkParameterAttribute.NetworkParameterType.ServerToAll)]
        public int ID { get; set; }

        [NetworkParameter(NetworkParameterAttribute.NetworkParameterType.ServerToOwner)]
        public string Name { get; set; }
    }
  

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

     public byte[] GetBytes(NetworkParameterAttribute.NetworkParameterType type)
    {
        MemoryStream stream = new MemoryStream();
        BinaryFormatter formatter = new BinaryFormatter();
        foreach (PropertyInfo info in this.GetType().GetProperties())
        {
            foreach (object attribute in info.GetCustomAttributes(true))
            {
                if (attribute is NetworkParameterAttribute amp;amp;
                    ((NetworkParameterAttribute)attribute).Type == type)
                {
                    formatter.Serialize(stream, info.GetValue(this, null));
                }
            }
        }
        byte[] buf = new byte[stream.Length];
        Array.Copy(stream.GetBuffer(), buf, stream.Length);
        return buf;
    }
  

Аналогичная функция может собрать объект обратно на принимающей стороне. Проблема, с которой я сталкиваюсь, заключается в том, что сериализация очень неэффективна с точки зрения используемого пространства. Например, получение свойств ServerToAll из TestObject приводит к 54 байтам (тогда как это может быть всего лишь 4).

Итак, вопрос: существует ли более эффективный способ сериализации объектов в поток байтов, который будет работать для моей намеченной цели? Обратите внимание, что я бы предпочел не писать много кода, связанного с сериализацией.

Спасибо!

Ответ №1:

NetworkParameterAttribute должен иметь дополнительное поле, которое обозначает соответствующее свойство как «грязное». Каждое изменение свойства должно эффективно устанавливать этот флаг, и во время сериализации флаг должен быть сброшен. На самом деле сериализовать следует только грязные свойства.

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

РЕДАКТИРОВАТЬ: Вместо того, чтобы иметь флаг внутри атрибута, мы можем иметь фактическое значение, которое было сериализовано последним. Преимущество в том, что нам не нужен дополнительный код для каждого свойства, чтобы поддерживать синхронизацию флага со свойством. Во время сериализации мы сравниваем два значения и сериализуем свойство, если значения не равны.

Комментарии:

1. Мне нравится эта идея, и я определенно хотел реализовать ее наряду с тем, что я уже сделал. Есть ли способ автоматически устанавливать это свойство при изменении свойства?

2. Я не думаю, что такой механизм существует. Вам нужно было бы добавить код. Но вместо того, чтобы иметь флаг, мы могли бы дублировать данные. Я расширю ответ.