如果协议缓冲区协定没有遵循两端,该怎么办?



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

我已经编写了自己的原始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;
}
}

如果客户端和服务器正在讨论不同的合约,那么您会认为它们之间发送的任何消息都可能导致接收端的取消序列化程序返回错误。删除消息、连接等;这行不通。

首先分离您的消息

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

出现错误时丢弃连接

ZeroMQ可以用来传达双方都很高兴他们在谈论同一个合同。例如,如果接收端收到一条消息,尝试对消息进行去串行化并返回错误,则接收端可以关闭其连接端,并清理套接字(清除任何未使用的消息(。

ZeroMQ允许发送端监视此类事件;如果发送消息之后是"发送";连接被另一端闭合";,这是一个很好的暗示,表明一切都不顺利。

事实上,这就是我做这种事情的方式(虽然不是ZeroMQ和GPB,但原则是一样的(。如果因为消息没有意义而被迫断开连接,我稍后会重新连接,看看另一端是否开始有意义。

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

是否使用.proto文件

你的合同是建立在一个";代码优先";风格,而不是";模式优先";方法使用.proto文件和protoc编译器生成C#代码。

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

合同不完整

与GPB签订的合同不完整。没有办法表达一个数字的可接受值的范围,也没有办法表达.proto文件中列表的可接受长度。在你的例子中,如果发送者想发送一个很长的字符串,接收者别无选择,只能接受并处理它

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

也许在您所遵循的代码优先方法中,可以有一个验证器方法来检查一切是否正常,并且(因为它是代码优先的(它是";单点真理";对于约束。

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

ASN.1的一些工具";流去序列化器";,它在从流中读取数据时解析数据(请注意,有些ASN.1线格式是自标定的!(。这意味着,当数据从网络套接字中读取时,会在那里进行评估,包括根据值和长度约束进行评估。这非常有用,因为可以在套接字流中错误的第一个字节上返回取消序列化错误。这允许程序在收到第一个错误字节时断开连接。可以说,这可以成为一个相当强大的安全功能:;说话有道理,否则我会在你第一次打嗝时关上插座;。这是抵御缓冲区溢出攻击的好方法。

最新更新