{-
When running this on Windows, watch the Handles and Threads counters in
Windows Task Manager. These are hidden by default, but can be displayed if you
go to View -> Select Columns.
-}
import Prelude hiding (log)
import Log
import Control.Concurrent
import Control.Monad.IO.Class
import Control.Monad.Trans.Reader
import Network
import Network.Socket.Options (setHandleTimeouts)
import System.IO
class MonadFork m where
forkM :: m () -> m ThreadId
instance MonadFork IO where
forkM = forkIO
instance MonadFork m => MonadFork (LogT m) where
forkM (LogT (ReaderT m)) = LogT (ReaderT (forkM . m))
server :: Socket -> IO ()
server sock = withLogContext "server" $
let loop n = do
(h, host, port) <- liftIO $ accept sock
_ <- forkM $ withLogContext (show n) $ do
log $ "Received connection from " ++ host ++ ":" ++ show port
liftIO $ hSetBuffering h LineBuffering
log "Sending \"one\""
liftIO $ hPutStrLn h "one"
log "Waiting 30 seconds"
liftIO $ threadDelay 30000000
log "Sending \"two\""
liftIO $ hPutStrLn h "two"
log "Closing the handle"
liftIO $ hClose h
log "Done"
loop $! n+1
in loop (1 :: Integer)
client :: Integer -> IO ()
client n = withLogContext "client" $
withLogContext (show n) $ do
log "Connecting to localhost:1234"
h <- liftIO $ connectTo "localhost" $ PortNumber 1234
liftIO $ setHandleTimeouts h 1000000 1000000
log "Connected. Getting first line"
line1 <- liftIO $ hGetLine h
log $ "First line: " ++ line1
recv_tid <- forkM $ do
log "Getting second line"
line2 <- liftIO $ hGetLine h
log $ "Second line: " ++ line2
liftIO $ threadDelay 2000000
log "Killing receiving thread"
liftIO $ killThread recv_tid
log "Done killing"
main :: IO ()
main = do
hSetBuffering stderr LineBuffering
sock <- liftIO $ listenOn $ PortNumber 1234
log "Listening on port 1234"
_ <- forkIO $ server sock
mapM_ client [1..]