发送到数据报套接字,无限制地阻塞

问题描述 投票:3回答:1

我们有一些代码将指标发送到SOCK_DGRAM,其中另一个守护进程监听并聚合/代理这些消息。打开套接字看起来像:

sock <- socket
(ai :: AddressInfo Inet Datagram UDP):_ <- getAddressInfo (Just "127.0.0.1") Nothing aiNumericHost
connect s (socketAddress ai) { port }
return sock

目前我们写信给它:

send sock payload mempty

我想确保上面的调用不会阻塞很长时间(或者至少不会无限期地阻塞),但我对unix套接字的理解不是很深,而且我无法理解send是如何阻塞的,在内部herehere

这里有一个相关的问题很有帮助:When a non-blocking send() only transfers partial data, can we assume it would return EWOULDBLOCK the next call?

所以我的问题具体是:

  • 一般套接字问题:我在这个实现中看到send将要阻塞(在忙等待之后),直到缓冲区中有空间。这个缓冲区究竟与消费者有什么关系?这是否意味着如果我们的send-ing守护进程缓慢或停止,listen可能会无限期地阻止?
  • 如果我宁愿中止并且永远不会阻止,我是否需要制作自己的System.Socket.Unsafe分叉,或者我错过了什么?

我这里只关心linux。

编辑:此外,可能让我开始所有这一切是我发现当度量收集器没有运行时,我上面的所有其他send调用抛出“连接被拒绝”异常。那么为什么会这样,或者它是否正常是我的另一个问题。

编辑2:这是一个完整的例子,说明如果有人想帮助repro连接被拒绝的问题:

import Data.Functor
import System.Socket
import System.Socket.Family.Inet

repro :: IO ()
repro = do
  let port = 6565
  (s :: Socket Inet Datagram UDP) <- socket
  (ai :: AddressInfo Inet Datagram UDP):_ <- getAddressInfo (Just "127.0.0.1") Nothing aiNumericHost
  connect s (socketAddress ai) { port }

  putStrLn "Starting send"
  void $ send s "FOO" mempty
  void $ send s "BAR" mempty
  putStrLn "done"

我正在使用socket-0.5.3.0

编辑3:这似乎是由于connect调用,不知何故。 (最新测试sockets):

{-# LANGUAGE ScopedTypeVariables, OverloadedStrings, NamedFieldPuns #-}
import Data.Functor
import System.Socket
import System.Socket.Protocol.UDP
import System.Socket.Type.Datagram
import System.Socket.Family.Inet

repro :: IO ()
repro = do
  (s :: Socket Inet Datagram UDP) <- socket

  -- Uncommenting raises eConnectionRefused, everytime:
  -- connect s (SocketAddressInet  inetLoopback  6565     :: SocketAddress Inet)
  putStrLn "Starting send"
  void $ sendTo s "FOO" mempty (SocketAddressInet  inetLoopback  6565     :: SocketAddress Inet)
  void $ sendTo s "BAR" mempty (SocketAddressInet  inetLoopback  6565     :: SocketAddress Inet)
  putStrLn "done"

据我了解,我们应该能够使用connect(至少是底层系统调用)来设置默认发送地址。我还没有挖掘库中connect的实现。

我打开了这个:https://github.com/lpeterse/haskell-socket/issues/55

linux sockets haskell nonblocking datagram
1个回答
2
投票

这不是Haskell问题 - 它是在Linux上发送两个UDP数据包到没有监听进程的localhost端口时的预期行为。以下C程序:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/udp.h>

int main()
{
    int s = socket(AF_INET, SOCK_DGRAM, 0);
    struct sockaddr_in dstaddr = { AF_INET, htons(6565), {htonl(0x7f000001)} };
    if (connect(s, (struct sockaddr*) &dstaddr, sizeof(dstaddr)))
        perror("connect");
    if (send(s, "FOO", 3, 0) == -1)
        perror("first send");
    if (send(s, "BAR", 3, 0) == -1)
        perror("second send");
    return 0;
}

将打印second send: Connection refused,假设没有正在侦听localhost端口6565。

如果您执行以下任何一项操作 - (i)发送给非本地主机,(ii)删除connect调用并将sends替换为sendtos,或(iii)将数据包发送到具有侦听UDP的进程的端口数据包 - 那么你就不会得到错误。

虽然udp(7)的联机帮助文件暗示了这一点,但这种行为有点复杂,而且没有任何记录。

你可能会发现讨论in this Stack Overflow question很有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.