在Haskell中混合使用ByteString解析和网络IO

时间:2013-03-12 06:21:09

标签: haskell

背景

我正在尝试为二进制网络协议编写客户端。 所有网络操作都是通过单个TCP连接执行的,因此就是这样 来自服务器的输入是连续的字节流。 然而,在应用层,服务器在概念上发送数据包 流,并且客户端继续读取,直到它知道已收到数据包 在发送自己的回复之前,完整的。

完成这项工作所需的大量工作包括解析和生成 二进制数据,我正在使用Data.Serialize模块。

问题

服务器在TCP流上向我发送“数据包”。 数据包不一定由换行符终止,也不是预定的   尺寸。 由预定数量的字段组成,字段通常开始   用4字节数字描述该字段的长度。 在Data.Serialize的帮助下,我已经有了解析ByteString的代码   将此数据包的版本转换为更易于管理的类型。

我希望能够用这些属性编写一些代码:

  1. 解析只定义一次,最好是在我的Serialize实例中。 我宁愿不在IO monad中进行额外的解析来读取正确的字节数。
  2. 当我尝试解析给定的数据包而不是所有字节都已到达时,懒惰 IO将等待额外的字节到达。
  3. 相反,当我尝试解析一个给定的数据包时,的所有字节都已到达 IO不再阻止。也就是说,我想要阅读足够的流 从服务器解析我的类型并形成一个回复发送的响应。如果IO 即使在足够的字节到达后解析我的类型,然后客户端 和服务器将陷入僵局,每个人都在等待来自另一个的更多数据。
  4. 在我发送自己的回复之后,我可以通过解析下一个类型重复该过程 我期望从服务器获得的数据包。
  5. 所以简而言之, 是否可以组合使用我当前的ByteString解析代码   使用惰性IO来从网络中读取正确的字节数?

    我尝试过什么

    我尝试将lazy ByteStreams与我的Data.Serialize实例结合使用,就像 这样:

    import Network
    import System.IO
    import qualified Data.ByteString.Lazy as L
    import Data.Serialize
    
    data MyType
    
    instance Serialize MyType
    
    main = withSocketsDo $ do
      h <- connectTo server port
      hSetBuffering h NoBuffering
      inputStream <- L.hGetContents h
      let Right parsed = decodeLazy inputStream :: Either String MyType
      -- Then use parsed to form my own response, then wait for the server reply...
    

    这似乎主要在上面的第3点失败:即使在足够之后它也会被阻止 到达解析MyType的字节数。我强烈怀疑这是因为 ByteStrings一次以给定的块大小读取,L.hGetContents为 等待这个街区的其余部分到达。虽然读这个属性 似乎有效的块大小有助于从磁盘进行高效读取 我正在寻找足够的字节来解析我的数据。

1 个答案:

答案 0 :(得分:7)

你的解析器出了问题,它太急切了。由于某种原因,它很可能需要消息之后的下一个字节。来自hGetContents的{​​{1}}不会阻止等待整个块。它在内部使用bytestring

我创建了简单的测试用例。服务器每秒发送“hello”:

hGetSome

客户端懒洋洋地读取整个内容:

import Control.Concurrent
import System.IO
import Network

port :: Int
port = 1234

main :: IO ()
main = withSocketsDo $ do
  s <- listenOn $ PortNumber $ fromIntegral port
  (h, _, _) <- accept s

  let loop :: Int -> IO ()
      loop 0 = return ()
      loop i = do
        hPutStr h "hello"
        threadDelay 1000000
        loop $ i - 1
  loop 5

  sClose s

如果您尝试同时运行两者,您将看到客户端每秒打印“hello”。因此,网络子系统没问题,问题出在其他地方 - 很可能是在你的解析器中。