网络传输的有效(空间)序列化

本文关键字:序列化 空间 有效 网络传输 | 更新日期: 2023-09-27 17:50:23

我正在尝试编写一些基础设施来促进服务器和客户端之间的对象更新。这可能会在游戏中使用,但是,我觉得这个问题根本不是游戏所特有的(所以我在这里提出了这个问题)。

出于安全和效率的原因,我希望服务器有选择地更新对象属性。例如,一个对象的特定属性可能只对控制该对象的客户端有用,因此服务器只会用该信息更新"所有者"。或者,一些属性可能需要发送到所有客户端。为了实现这一点,我定义了一个自定义属性,该属性指定了网络处理该属性的方式:

    [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 &&
                    ((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;
    }

一个类似的函数可以在接收端将对象重新组合在一起。我遇到的问题是,就空间使用而言,序列化的效率非常低。例如,从TestObject中抓取ServerToAll属性会产生54字节(然而它可能少到4)。

那么问题来了:是否有一种更有效的方法将对象序列化为字节流,以满足我的预期目的?请注意,我不希望编写大量与序列化相关的代码。

谢谢!

网络传输的有效(空间)序列化

NetworkParameterAttribute应该有额外的字段,表示相应的属性为"dirty"。对属性的每次更改都应该有效地设置此标志,并且在序列化期间应该重置该标志。只有脏属性才应该被序列化。

另外,现在对象只是部分序列化,在序列化过程中,现在需要提供关于正在序列化的属性的信息。在填充流时保持一个dirty属性的位向量,并将该位向量放在返回的字节数组的开头。

编辑:我们可以使用最后序列化的实际值,而不是在属性中使用标志。这样做的好处是,我们不需要为每个属性添加额外的代码来保持标志与属性的同步。在序列化过程中,比较两个值,如果值不相等,则序列化属性。