CLIENT SERVER AUTHENTICATION FINAL PROJECT
Running Head: HANDLING CONCURRENT CLIENTS
HANDLING CONCURRENT CLIENTS 6
HANDLING CONCURRENT CLIENTS
Student’s Name
Institution
Date of Submission HANDLING CONCURRENT CLIENTS
Discuss how you will design the server in the given situation to handle multiple clients arbitrarily entering and leaving the system?
Once a one or single client server has been established, it becomes obvious that this server can only serve one client at a time. In order to have a server that can handle more than a single client at a time, a multiple or multi – client server must be created to perform this operation. Here are the steps followed in creating a multi – client server and its discussion.
main = withSocketsDo $ do
sock <- listenOn {PortNumber {fromIntegral port}}
printf "Listening on port %d\n" port
forever $ do
{handle, host, port} <- accept sock
printf "Accepted connection from %s: %s\n" host {show port}
forkFinally {talk handle} {\_ -> hClose handle}
port :: Int
port = 44444
The first thing that the programmer does is to create a listening port 4444 through the network socket. Once the listening port has been created, the programmer goes ahead to insert a loop that will accept all the possible connections that are made by the client. The loop then waits for any possible connection that might be made by any client. Prior to any connection, the accept command from the program remains blocked until the moment the client will send a request. After the request has been initiated, a handle is created that allows for communication between the client and the server where details about the client are shared. The sharing of the client’s details enables a link to be created between the server and the client in the form of a binding, host to the client and port to the server. This binding allows the client to log in to the server. Once the binding has been made, the programmer creates a new thread that is purposely made to handle the client’s requests. This becomes folkfinally of the new thread formed. The interaction between the client and the server henceforth is assigned to talk where the handle returns after a connection call is accepted.
Describe your server design in detail.
There are four possible designs that allows for ease in handling concurrent clients. Among the four, my choice of design is design four that uses the STM. STM is a design that has seen improvement from design three that uses broadcast chan. In this design, the channel for communication between the client and the server is avoided by storing in TVar all factors that are at the moment.
Justification of STM Design
In STM the use of TVar reflects this on the screen:
newtype State = State [ currentFactor :: TVar Int ]
The reason for using STM design has been contributed by its ability to block changes until something happens. Due to this it spares the server the need to send messages overtly when a change is made. In order to understand this, let’s follow the explanation below:
When a client makes a sequence of events (N) to the server, the following happens;
· The Handle receives N commands from the client and sends it to the TChan thread of the server.
· Once received, the server makes a command on its TChan and starts modifying the current factors in TVar.
· All the respective threads made acknowledge the changes observed in TVar and forward the changed value back to the client.
A simple diagram of STM Design
TVar
Server
TChan
Thread Received
Network Socket
An alternative language to be used to implement this design would be C. Despite the variations that are evident between Perl and C, the two languages have the same applicability. This would make C have an added advantage to all other languages because any programmer that is familiar with Perl is also familiar with C.
Implementing STM Design
STM is the simplest architecture to be implemented; here is how it can be done;
server2.hs
main = withSocketsDo $ do
sock <- listenOn {PortNumber {fromIntegral port}}
printf "Listening on port %d\n" port
factor <- atomically $ newTVar 2
forever $ do
{handle, host, port} <- accept sock
printf "Accepted connection from %s: %s\n" host {show port}
forkFinally {talk handle factor} {\_ -> hClose handle}
port :: Int
port = 44444
The new connection made to the client from the talk function is then set:
talk :: Handle -> TVar Integer -> IO {}
talk h factor = do
hSetBuffering h LineBuffering
c <- atomically newTChan
race {server h factor c} {receive h c}
return {}
|
|
|
|
|
|
Once received, the repeated function from the Handle writes the following to the TChan:
receive :: Handle -> TChan String -> IO {}
receive h c = forever $ do
line <- hGetLine h
atomically $ writeTChan c line
At the server, the following takes place:
server :: Handle -> TVar Integer -> TChan String -> IO {}
server h factor c = do
f <- atomically $ readTVar factor
hPrintf h "Current factor: %d\n" f
loop f
where
loop f = do
action <- atomically $ do
f' <- readTVar factor
if {f /= f'}
then return {newfactor f'}
also do
l <- readTChan c
return {command f l}
action
newfactor f = do
hPrintf h "new factor: %d\n" f
loop f
command f s
= case s of
"end" ->
hPutStrLn h {"Thank you for using the " ++
"Perl doubling service."}
'*':s -> do
atomically $ writeTVar factor [read s :: Integer]
loop f
line -> do
hPutStrLn h {show {f * {read line :: Integer}}}
loop f
There is no challenging task to in implementing STM design since it’s the simplest design compared to the other three. This is made possible by its ability to block any changes before any command takes place.