
Jon Fairbairn wrote:
Is recvFrom meant to be a one-shot function i.e. the socket is only closed when the process exits?
The implementation is:
recvFrom host port = do ip <- getHostByName host let ipHs = hostAddresses ip s <- listenOn port let waiting = do ~(s', SockAddrInet _ haddr) <- Socket.accept s he <- getHostByAddr AF_INET haddr if not (any (`elem` ipHs) (hostAddresses he)) then do sClose s' waiting else do h <- socketToHandle s' ReadMode msg <- hGetContents h return msg
message <- waiting return message
This is rather more powerful than recvFrom in C, isn't it?
Yes, very much so.
Perhaps it's misnamed: C's recvFrom deals with finite messages, but with the above I can receive an infinite list, which is the source of the problem, even if rather cool.
Well, C's recvfrom is normally only used with UDP, where you deal with individual packets rather than connections. It's essentially recv() but with the ability to retrieve the packet's source IP+port as well as its payload. And recv() is just read() with some flags.
Note that the listening socket s is passed to accept then forgotten about. If it was accessible, it would be possible to either accept further connections on it, or to close it. As it stands, it will remain open and unused for the duration of the calling process.
So the problem is the same as with hGetContents in general, compounded by the calling programme not having access to the socket, so it can't close it even if it knows it's finished with the data.
No, it's worse than that. There are two sockets involved (the listening socket plus the connection-specific socket), and you don't get access to either of them. AFAIK, you can live without the connection socket, provided that you consume all of the data, and the connection is purely one-way (i.e. the client sends the data then closes the connection; it doesn't expect the recipient to close its end). The real problem is that the listening socket hangs around, tying up the port. Actually, recvFrom should be able to close the listening socket as soon as it has accepted the connection.
Right. If listenOn and accept are in Network, sClose should be in there too. That would at least provide an API which is usable for the simplest programs.
Agreed, and recvFrom seems to need to be something else, though the problem could be ameliorated by making withSocketsDo close any leftover sockets. You'd then have to use it for both Linux and Windows.
It would have to only close the sockets which were created via the Haskell networking code. You wouldn't want it to close e.g. an X11 connection which Gtk+Hs/GLUT/etc were using, or stdin if that happened to be a socket.
OTOH, the core problem with Network.recvFrom is essentially that it appears to be a misguided attempt to provide a symmetric counterpart to Network.sendTo. While the low-level sendTo/recvFrom functions may be roughly symmetric, at a higher level, the client and server ends of a connection aren't at all symmetric.
Given that, recvFrom could :: HostName -> Socket -> IO String. We'd have to call listenOn to get the socket, but that seems a small hardship compared to losing the use of the port.
If anything, it should probably just be:
recvFrom :: Socket -> IO String
The behaviour of discarding connections until it gets one from the
specified hostname seems rather arbitrary, IMHO.
The more I think about it, the more that Network.recvFrom looks like
someone was hell-bent on producing a complement to Network.sendTo,
regardless of the benefit (or even sanity) of doing so.
Actually, if someone desperately wants a complement to sendTo, a
recvTo function would be more useful than recvFrom. I.e. connect to a
server and read data until the server closes the connection (usable
with services such as systat, netstat, daytime etc).
Similarly, sendFrom (i.e. accept a connection and send data) would be
just as useful as recvFrom.
--
Glynn Clements