import Network.Socket
import Control.Monad
import Network
import System.Environment (getArgs)
import System.IO
import Control.Concurrent (forkIO)
main :: IO ()
main = withSocketsDo $ do
putStrLn ("up top\n")
[portStr] <- getArgs
sock' <- socket AF_INET Stream defaultProtocol
let port = fromIntegral (read portStr :: Int)
socketAddress = SockAddrInet port 0000
bindSocket sock' socketAddress
listen sock' 1
putStrLn $ "Listening on " ++ (show port)
(sock, sockAddr) <- Network.Socket.accept sock'
handle <- socketToHandle sock ReadWriteMode
sockHandler sock handle
-- hClose handle putStrLn ("close handle\n")
sockHandler :: Socket -> Handle -> IO ()
sockHandler sock' handle = forever $ do
hSetBuffering handle LineBuffering
forkIO $ commandProcessor handle
commandProcessor :: Handle -> IO ()
commandProcessor handle = do
line <- hGetLine handle
let (cmd:arg) = words line
case cmd of
"echo" -> echoCommand handle arg
"add" -> addCommand handle arg
_ -> do hPutStrLn handle "Unknown command"
echoCommand :: Handle -> [String] -> IO ()
echoCommand handle arg = do
hPutStrLn handle (unwords arg)
addCommand :: Handle -> [String] -> IO ()
addCommand handle [x,y] = do
hPutStrLn handle $ show $ read x + read y
addCommand handle _ = do
hPutStrLn handle "usage: add Int Int"
I’m noticing some quirks in it’s behavior, but the one I want to address for the moment is what happens when a client disconnects with the server. When that happens, the server throws the following exception endlessly, and will not respond to further client connections.
strawboss: : hGetLine: end of file
I’ve tried flushing the handle, and closing the handle. I think that closing the handle is the right thing to do, but I cannot figure out where te correct place to close the handle is. So my first question is: Is the solution to this problem a judicious hClose placement in the code? If not, where does the problem lie?
There are several problems in this code. The main one is that you have your
foreverin the wrong place. What I assume you want is to endlessly accept connections, and deal with them insockHandler, whereas your code currently only ever accepts a single connection, and then endlessly forks off worker threads to handle that single connection in parallel. This causes the mess you’re experiencing.Instead, you’ll want to move the
forevertomain:However, you will still get an exception when a client disconnects, because you’re not checking if the connection has ended before calling
hGetLine. We can fix this by adding usinghIsEOF. You can then safely do ahCloseon the handle once you know you’re done with it.Here’s your code with these modifications in place. I also took the liberty of restructuring your code a little.