add socket server
This commit is contained in:
+105
@@ -0,0 +1,105 @@
|
||||
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
|
||||
@@ -0,0 +1,98 @@
|
||||
{-# LANGUAGE TypeSynonymInstances #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
|
||||
module AI.Socket (
|
||||
mkSocketEnv
|
||||
) where
|
||||
|
||||
import Control.Monad.Trans (liftIO)
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.State
|
||||
import Control.Concurrent
|
||||
import Control.Concurrent.Chan
|
||||
|
||||
import Player
|
||||
import Pile
|
||||
import Card
|
||||
import Utils
|
||||
import Render
|
||||
import AI.Server
|
||||
|
||||
data SocketEnv = SocketEnv { getTeam :: Team
|
||||
, getHand :: Hand
|
||||
, table :: [CardS Played]
|
||||
, myHand :: [Card]
|
||||
, socketThread :: Maybe ThreadId
|
||||
, socketServer :: Maybe ServerEnv }
|
||||
deriving Show
|
||||
|
||||
setTable :: [CardS Played] -> SocketEnv -> SocketEnv
|
||||
setTable tab env = env { table = tab }
|
||||
|
||||
setHand :: [Card] -> SocketEnv -> SocketEnv
|
||||
setHand hand env = env { myHand = hand }
|
||||
|
||||
setSocket :: ThreadId -> SocketEnv -> SocketEnv
|
||||
setSocket n env = env { socketThread = Just n }
|
||||
|
||||
clearSocket :: SocketEnv -> SocketEnv
|
||||
clearSocket env = env { socketThread = Nothing }
|
||||
|
||||
setServer :: ServerEnv -> SocketEnv -> SocketEnv
|
||||
setServer s env = env { socketServer = Just s }
|
||||
|
||||
type Socket m = StateT SocketEnv m
|
||||
|
||||
instance MonadPlayer m => MonadPlayer (Socket m) where
|
||||
trumpColour = lift $ trumpColour
|
||||
turnColour = lift $ turnColour
|
||||
showSkat = lift . showSkat
|
||||
|
||||
instance Player SocketEnv where
|
||||
team = getTeam
|
||||
hand = getHand
|
||||
chooseCard p table _ hand = runStateT (do
|
||||
modify $ setTable table
|
||||
modify $ setHand hand
|
||||
choose) p
|
||||
|
||||
choose :: MonadPlayer m => Socket m Card
|
||||
choose = do
|
||||
initialize
|
||||
trumpCol <- trumpColour
|
||||
turnCol <- turnColour
|
||||
hand <- gets myHand
|
||||
(Just server) <- gets socketServer
|
||||
liftIO $ writeChan (global server) "hi"
|
||||
let possible = filter (isAllowed trumpCol turnCol hand) hand
|
||||
case length hand of
|
||||
1 -> liftIO (putStrLn "stopping server") >> stop
|
||||
_ -> return ()
|
||||
return $ head possible
|
||||
|
||||
initialize :: MonadPlayer m => Socket m ()
|
||||
initialize = do
|
||||
state <- gets socketThread
|
||||
case state of
|
||||
Just _ -> return ()
|
||||
Nothing -> do
|
||||
liftIO $ putStrLn "initializing server"
|
||||
server <- liftIO $ initServer 4242 (DelimiterBuffering "\n") sampleHandler
|
||||
n <- liftIO $ forkIO $ runReaderT procRequests server
|
||||
modify $ setSocket n
|
||||
modify $ setServer server
|
||||
|
||||
stop :: MonadPlayer m => Socket m ()
|
||||
stop = do
|
||||
state <- gets socketThread
|
||||
case state of
|
||||
Just n -> do liftIO $ putStrLn "killing thread"
|
||||
liftIO $ killThread n
|
||||
modify clearSocket
|
||||
Just server <- gets socketServer
|
||||
liftIO $ close server
|
||||
Nothing -> return ()
|
||||
|
||||
mkSocketEnv :: Team -> Hand -> SocketEnv
|
||||
mkSocketEnv tm h = SocketEnv tm h [] [] Nothing Nothing
|
||||
Reference in New Issue
Block a user