从Socket解析ByteString失败

Mar*_*hli 1 sockets haskell

我们正在Haskell (HMB)中编写消息代理.因此,在从socket(Network.Socket)接收消息后,必须解析消息(Data.Binary).到目前为止,我们一直在测试环回(localhost) - 用于生成和解析消息.这很安静.如果我们通过从另一台机器生成消息进行基准测试,那么我们就会遇到问题:突然,解析器没有足够的字节来解析.

每个消息的前4个字节定义消息的长度,从而描述要解析的消息.如上所述,我们使用Data.Binary进行解析 - 所以这是懒惰的.出于测试目的,我们使用谷物库将前4个字节的解析切换为严格.这同样的问题.我们现在甚至试图用谷物完全解析请求,问题也仍然存在.

在代码中,您将看到我们进行线程化.但是,我们也尝试了没有通道(单线程设置),但这也没有解决问题.

下面是代码(Thread1)的一部分,其中接收的字节被写入要进一步使用/解析的通道.(如前所述,如果省略通道并直接解析输入,则没有任何变化):

runConnection :: (Socket, SockAddr) -> RequestChan -> Bool -> IO()
runConnection conn chan False = return ()
runConnection conn chan True = do
    r <- recvFromSock conn
    case (r) of
        Left e -> do
            handleSocketError conn e
            runConnection conn chan False
        Right input -> do
            threadDelay 5000 -- THIS FIXES THE PROBLEM!?
            writeToReqChan conn chan input
            runConnection conn chan True
Run Code Online (Sandbox Code Playgroud)

这是输入被解析的部分(Thread2):

runApiHandler :: RequestChan -> ResponseChan -> IO()
runApiHandler rqChan rsChan = do
  (conn, req) <- readChan rqChan
  case readRequest req of -- readRequest IS THE PARSER
    Left (bs, bo, e) -> handleHandlerError conn $ ParseRequestError e
    Right (bs, bo, rm) -> do
      res <- handleRequest rm
      case res  of
        Left e -> handleHandlerError conn e
        Right bs -> writeToResChan conn rsChan bs
  runApiHandler rqChan rsChan
Run Code Online (Sandbox Code Playgroud)

现在我想通了,如果解析过程有点延迟(参见第一个代码块中的threadDelay),一切正常.这基本上意味着,解析器不会等待从套接字接收的字节.

这是为什么?为什么解析器不等待套接字有足够的字节?我们的设置是否存在普遍错误?

bga*_*ari 5

我敢打赌,问题与解析器无关,而是由于UNIX套接字的阻塞语义.

虽然环回接口可能直接将数据包从发送方传递到接收方,但以太网接口可能需要拆分数据包以适应链路的最大传输单元(MTU).这称为数据包碎片.

系统调用的len 参数recv仅仅是接收长度的上限(例如目标缓冲区的大小); 呼叫可能产生的数据少于您的要求.引用联机帮助页,

如果套接字上没有消息可用,则接收调用等待消息到达,除非套接字是非阻塞的(请参阅fcntl(2)),在这种情况下返回值-1并将外部变量errno设置为EAGAIN或EWOULDBLOCK.接收呼叫通常会返回任何可用的数据,直到请求的数量,而不是等待收到所请求的全部金额.

因此,您可能需要多次recv调用才能检索整个数据包.如果您延迟recv操作系统可以重新组装原始数据包,则您的示例有效,因为所有片段在请求时已到达.

正如meiersi指出的那样,在Haskell世界中已经开发了各种流I/O库来解决这个问题.这些措施包括pipes,conduit,io-streams,和其他人.根据您的目标,这可能是处理此问题的自然方式.