发送到数据报套接字,不会无限期阻塞
Sending to a Datagram socket, without blocking indefinitely
我们有一些代码可以将指标发送到 SOCK_DGRAM
另一个守护进程侦听的地方和 aggregates/proxies 这些消息。打开套接字看起来像:
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
个块,查看内部结构 here and here。
这里有一个相关的问题很有帮助:When a non-blocking send() only transfers partial data, can we assume it would return EWOULDBLOCK the next call?
所以我的问题具体是:
- 一般套接字问题:我看到在此实现中
send
将阻塞(在忙等待之后)直到缓冲区中有空间。这个缓冲区与消费者的关系究竟如何?这是否意味着如果我们的 listen
-ing 守护进程缓慢或停止,send
可能会无限期阻塞?
- 如果我宁愿中止并且从不阻止,我是否需要自己制作
System.Socket.Unsafe
的分支,还是我遗漏了什么?
我只关心这里的 linux。
编辑:另外,可能让我开始做这一切的是我发现当指标收集器不是 运行 时,我的所有其他send
以上调用会引发 "connection refused" 异常。那么为什么会这样,或者这是否正常是我的另一个问题。
EDIT2:如果有人愿意帮助重现,这里有一个完整的例子来说明连接被拒绝的问题:
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
。
EDIT3:这似乎是由于 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
的实现。
这不是 Haskell 问题 -- 这是 Linux 上的预期行为,即在没有侦听进程的情况下向本地主机端口发送两个 UDP 数据包。以下 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
,假设没有任何内容正在侦听本地主机端口 6565。
如果您执行以下任一操作——(i) 发送到非本地主机,(ii) 删除 connect
调用并将 send
替换为 sendto
s,或者 (iii) 将数据包发送到一个端口,进程监听 UDP 数据包——这样你就不会收到错误。
虽然 udp(7)
的联机帮助页对此有所暗示,但该行为有点复杂且在任何地方都没有很好的记录。
您可能会发现讨论 in this Stack Overflow question 有帮助。
我们有一些代码可以将指标发送到 SOCK_DGRAM
另一个守护进程侦听的地方和 aggregates/proxies 这些消息。打开套接字看起来像:
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
个块,查看内部结构 here and here。
这里有一个相关的问题很有帮助:When a non-blocking send() only transfers partial data, can we assume it would return EWOULDBLOCK the next call?
所以我的问题具体是:
- 一般套接字问题:我看到在此实现中
send
将阻塞(在忙等待之后)直到缓冲区中有空间。这个缓冲区与消费者的关系究竟如何?这是否意味着如果我们的listen
-ing 守护进程缓慢或停止,send
可能会无限期阻塞? - 如果我宁愿中止并且从不阻止,我是否需要自己制作
System.Socket.Unsafe
的分支,还是我遗漏了什么?
我只关心这里的 linux。
编辑:另外,可能让我开始做这一切的是我发现当指标收集器不是 运行 时,我的所有其他send
以上调用会引发 "connection refused" 异常。那么为什么会这样,或者这是否正常是我的另一个问题。
EDIT2:如果有人愿意帮助重现,这里有一个完整的例子来说明连接被拒绝的问题:
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
。
EDIT3:这似乎是由于 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
的实现。
这不是 Haskell 问题 -- 这是 Linux 上的预期行为,即在没有侦听进程的情况下向本地主机端口发送两个 UDP 数据包。以下 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
,假设没有任何内容正在侦听本地主机端口 6565。
如果您执行以下任一操作——(i) 发送到非本地主机,(ii) 删除 connect
调用并将 send
替换为 sendto
s,或者 (iii) 将数据包发送到一个端口,进程监听 UDP 数据包——这样你就不会收到错误。
虽然 udp(7)
的联机帮助页对此有所暗示,但该行为有点复杂且在任何地方都没有很好的记录。
您可能会发现讨论 in this Stack Overflow question 有帮助。