add haskell server with delimiters

This commit is contained in:
erichhasl
2017-10-15 22:32:53 +02:00
parent aa793be1db
commit a34b8f107b
+99
View File
@@ -0,0 +1,99 @@
module Socket 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
}
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)
-- | 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