初学者,自用笔记

职责抽象

接口

// 基础序列化接口(所有序列化器的核心契约)

// 定义对象二进制的基础转换能力,适配任意序列化格式
// 全量序列化接口:只提供最基础对象和字节转换
public interface IFullSerializer
{
    byte[] Serialize<T>(T obj) where T : class, new();
    T Deserialize<T>(byte[] data) where T : class, new();
}

// 增量序列化扩展接口(可选实现,适配需要增量更新的场景)
// 定义增量更新的序列化能力,独立于基础序列化
public interface IDeltaSerializer : IFullSerializer
{
    // 增量序列化(对比新旧对象,生成仅包含变更的二进制补丁)
    byte[] SerializeDelta<T>(T oldObj, T newObj) where T : class, new();

    // 增量反序列化(将增量补丁应用到旧对象,生成新对象)
    T ApplyDelta<T>(T oldObj, byte[] deltaData) where T : class, new();
}

// 引用管理扩展接口(可选实现,适配需要处理对象引用的场景)
// 定义对象引用的序列化/反序列化能力,避免重复传输
public interface IReferenceSerializer : IFullSerializer
{
    // 设置引用池(序列化前初始化,存储已序列化的对象ID和数据)
    void SetReferencePool(Dictionary<ulong, byte[]> refPool);

    // 获取引用池(反序列化后获取,用于解析引用关系)
    Dictionary<ulong, byte[]> GetReferencePool();
}

// 完整序列化接口:全量 + 增量 + 引用池
public interface ISerializer : IFullSerializer
{
    byte[] SerializeDelta<T>(T oldObj, T newObj) where T : class, new();
    T ApplyDelta<T>(T oldObj, byte[] deltaData) where T : class, new();

    void SetReferencePool(Dictionary<ulong, byte[]> refPool);
    Dictionary<ulong, byte[]> GetReferencePool();
}

基类

// 序列化器基类(封装通用逻辑,减少子类重复代码)
public abstract class SerializerBase : IFullSerializer
{
    // 通用空值校验(所有序列化器复用)
    protected virtual void ValidateInput<T>(T obj) where T : class, new()
    {
        if (obj == null)
        {
            throw new ArgumentNullException(nameof(obj), "待序列化对象不能为空");
        }
    }

    // 通用二进制数据校验(所有序列化器复用)
    protected virtual void ValidateData(byte[] data)
    {
        if (data == null || data.Length == 0)
        {
            throw new ArgumentException("反序列化数据不能为空或空数组", nameof(data));
        }
    }

    // 抽象全量序列化(子类实现具体格式逻辑)
    public abstract byte[] Serialize<T>(T obj) where T : class, new();

    // 抽象全量反序列化(子类实现具体格式逻辑)
    public abstract T Deserialize<T>(byte[] data) where T : class, new();
}

具体实现

// Protobuf序列化器(实现增量+引用能力,适配核心诉求)
public class ProtobufSerializer : SerializerBase, ISerializer
{
    // 引用池(存储对象ID与序列化数据的映射,避免重复传输)
    private Dictionary<ulong, byte[]> _refPool = new Dictionary<ulong, byte[]>();

    // 设置引用池(实现IReferenceSerializer)
    public void SetReferencePool(Dictionary<ulong, byte[]> refPool)
    {
        _refPool = refPool ?? new Dictionary<ulong, byte[]>();
    }

    // 获取引用池(实现IReferenceSerializer)
    public Dictionary<ulong, byte[]> GetReferencePool()
    {
        return _refPool;
    }

    // Protobuf全量序列化(紧凑编码+引用处理)
    public override byte[] Serialize<T>(T obj) where T : class, new()
    {
        ValidateInput(obj);
        _refPool.Clear(); // 每次序列化重置引用池

        // 处理Protobuf消息对象(需继承IMessage)
        if (obj is IMessage message)
        {
            using (var ms = new MemoryStream())
            {
                // 引用处理:检查对象是否有唯一ID(IHasObjectId为自定义标记接口)
                if (obj is IHasObjectId refObj && _refPool.ContainsKey(refObj.ObjectId))
                {
                    // 仅序列化引用标记+ID(紧凑格式)
                    var refData = new RefObject { IsRef = true, RefId = refObj.ObjectId };
                    refData.WriteTo(ms);
                    return ms.ToArray();
                }

                // 无引用:序列化完整数据并加入引用池
                if (obj is IHasObjectId newRefObj)
                {
                    _refPool[newRefObj.ObjectId] = message.ToByteArray();
                }
                message.WriteTo(ms);
                return ms.ToArray();
            }
        }

        return Array.Empty<byte>();
    }

    // Protobuf全量反序列化
    public override T Deserialize<T>(byte[] data) where T : class, new()
    {
        ValidateData(data);

        // 先检查是否为引用数据
        var refData = new RefObject();
        using (var ms = new MemoryStream(data))
        {
            refData.MergeFrom(ms);
            if (refData.IsRef && _refPool.TryGetValue(refData.RefId, out var rawData))
            {
                return Deserialize<T>(rawData); // 解析引用对应的原始数据
            }
        }

        // 解析原始数据
        if (Activator.CreateInstance<T>() is IMessage message)
        {
            message.MergeFrom(data);
            return message as T;
        }

        return new T();
    }

    // Protobuf增量序列化(仅传输变更字段)
    public byte[] SerializeDelta<T>(T oldObj, T newObj) where T : class, new()
    {
        ValidateInput(oldObj);
        ValidateInput(newObj);

        // 生成变更字段掩码(FieldMask为Protobuf原生类型)
        var fieldMask = GenerateFieldMask(oldObj, newObj);
        // 构建增量包(仅包含变更字段+元信息)
        var deltaPackage = new DeltaPackage
        {
            Meta = GetMetaData(newObj),
            ChangedFields = fieldMask,
            Payload = SerializeChangedFields(newObj, fieldMask)
        };

        return Serialize(deltaPackage);
    }

    // 增量反序列化(应用补丁到旧对象)
    public T ApplyDelta<T>(T oldObj, byte[] deltaData) where T : class, new()
    {
        ValidateInput(oldObj);
        ValidateData(deltaData);

        // 反序列化增量包
        var deltaPackage = Deserialize<DeltaPackage>(deltaData);
        if (deltaPackage == null) return oldObj;

        // 应用变更字段到旧对象
        ApplyChangedFields(oldObj, deltaPackage.ChangedFields, deltaPackage.Payload);
        // 更新版本号(增量更新标记)
        if (oldObj is IHasVersion versionObj)
        {
            versionObj.Version = deltaPackage.Meta.Version;
        }

        return oldObj;
    }

    // 辅助:生成变更字段掩码
    private FieldMask GenerateFieldMask<T>(T oldObj, T newObj) where T : class, new()
    {
        var fieldMask = new FieldMask();
        var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (var prop in properties)
        {
            var oldValue = prop.GetValue(oldObj);
            var newValue = prop.GetValue(newObj);
            if (!Equals(oldValue, newValue))
            {
                fieldMask.Paths.Add(prop.Name);
            }
        }
        return fieldMask;
    }

    // 辅助:仅序列化变更字段
    private byte[] SerializeChangedFields<T>(T obj, FieldMask fieldMask) where T : class, new()
    {
        var partialObj = Activator.CreateInstance<T>();
        foreach (var path in fieldMask.Paths)
        {
            var prop = typeof(T).GetProperty(path);
            if (prop != null)
            {
                prop.SetValue(partialObj, prop.GetValue(obj));
            }
        }
        return Serialize(partialObj);
    }

    // 辅助:应用变更字段到旧对象
    private void ApplyChangedFields<T>(T oldObj, FieldMask fieldMask, byte[] payload) where T : class, new()
    {
        var partialObj = Deserialize<T>(payload);
        foreach (var path in fieldMask.Paths)
        {
            var prop = typeof(T).GetProperty(path);
            if (prop != null)
            {
                prop.SetValue(oldObj, prop.GetValue(partialObj));
            }
        }
    }

    // 辅助:获取对象元信息(版本+ID)
    private MetaData GetMetaData<T>(T obj) where T : class, new()
    {
        var meta = new MetaData();
        if (obj is IHasObjectId refObj) meta.ObjectId = refObj.ObjectId;
        if (obj is IHasVersion versionObj) meta.Version = versionObj.Version;
        return meta;
    }
}

简单实例

public class SocketServerDemo : MonoBehaviour
{
    // 序列化器配置(仅需修改枚举即可切换格式)
    private readonly SerializerType _serializerType = SerializerType.Protobuf;
    // 序列化器实例(依赖抽象而非具体实现)
    private readonly ISerializer _serializer = SerializerFactory.CreateDeltaSerializer(SerializerType.Protobuf);

    // 客户端数据缓存(用于增量对比)
    private Dictionary<EndPoint, UserData> _clientDataCache = new Dictionary<EndPoint, UserData>();

    private void OnReceiveBody(byte[] body, EndPoint clientEP)
    {
        // 1. 从缓存获取旧对象
        if (!_clientDataCache.TryGetValue(clientEP, out var oldData))
        {
            oldData = new UserData();
            _clientDataCache[clientEP] = oldData;
        }

        // 2. 增量反序列化(业务层仅依赖IDeltaSerializer抽象)
        var newData = _serializer.ApplyDelta(oldData, body);

        // 3. 业务处理(更新缓存)
        _clientDataCache[clientEP] = newData;
        if (newData is IHasVersion versionObj) versionObj.Version++;

        // 4. 增量序列化响应(仅传输变更)
        var replyData = _serializer.SerializeDelta(oldData, newData);
        _server.Send(clientEP, replyData);
    }
}

存档系统(待实现)