How to resolve the algorithm Chat server step by step in the Haskell programming language

Published on 7 June 2024 03:52 AM

How to resolve the algorithm Chat server step by step in the Haskell programming language

Table of Contents

Problem Statement

Write a server for a minimal text based chat. People should be able to connect via ‘telnet’, sign on with a nickname, and type messages which will then be seen by all other connected users. Arrivals and departures of chat members should generate appropriate notification messages.

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Chat server step by step in the Haskell programming language

The code you provided is a server application written in Haskell using the Network library. It allows multiple clients to connect to the server and chat with each other.

The main function sets up a server on port 5002 and starts a loop that waits for clients to connect. When a client connects, the server creates a new thread to handle the client's connection. The thread runs the userChat function, which prompts the client for a username and then allows them to chat with other clients.

The userChat function uses the ReaderT monad transformer to pass the current thread data (the handle to the client's socket and the map of usernames to handles) to each step of the computation. It also uses the Error monad transformer to handle errors that may occur during the computation.

The userChat function first prompts the client for a username and then checks if the username is already in use. If the username is already in use, the server sends an error message to the client and prompts them to enter a new username. If the username is not in use, the server adds the client's handle to the map of usernames to handles and sends a welcome message to the client.

The userChat function then enters a loop that waits for the client to send a message. When the client sends a message, the server broadcasts the message to all other clients. The broadcast function uses the Map library to iterate over the map of usernames to handles and send the message to each client.

The clientLoop function is responsible for handling the client's connection. It accepts a socket and a map of usernames to handles as arguments and then creates a new thread to handle the client's connection. The thread runs the userChat function, which prompts the client for a username and then allows them to chat with other clients.

The main function starts the server and then enters a loop that waits for clients to connect. When a client connects, the server creates a new thread to handle the client's connection. The thread runs the userChat function, which prompts the client for a username and then allows them to chat with other clients.

Source code in the haskell programming language

{-# LANGUAGE OverloadedStrings #-}
import Network
import System.IO
import Control.Concurrent
import qualified Data.Text as T
import Data.Text (Text)
import qualified Data.Text.IO as T
import qualified Data.Map as M
import Data.Map (Map)
import Control.Monad.Reader
import Control.Monad.Error
import Control.Exception
import Data.Monoid 
import Control.Applicative

type ServerApp = ReaderT ThreadData IO
data Speaker = Server | Client Text
data ThreadData = ThreadData { threadHandle :: Handle
                             , userTableMV :: MVar (Map Text Handle)}

echoLocal = liftIO . T.putStrLn
echoRemote = echoMessage . (">> "<>)
echoMessage msg = viewHandle >>= \h -> liftIO $ T.hPutStrLn h msg
getRemoteLine = viewHandle >>= liftIO . T.hGetLine
putMVarT = (liftIO.) . putMVar
takeMVarT = liftIO . takeMVar
readMVarT = liftIO . readMVar
modifyUserTable fn = viewUsers >>= \mv ->
                     liftIO $ modifyMVar_ mv (return . fn)
viewHandle = threadHandle <$> ask
viewUsers = userTableMV <$> ask

userChat :: ServerApp ()
userChat = do
    name <- addUser 
    echoLocal name
    h <- viewHandle
    (flip catchError) (\_ -> removeUser name) $
      do echoLocal $ "Accepted " <> name
         forever $ getRemoteLine >>= broadcast (Client name)

removeUser :: Text -> ServerApp ()
removeUser name = do
    echoLocal $ "Exception with " <> name <> ", removing from userTable"
    broadcast Server $ name <> " has left the server"
    modifyUserTable (M.delete name)

addUser :: ServerApp Text
addUser = do
    h <- viewHandle
    usersMV <- viewUsers
    echoRemote "Enter username" 
    name <- T.filter (/='\r') <$> getRemoteLine
    userTable <- takeMVarT usersMV
    if name `M.member` userTable
      then do echoRemote "Username already exists!" 
              putMVarT usersMV userTable
              addUser
      else do putMVarT usersMV (M.insert name h userTable)
              broadcast Server $ name <> " has joined the server"
              echoRemote "Welcome to the server!\n>> Other users:"
              readMVarT usersMV >>=
                  mapM_ (echoRemote . ("*" <>) . fst) 
                . filter ((/=name). fst) . M.toList
              return name

broadcast :: Speaker -> Text -> ServerApp ()
broadcast user msg =
    viewUsers >>= readMVarT >>= mapM_ (f . snd) . fn . M.toList
  where f h = liftIO $ T.hPutStrLn h $ nm <> msg
        (fn, nm) = case user of
                    Server -> (id, ">> ")
                    Client t -> (filter ((/=t) . fst), t <> "> ")

clientLoop socket users = do
    (h, _, _) <- accept socket
    hSetBuffering h LineBuffering
    forkIO $ runReaderT userChat (ThreadData h users)
    clientLoop socket users

main = do
    server <- listenOn $ PortNumber 5002
    T.putStrLn "Server started"
    newMVar (M.empty) >>= clientLoop server


  

You may also check:How to resolve the algorithm Mutual recursion step by step in the 8080 Assembly programming language
You may also check:How to resolve the algorithm Identity matrix step by step in the Mathematica / Wolfram Language programming language
You may also check:How to resolve the algorithm Temperature conversion step by step in the PHP programming language
You may also check:How to resolve the algorithm CRC-32 step by step in the J programming language
You may also check:How to resolve the algorithm Proper divisors step by step in the Modula-2 programming language