We have some code to send metrics to a SOCK_DGRAM
where another daemon listens and aggregates/proxies these messages. Opening the socket looks like:
sock <- socket
(ai :: AddressInfo Inet Datagram UDP):_ <- getAddressInfo (Just "127.0.0.1") Nothing aiNumericHost
connect s (socketAddress ai) { port }
return sock
And at the moment we write to it like:
send sock payload mempty
I want to ensure the call above doesn't block for very long (or at the very least doesn't block indefinitely), but my understanding of unix sockets is not very deep and I'm having trouble understanding how exactly send
blocks, looking at internals here and here.
There is a related question here that was helpful: When a non-blocking send() only transfers partial data, can we assume it would return EWOULDBLOCK the next call?
So my questions specifically are:
- General socket question: I see in this implementation
send
is going to block (after busy-waiting) until there is room in the buffer. How exactly is this buffer related to the consumer? Does this meansend
may block indefinitely if ourlisten
-ing daemon is slow or stalls? - If I would rather abort and never block, do I need to make my own fork of
System.Socket.Unsafe
, or am I missing something?
I'm only concerned with linux here.
EDIT: Also, and what probably got me started with all of this is I find that when the metrics collector is not running, that every other of my send
calls above throws a "connection refused" exception. So why that is, or whether it's normal is another question I have.
EDIT2: Here's a complete example illustrating the connection refused issue if anyone would like to help 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"
I'm using socket-0.5.3.0
.
EDIT3: this seems to be due to the connect
call, somehow. (Testing on sockets
latest):
{-# 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"
As I understand it we should be able to use connect
(at least the underlying syscall) to set the default send address. I haven't dug into the implementation of connect
in the library yet.
I've opened this: https://github.com/lpeterse/haskell-socket/issues/55