gpt4 book ai didi

sockets - 从 Socket 解析 ByteString 失败

转载 作者:行者123 更新时间:2023-12-02 16:05:20 24 4
gpt4 key购买 nike

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

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

在代码中您会看到我们进行了线程处理。但是,我们也尝试过不使用 channel (单线程设置),但这也没有解决问题。

这是代码的一部分(线程1),其中接收到的字节被写入 channel 以供进一步使用/解析。 (如前所述,如果我们省略 channel 并直接解析输入,则不会发生任何变化):

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

这是解析输入的部分(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

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

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

最佳答案

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

当一个环回时接口(interface)可能会将数据包直接从发送者传递到接收者,以太网接口(interface)可能需要分解数据包以适应最大链路的传输单元 (MTU)。这称为数据包碎片。

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

If no messages are available at the socket, the receive calls wait for a message to arrive, unless the socket is nonblocking (see fcntl(2)), in which case the value -1 is returned and the external variable errno is set to EAGAIN or EWOULDBLOCK. The receive calls normally return any data available, up to the requested amount, rather than waiting for receipt of the full amount requested.

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

meiersi指出,Haskell 世界中开发了多种流 I/O 库来解决这个问题等。其中包括pipes , conduit , io-streams , 和别的。根据您的目标,这可能是处理此问题的自然方法。

关于sockets - 从 Socket 解析 ByteString 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30375962/

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