【问题标题】:Do something if the protocol buffer contract isn't followed both ends?如果协议缓冲区合同两端都没有遵循,该怎么办?
【发布时间】:2022-10-24 02:52:35
【问题描述】:

刚开始使用协议缓冲区并有点困惑。

我编写了自己的原始 TCP 套接字服务器,并在顶部添加了协议缓冲区而不是 JSON。

我想确保遵循协议缓冲区消息的合同并处理它们不存在的情况,这可能吗?

例如,如果客户端有一个协议缓冲区类

[ProtoContract]
public class WelcomeMessage
{
    [ProtoMember(2)]
    public string Message {get;set;}
}

但是服务器有一个

[ProtoContract]
public class WelcomeClient
{
    [ProtoMember(1)]
    public int MagicNumber {get;set;}
    [ProtoMember(2)]
    public string WelcomeMessage {get;set;}
}

MagicNumber 的默认值设置为 0,但这会导致意外情况。我该如何处理这类案件?

我只是为了从字节数组反序列化:

public class ProtocolBufferUtilities
{
    public static T Deserialize<T>(byte[] data)
    {
        using var stream = new MemoryStream(data);
        var result = Serializer.Deserialize<T>(stream);

        return result;
    }
}

【问题讨论】:

    标签: c# protocol-buffers protobuf-net


    【解决方案1】:

    如果客户端和服务器正在谈论不同的合约,那么您会期望它们之间发送的任何消息都可能导致接收端的反序列化器返回错误。丢弃消息、连接等;它行不通。

    首先将您的消息分开!

    但是让我们退后一步,使用原始套接字。 GPB 线格式不是自我划分的,您必须有另一种方式来判断一个消息的字节流何时停止而另一个消息何时开始。一个简单的方法是采用 ZeroMQ,它是面向消息的(而不是像原始套接字那样面向流)。

    出错时断开连接

    然后,ZeroMQ 可用于传达双方很高兴他们正在谈论同一个合同。例如,如果接收端收到一条消息,尝试反序列化消息并返回错误,则接收端可以关闭其连接端,并清理套接字(摆脱任何未使用的消息)。

    ZeroMQ 允许发送端监控此类事件;如果发送消息后跟“连接被另一端关闭”,这是一个很好的暗示,一切都不好。

    这其实是我做这种事情的方式(虽然不是用ZeroMQ,也不是用GPB,但原理是一样的)。如果因为消息没有意义而被迫断开连接,我稍后会恢复连接,看看另一端是否开始有意义。

    这样做的好处是,如果另一端已停止,更新到正确的版本并重新启动,则不必重新启动整个系统(客户端和服务器)。接收端只是坐在那里,建立连接,尝试和失败,直到有人把事情做对。

    使用 .proto 文件?

    您正在以“代码优先”样式构建合约,而不是使用 .proto 文件和 protoc 编译器生成 C# 代码的“模式优先”方法。

    我更喜欢后者,因为它更容易利用 Google 可能在 protoc 中实现的生成代码的改进;你大多只是重新编译。此外,项目的不同部分可以很容易地用不同的语言编写,即定义合同的 .proto 文件。

    合同不完整

    使用 GPB,合同是不完整的。无法表示数字的可接受值的范围,或 .proto 文件中列表的可接受长度。在您的示例中,如果发送方要发送一个很长的字符串,则接收方别无选择,只能将其吸收并处理。

    因此,对于 GPB,处理此问题的唯一方法是在 .proto 文件之外同意此类约束。

    也许在您遵循的代码优先方法中,可能有一个验证器方法可以检查一切是否正常,并且(因为它是代码优先)它是约束的“单一事实点”。

    其他技术做得更好。 ASN.1 模式语法允许定义对值和长度的约束,并且生成的代码将拒绝序列化超出规范的对象,并拒绝反序列化不符合要求的传入线格式。

    ASN.1 的一些工具执行“流解串器”,它在从流中读取数据时解析数据(请注意,某些 ASN.1 线格式是自划分的!)。这意味着,当从网络套接字读取数据时,它会在那里被评估,然后包括值和长度限制。这非常有用,因为反序列化错误可以在错误的套接字流中的第一个字节上返回。这允许程序在收到第一个错误字节时断开连接。可以说,这可以成为一个非常强大的安全功能:“说话有道理,或者我在你第一次打嗝时关闭套接字”。这是抵御缓冲区溢出攻击的好方法。

    【讨论】:

      猜你喜欢
      • 2012-11-25
      • 2014-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-18
      • 1970-01-01
      • 2021-12-19
      • 2011-11-15
      相关资源
      最近更新 更多