我有一个用C ++编写的TCP Server,它希望以以下格式发出请求:
struct Header {
int headerField1;
int headerField2;
}
struct Request {
Header header;
char[14] uniqueID;
char[12] password;
}
我想实现一个客户端以将此请求发送到Haskell中的服务器
我已经尝试过Data.Binary.encode,但无法解决问题。 我更困惑如何在Haskell中使用任意大小的类型。即char [12];
Haskell代码:
data Header = Header
{
headerField1 :: Word32
, headerField2 :: Word32
} deriving (Generic)
instance Binary Header
data Request = Request
{
header :: Header
, uniqueID :: ByteString -- I am not sure which data type to use here.
, password :: ByteString -- Same as above, as length is defined 12 bytes which is arbitrary.
} deriving (Generic)
instance Binary Request
我已经向数据解析器写入了一个自定义字节串,由于没有任意大小的类型,因此它对于标头非常有用
parseHeader = do
Header <$>
getWord32le <*>
getWord32le
我正在寻找一种Haskell方法来对定义为ByteString的数据包结构进行序列化和反序列化(反之亦然),以及一种创建任意大小的数据类型的方式-char [12]
答案 0 :(得分:1)
要首先解决主要问题,可以使用getByteString
(或getLazyByteString
)解析已知长度的字节串。因此,Request
的二进制解析器可能是:
parseRequest :: Get Request
parseRequest =
Request
<$> parseHeader
<*> getByteString 14
<*> getByteString 12
如果您还有一个序列化器,例如putRequest
,则可以将其与解析器一起放在Binary
实例中,从而可以方便地使用库的其他功能(但您不能不必)。
instance Binary Request where
get = parseRequest
put = putRequest
为避免混淆密码和ID,将它们包装成新类型似乎是一个好主意:
newtype UniqueID = MkUniqueID ByteString -- length 14
newtype Password = MkPassword ByteString -- length 12
在对它们执行操作时,请确保它们不会构造错误长度的值。然后,您可以在导出类型时隐藏构造函数,以使用户无法破坏这些不变式。
这些类型的解析器是您指定所需长度的地方:
parseUniqueID :: Get UniqueID
parseUniqueID = MkUniqueID <$> getByteString 14
parsePassword :: Get Password
parsePassword = MkPassword <$> getByteString 12
现在,这使Request
的定义更具描述性,在Haskell代码中混合使用密码和ID的唯一方法是在序列化/反序列化中弄错顺序,因此这减少了在其他地方出错的可能性。
data Request = Request
{ header :: Header
, uniqueID :: UniqueID
, password :: Password
}
parseRequest :: Get Request
parseRequest =
Request
<$> parseHeader
<*> parseUniqueID
<*> parsePassword