我正在尝试为二进制网络协议编写客户端。 所有网络操作都是通过单个TCP连接执行的,因此就是这样 来自服务器的输入是连续的字节流。 然而,在应用层,服务器在概念上发送数据包 流,并且客户端继续读取,直到它知道已收到数据包 在发送自己的回复之前,完整的。
完成这项工作所需的大量工作包括解析和生成 二进制数据,我正在使用Data.Serialize模块。
服务器在TCP流上向我发送“数据包”。 数据包不一定由换行符终止,也不是预定的 尺寸。 由预定数量的字段组成,字段通常开始 用4字节数字描述该字段的长度。 在Data.Serialize的帮助下,我已经有了解析ByteString的代码 将此数据包的版本转换为更易于管理的类型。
我希望能够用这些属性编写一些代码:
所以简而言之, 是否可以组合使用我当前的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
为
等待这个街区的其余部分到达。虽然读这个属性
似乎有效的块大小有助于从磁盘进行高效读取
我正在寻找足够的字节来解析我的数据。
答案 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”。因此,网络子系统没问题,问题出在其他地方 - 很可能是在你的解析器中。