gpt4 book ai didi

haskell - 在 Haskell 中混合 ByteString 解析和网络 IO

转载 作者:行者123 更新时间:2023-12-02 14:40:44 26 4
gpt4 key购买 nike

背景

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

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

问题

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

我希望能够编写一些具有这些属性的代码:

  1. 解析仅定义一次,最好在我的 Serialize 实例中定义。我不想在 IO monad 中进行额外的解析来读取正确的字节数。
  2. 当我尝试解析给定的数据包但并非所有字节都已到达时,懒惰IO 只会等待额外的字节到达。
  3. 相反,当我尝试解析给定的数据包并且其所有字节到达时IO 不再阻塞。也就是说,我想读足够的流从服务器解析我的类型并形成要发回的响应。如果IO即使在足够的字节到达来解析我的类型之后也会阻塞,然后客户端服务器将陷入僵局,双方都在等待对方的更多数据。
  4. 发送自己的回复后,我可以通过解析下一个类型来重复该过程我期望从服务器发送的数据包数量。

简单来说,是否可以结合使用我当前的 ByteString 解析代码 使用惰性 IO 从网络上读取正确数量的字节?

我尝试过的

我尝试将惰性字节流与我的 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 的字节数。我强烈怀疑这是因为一次使用给定的 block 大小读取 ByteString,并且 L.hGetContents 是等待该 block 的其余部分到达。虽然读取这个属性有效的 block 大小有助于从磁盘进行有效的读取,它似乎是妨碍我读取足够的字节来解析我的数据。

最佳答案

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

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

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

客户端懒惰地读取全部内容:

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”。所以,网络子系统没问题,问题出在其他地方——很可能在你的解析器中。

关于haskell - 在 Haskell 中混合 ByteString 解析和网络 IO,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15354636/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com