Adds a channel to client to send messages to.

- Adds message type and parsing for incoming messages.
pull/1/head
Abhinav Sarkar 2015-09-09 23:10:37 +05:30
parent 973b0cc926
commit 0cd59a4e92
4 changed files with 39 additions and 11 deletions

View File

@ -16,7 +16,8 @@ cabal-version: >=1.10
library library
hs-source-dirs: src hs-source-dirs: src
exposed-modules: Link.Server, exposed-modules: Link.Server,
Link.Types Link.Types,
Link.Protocol
default-extensions: OverloadedStrings, RecordWildCards, ScopedTypeVariables, default-extensions: OverloadedStrings, RecordWildCards, ScopedTypeVariables,
BangPatterns, TupleSections, NamedFieldPuns BangPatterns, TupleSections, NamedFieldPuns
build-depends: base >= 4.7 && < 5, build-depends: base >= 4.7 && < 5,

8
src/Link/Protocol.hs Normal file
View File

@ -0,0 +1,8 @@
module Link.Protocol where
import Link.Types
parseCommand :: String -> Maybe Message
parseCommand command = case (words command) of
"PRIVMSG" : userName : msg -> Just $ PrivMsg (User userName) (unwords msg)
_ -> Nothing

View File

@ -1,7 +1,8 @@
module Link.Server where module Link.Server where
import Control.Exception (finally) import Control.Exception (finally)
import Control.Concurrent (forkFinally, newMVar, modifyMVar, modifyMVar_) import Control.Concurrent (forkFinally, newMVar, modifyMVar, modifyMVar_, newChan,
writeChan, withMVar)
import Control.Monad (forever) import Control.Monad (forever)
import Network (withSocketsDo, listenOn, accept, PortID(..)) import Network (withSocketsDo, listenOn, accept, PortID(..))
import System.IO (hClose, hSetNewlineMode, hSetBuffering, BufferMode(..), import System.IO (hClose, hSetNewlineMode, hSetBuffering, BufferMode(..),
@ -10,6 +11,7 @@ import Text.Printf (printf, hPrintf)
import qualified Data.Map.Strict as Map import qualified Data.Map.Strict as Map
import Link.Protocol
import Link.Types import Link.Types
runServer :: Int -> IO () runServer :: Int -> IO ()
@ -22,10 +24,10 @@ runServer port = withSocketsDo $ do
forever $ do forever $ do
(handle, host, port') <- accept sock (handle, host, port') <- accept sock
printf "Accepted connection from %s: %s\n" host (show port') printf "Accepted connection from %s: %s\n" host (show port')
forkFinally (talk server handle) (\_ -> hClose handle) forkFinally (connectClient server handle) (\_ -> hClose handle)
talk :: Server -> Handle -> IO () connectClient :: Server -> Handle -> IO ()
talk server handle = do connectClient server handle = do
hSetNewlineMode handle universalNewlineMode hSetNewlineMode handle universalNewlineMode
hSetBuffering handle LineBuffering hSetBuffering handle LineBuffering
readName readName
@ -45,20 +47,33 @@ talk server handle = do
Just client -> Just client ->
runClient server client `finally` removeClient server user runClient server client `finally` removeClient server user
sendMessage :: Message -> Client -> IO ()
sendMessage message Client {..} = writeChan clientChan message
checkAddClient :: Server -> User -> Handle -> IO (Maybe Client) checkAddClient :: Server -> User -> Handle -> IO (Maybe Client)
checkAddClient Server {..} user@User {..} handle = do checkAddClient Server {..} user@User {..} handle = do
modifyMVar serverUsers $ \clientMap -> modifyMVar serverUsers $ \clientMap ->
if Map.member user clientMap if Map.member user clientMap
then return (clientMap, Nothing) then return (clientMap, Nothing)
else do else do
let client = Client user handle clientChan <- newChan
let client = Client user handle clientChan
printf "New user connected: %s\n" userName printf "New user connected: %s\n" userName
return (Map.insert user client clientMap, Just client) return (Map.insert user client clientMap, Just client)
runClient :: Server -> Client -> IO () runClient :: Server -> Client -> IO ()
runClient server Client {..} = forever $ do runClient Server {..} Client {..} = forever $ do
command <- hGetLine clientHandle command <- hGetLine clientHandle
print command printf "<%s>: %s\n" (userName clientUser) command
case parseCommand command of
Nothing -> return ()
Just com -> handleCommand com
where
handleCommand message@(PrivMsg user _) =
withMVar serverUsers $ \clientMap ->
case Map.lookup user clientMap of
Nothing -> printf "No such user: %s\n" (userName user)
Just client -> sendMessage message client
removeClient :: Server -> User -> IO () removeClient :: Server -> User -> IO ()
removeClient Server {..} user = removeClient Server {..} user =

View File

@ -1,17 +1,21 @@
module Link.Types where module Link.Types where
import System.IO (Handle) import System.IO (Handle)
import Control.Concurrent (MVar) import Control.Concurrent (MVar, Chan)
import qualified Data.Map as Map import qualified Data.Map as Map
data User = User { userName :: !String } data User = User { userName :: !String }
deriving (Show, Eq, Ord) deriving (Show, Eq, Ord)
data Client = Client { data Client = Client {
clientUser :: !User clientUser :: !User
, clientHandle :: !Handle , clientHandle :: !Handle
} deriving (Show, Eq) , clientChan :: !(Chan Message)
}
data Server = Server { data Server = Server {
serverUsers :: MVar (Map.Map User Client) serverUsers :: MVar (Map.Map User Client)
} }
data Message = PrivMsg User String
deriving (Show, Eq)