我正在尝试为二进制网络协议编写客户端.所有网络操作都通过单个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...
Run Code Online (Sandbox Code Playgroud)
这似乎主要在上面的第3点失败:即使在已经到达足够数量的字节来解析MyType之后它仍然被阻止.我强烈怀疑这是因为ByteStrings一次以给定的块大小读取,并且L.hGetContents正在等待该块的其余部分到达.虽然读取有效块大小的这个属性有助于从磁盘进行有效读取,但它似乎正在试图读取足够的字节来解析我的数据.
你的解析器有问题,它太急切了.由于某种原因,它很可能需要消息之后的下一个字节.hGetContents从bytestring不会阻塞等待整个块.它在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
Run Code Online (Sandbox Code Playgroud)
客户端懒洋洋地读取整个内容:
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
Run Code Online (Sandbox Code Playgroud)
如果您尝试同时运行两者,您将看到客户端每秒打印"hello".所以,网络子系统还可以,问题出在其他地方 - 最有可能是你的解析器.
| 归档时间: |
|
| 查看次数: |
767 次 |
| 最近记录: |