首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Haskell中的ByteString解析与网络IO混合

Haskell中的ByteString解析与网络IO混合
EN

Stack Overflow用户
提问于 2013-03-12 06:21:08
回答 1查看 858关注 0票数 6

背景

我试图为二进制网络协议编写一个客户端。所有网络操作都是在一个TCP连接上执行的,因此从这个意义上来说,来自服务器的输入是一个连续的字节流。然而,在应用层,服务器从概念上在流上发送数据包,客户端在发送自己的响应之前一直读取,直到它知道数据包已经全部接收为止。

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

问题所在

服务器在TCP流上向我发送一个“数据包”。分组不一定以换行符终止,也不一定具有预定大小。它确实由一个预先确定的字段数组成,字段通常以描述该字段长度的4个字节数开始。在Data.Serialize的帮助下,我已经有了将这个包的ByteString版本解析为更易于管理的类型的代码。

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

  1. 解析只定义一次,最好是在我的序列化实例中。我不希望在IO monad中进行额外的解析来读取正确的字节数。
  2. 当我试图解析一个给定的数据包而不是所有的字节已经到达时,懒惰的IO只会等待额外的字节到达。
  3. 相反,当我试图解析一个给定的数据包并且它的所有字节已经到达时,IO不再阻塞。也就是说,我想从服务器上读取足够多的流来解析我的类型并形成一个返回响应。如果IO阻塞了足够多的字节来解析我的类型,那么客户机和服务器就会陷入僵局,它们都在等待对方提供更多的数据。
  4. 在发送自己的响应后,我可以通过解析服务器期望的下一种类型的数据包来重复这个过程。

因此,简单地说,是否有可能利用我当前的ByteString解析代码和懒惰的IO来准确地从网络读取正确的字节数?

我试过的

我尝试将惰性ByteStreams与我的Data.Serialize实例结合使用,如下所示:

代码语言:javascript
复制
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正在等待这个块的其余部分到达。虽然读取有效块的这一属性有助于从磁盘进行有效读取,但它似乎妨碍了我读取足够多的字节来解析数据。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-03-12 08:29:10

解析器有问题,它太急切了。由于某种原因,它很可能需要消息后面的下一个字节。hGetContentsbytestring没有阻止等待整个块。它在内部使用hGetSome

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

代码语言:javascript
复制
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

客户懒洋洋地阅读整个内容:

代码语言:javascript
复制
import qualified Data.ByteString.Lazy as BSL
import System.IO
import Network

port :: Int
port = 1234

main :: IO ()
main = withSocketsDo $ do
  h <- connectTo "localhost" $ PortNumber $ fromIntegral port
  bs <- BSL.hGetContents h
  BSL.putStrLn bs
  hClose h

如果您尝试同时运行这两种方式,那么您将看到客户机每秒钟打印一次"hello“。所以,网络子系统很好,问题就在其他地方--很可能是在解析器中。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15354636

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档