4
votes

I'm new to trying to program with Unix sockets, and struggling to get a simple server working. I'd like this to stay running and print messages it receives, but instead it prints the first message and exits. Depends on network, and bytestring.

module Main where

import Network.Socket hiding (send, sendTo, recv, recvFrom)
import Network.Socket.ByteString
import qualified Data.ByteString.Char8 as C
import Control.Monad

main :: IO ()
main = withSocketsDo $ do
       sock <- socket AF_UNIX Stream 0 -- and try UDP?
       bind sock (SockAddrUnix "/tmp/test_sock.ipc")
       listen sock maxListenQueue -- TODO is maxListenQueue what we want?
       (conn, _) <- accept sock
       talk conn
       close conn
       close sock
       putStrLn "DONE"

    where
      talk :: Socket -> IO ()
      talk conn =
          do msg <- recv conn 1024
             unless (C.null msg) $ do
               C.putStrLn msg 
               talk conn

I'm testing with socat, which I also don't really know how to use:

echo "FOOOOO" | socat - UNIX-CONNECT:/tmp/test_sock.ipc

Any pointers on the haskell code, and what I might be misunderstanding about unix sockets would be helpful.

EDIT Using Datagram instead of Stream I'm able to get more or less what I want:

main :: IO () main = withSocketsDo $ do sock <- socket AF_UNIX Datagram 0 -- and try UDP? bind sock (SockAddrUnix "/tmp/test_sock.ipc") -- for Datagram: talk sock close sock putStrLn "DONE"

where
  talk :: Socket -> IO ()
  talk conn =
      do msg <- recv conn 1024
         unless (C.null msg) $ do
           C.putStrLn msg 
           talk conn

And I can test successfully with:

echo "FOOOOO" | socat - UNIX-SENDTO:/tmp/test_sock.ipc

I think I'm interested in datagram sockets anyway, but I'll leave this open if anyone wants to explain things to me.

1

1 Answers

4
votes

Your code only runs "accept" once, so it can only handle one connection. If you want to handle multiple connections, the "accept" part has to be repeated, not just the "recv" part.

module Main where

import Network.Socket hiding (send, sendTo, recv, recvFrom)
import Network.Socket.ByteString
import qualified Data.ByteString.Char8 as C
import Control.Monad

main :: IO ()
main = withSocketsDo $ do
       sock <- socket AF_UNIX Stream 0 -- and try UDP?
       bind sock (SockAddrUnix "/tmp/test_sock.ipc")
       listen sock maxListenQueue -- TODO is maxListenQueue what we want?
       go sock
       close conn
       close sock
       putStrLn "DONE"

    where
      go sock = do
        (conn,_) <- accept sock
        talk conn
        go sock
      talk :: Socket -> IO ()
      talk conn =
          do msg <- recv conn 1024
             unless (C.null msg) $ do
               C.putStrLn msg 
               talk conn