68 lines
2.3 KiB
Haskell
68 lines
2.3 KiB
Haskell
module Link.Server where
|
|
|
|
import Control.Exception hiding (handle)
|
|
import Control.Concurrent hiding (forkFinally)
|
|
import Control.Monad (forever)
|
|
import Data.Time (getCurrentTime)
|
|
import Network (withSocketsDo, listenOn, accept, PortID(..))
|
|
import System.IO (hClose, hSetNewlineMode, hSetBuffering, BufferMode(..),
|
|
universalNewlineMode, hGetLine, Handle, stdout)
|
|
import Text.Printf (printf)
|
|
|
|
import qualified Data.Map.Strict as Map
|
|
|
|
import Link.Client
|
|
import Link.Protocol
|
|
import Link.Types
|
|
import Link.Util
|
|
|
|
runServer :: Int -> IO ()
|
|
runServer port = withSocketsDo $ do
|
|
hSetBuffering stdout LineBuffering
|
|
serverUsers <- newMVar Map.empty
|
|
let server = Server serverUsers
|
|
sock <- listenOn . PortNumber . fromIntegral $ port
|
|
printf "Listening on port %d\n" port
|
|
forever $ do
|
|
(handle, host, port') <- accept sock
|
|
printf "Accepted connection from %s: %s\n" host (show port')
|
|
forkIO $ connectClient server handle `finally` hClose handle
|
|
|
|
connectClient :: Server -> Handle -> IO ()
|
|
connectClient server handle = do
|
|
hSetNewlineMode handle universalNewlineMode
|
|
hSetBuffering handle LineBuffering
|
|
readName
|
|
where
|
|
readName = do
|
|
name <- hGetLine handle
|
|
if null name
|
|
then readName
|
|
else do
|
|
let user = User name
|
|
ok <- checkAddClient server user handle
|
|
case ok of
|
|
Nothing -> do
|
|
printToHandle handle $ formatMessage (NameInUse name)
|
|
readName
|
|
Just client -> do
|
|
printToHandle handle $ formatMessage (Connected name)
|
|
runClient server client `finally` removeClient server user
|
|
|
|
checkAddClient :: Server -> User -> Handle -> IO (Maybe Client)
|
|
checkAddClient Server {..} user@User {..} handle =
|
|
modifyMVar serverUsers $ \clientMap ->
|
|
if Map.member user clientMap
|
|
then return (clientMap, Nothing)
|
|
else do
|
|
clientChan <- newChan
|
|
now <- newMVar =<< getCurrentTime
|
|
let client = Client user handle clientChan now
|
|
printf "New user connected: %s\n" userName
|
|
return (Map.insert user client clientMap, Just client)
|
|
|
|
removeClient :: Server -> User -> IO ()
|
|
removeClient Server {..} user =
|
|
modifyMVar_ serverUsers $ \clientMap ->
|
|
return $ Map.delete user clientMap
|