Так же, как комментарий общего стиля, я бы разделил реакцию сервера на подключение клиента к отдельной функции. Это облегчает чтение на мой взгляд
main = withSocketsDo $ do
socket <- listenOn port
accept socket >>= handleClientRequest socket
handleClientRequest socket (handle, host, portno) = do
hSetBuffering handle LineBuffering
msg <- hGetLine handle
putStrLn $ "The client says: " ++ msg
hClose handle
sClose socket
putStrLn "Server is done."
Теперь мы, вероятно, хотим, чтобы цикл продолжался бесконечно, так как большинство серверов работают именно так. Поэтому мы используем вечно (из Control.Monad)
main = withSocketsDo $ do
socket <- listenOn port
forever $ accept socket >>= handleClientRequest socket
handleClientRequest socket (handle, host, portno) = do
hSetBuffering handle LineBuffering
msg <- hGetLine handle
putStrLn $ "The client says: " ++ msg
hClose handle
sClose socket
putStrLn "Server is done."
и отсюда, способ использования forkIO становится вполне понятным
main = withSocketsDo $ do
socket <- listenOn port
forever $ accept socket >>= forkIO . (handleClientRequest socket)
handleClientRequest socket (handle, host, portno) = do
hSetBuffering handle LineBuffering
msg <- hGetLine handle
putStrLn $ "The client says: " ++ msg
hClose handle
sClose socket
putStrLn "Server is done."