|
- module AI.Server where
-
- import qualified Network.Socket as Net
- import qualified System.IO as Sys
- import Control.Concurrent
- import Control.Concurrent.Chan
- import Control.Monad (forever)
- import Control.Monad.Reader
- import Data.List.Split
-
- data Buffering = NoBuffering
- | LengthBuffering
- | DelimiterBuffering String
- deriving (Show)
-
- data ServerEnv = ServerEnv
- { buffering :: Buffering -- ^ Buffermode
- , socket :: Net.Socket -- ^ the socket used to communicate
- , global :: Chan String
- , onReceive :: OnReceive
- }
-
- instance Show ServerEnv where
- show env = "A Server"
-
- type Server = ReaderT ServerEnv IO
-
- type OnReceive = Sys.Handle -> Net.SockAddr -> String -> Server ()
-
- broadcast :: String -> Server ()
- broadcast msg = do
- bufmode <- asks buffering
- chan <- asks global
- case bufmode of
- DelimiterBuffering delim -> liftIO $ writeChan chan $ msg ++ delim
- _ -> liftIO $ writeChan chan msg
-
- send :: Sys.Handle -> String -> Server ()
- send connhdl msg = do
- bufmode <- asks buffering
- case bufmode of
- DelimiterBuffering delim -> liftIO $ Sys.hPutStr connhdl $ msg ++ delim
- _ -> liftIO $ Sys.hPutStr connhdl msg
-
-
- -- | Initialize a new server with the given port number and buffering mode
- initServer :: Net.PortNumber -> Buffering -> OnReceive -> IO ServerEnv
- initServer port buffermode handler = do
- sock <- Net.socket Net.AF_INET Net.Stream 0
- Net.setSocketOption sock Net.ReuseAddr 1
- Net.bind sock (Net.SockAddrInet port Net.iNADDR_ANY)
- Net.listen sock 5
- chan <- newChan
- forkIO $ forever $ do
- msg <- readChan chan -- clearing the main channel
- return ()
- return (ServerEnv buffermode sock chan handler)
-
- close :: ServerEnv -> IO ()
- close = Net.close . socket
-
- -- | Looping over requests and establish connection
- procRequests :: Server ()
- procRequests = do
- sock <- asks socket
- (conn, clientaddr) <- liftIO $ Net.accept sock
- env <- ask
- liftIO $ forkIO $ runReaderT (procMessages conn clientaddr) env
- procRequests
-
- -- | Handle one client
- procMessages :: Net.Socket -> Net.SockAddr -> Server ()
- procMessages conn clientaddr = do
- connhdl <- liftIO $ Net.socketToHandle conn Sys.ReadWriteMode
- liftIO $ Sys.hSetBuffering connhdl Sys.NoBuffering
- globalChan <- asks global
-
- commChan <- liftIO $ dupChan globalChan
-
- reader <- liftIO $ forkIO $ forever $ do
- msg <- readChan commChan
- Sys.hPutStrLn connhdl msg
-
- handler <- asks onReceive
- messages <- liftIO $ Sys.hGetContents connhdl
- buffermode <- asks buffering
- case buffermode of
- DelimiterBuffering delimiter ->
- mapM_ (handler connhdl clientaddr) (splitOn delimiter messages)
- LengthBuffering -> liftIO $ putStrLn (take 4 messages)
- _ -> return ()
-
- -- clean up
- liftIO $ do killThread reader
- Sys.hClose connhdl
-
- sampleHandler :: OnReceive
- sampleHandler connhdl addr query = do
- liftIO $ putStrLn $ "new query " ++ query
- send connhdl $ "> " ++ query
-
- main :: IO ()
- main = do
- env <- initServer 4242 LengthBuffering sampleHandler
- runReaderT procRequests env
|