背景:
我发现自己经常使用 F# Records。目前我正在研究一个专有二进制协议(一种设计非常奇怪的协议......)的数据包解析和重放项目。
我们为数据包定义骨架记录。
type bytes = byte array
type packetSkeleton = {
part1 : bytes
part2 : bytes
... }
现在,很容易使用它来“剖析”我们的数据包(实际上只是给字节字段命名)。
let dissect (raw : bytes) =
let slice a b = raw.[a..b]
{ part1 = slice 0 4
part2 = slice 4 5
... }
即使对于较长的数据包,这也很有效,如果切片有可预测的模式,我们甚至可以使用一些简洁的递归函数。
所以我剖析了数据包,提取了我需要的字段,并使用我从剖析中获取的字段创建了一个基于 packetSkeleton 的数据包,现在看起来有点尴尬:
let createAuthStub a b c d e f g h ... =
{ part1 = a; part2 = b
part3 = d; ...
}
然后,在创建填充的存根之后,我需要将其反序列化为可以放在网络上的表单:
(* packetSkeleton -> byte array *)
let deserialise (packet : packetSkeleton) =
[| packet.part1; packet.part2; ... |]
let xab = dissect buf
let authStub = createAuthStub xab.part1 1 2 xab.part9 ...
deserialise authStub |> send
所以最终我有 3 个区域,记录类型,为给定数据包创建记录,以及反序列化的字节数组。有些事情告诉我,就代码清晰度而言,这对我来说是一个糟糕的设计选择,即使在这个早期阶段,我也已经感觉到它开始让我感到震惊。
问题:
a) 我是否为这样的项目使用了正确的数据类型?我的方法正确吗?
b)我是否应该放弃尝试让这段代码感觉干净?
因为我有点通过触摸来编码这个,我会很感激一些见解!
PS 我意识到这个问题非常适合 C,但 F# 更有趣(另外,稍后对解剖器的验证听起来很吸引人)!