SE 765 - Distributed Software Development |
CS 610 - Introduction to Parallel and Distributed Computing |
Assignment #6
To be begun in class and completed at home. The final program, a snapshot of its workings, and answers to questions should be emailed to me by 6PM, December 9th.
Simple client/server in Erlang exercise.
1 Introduction
You will build a simple client server system in Erlang.
2 A simple server
A server generally has some state. Thus we have to use patterns similar to the following:
server(State) -> receive
some_request ->
%%..... do some stuff
%% ..... compute the new state NewState server(NewState)
end.
You will extend the following very simple server. Your server should have one piece of state, a value that records how many times it has been called.
server(State) -> receive
{request,Return_PID} ->
io:format("SERVER ~w: Client request received from ~w~n", [self(), Return_PID]) ,
NewState = State + 1,
Return_PID ! {hit_count,NewState},
server(NewState)
end.
The client is simpler, it takes as a parameter the server PID, sends a request, and prints out a value.
client(Server_Address) ->
Server_Address ! {request, self()},
receive
{hit_count,Number} ->
io:format("CLIENT ~w: Hit count was ~w~n",[self(), Number])
end.
Combine server and client in one file named simple.erl and try it. .
-module(simple).
-export([server/1,client/1,start/0]).
server(State) ->
receive
{request,Return_PID} ->
io:format("SERVER ~w: Client request received from ~w~n",
[self(), Return_PID]) ,
NewState = State + 1,
Return_PID ! {hit_count,NewState},
server(NewState)
end.
client(Server_Address) ->
Server_Address ! {request, self()},
receive
{hit_count,Number} ->
io:format("CLIENT ~w: Hit count was ~w~n",[self(), Number])
end.
start() ->
Server_PID = spawn(simple,server,[0]),
spawn(simple,client,[Server_PID]).
Compile and run this code from the Erlang shell
3 Many clients
Add the following function to spawn multiple clients:
spawn_n(N,Server_PID) -> if
N>0 -> spawn(simple,client,[Server_PID]),
%% Use a random sleep in miliseconds to simulate the
%% client traffic pattern. timer:sleep(random:uniform(100)),
spawn_n(N-1,Server_PID);
N == 0 ->
io:format("Last client spawned.~n")
end.
4 Questions
You should turn in (email to me) answers to the questions below .
Question 1: In the simple server
server(State) -> receive
{request,Return_PID} ->
io:format("SERVER ~w: Client request received from ~w~n", [self(), Return_PID]),
NewState = State + 1,
Return_PID ! {hit_count,NewState}, server(NewState)
end.
What does the following line do?
Return_PID ! {hit_count,NewState},
Question 2: In the simple client
client(Server_Address) -> Server_Address ! {request, self()}, receive
{hit_count,Number} ->
io:format("CLIENT ~w: Hit count was ~w~n",[self(), Number])
end.
What does self() do and why is it used in this example?
Question 3: In the simple start() function what does the following line do?
spawn(simple,client,[Server_PID]).
5 Add functionality to the client and server
Start with the simple.erl file and add functionality by following the steps below. Submit the completed simple.erl file.
Step 1: Modify start() to
take a single parameter which is the number of clients that have to be spawned.
Don’t forget to modify the export directives correctly.
Step
2: The owner of the server should have the right to query the count without
incrementing it. Modify the server by inserting code for the message:
{server_owner,Server_PID}
into the postion shown below
server(State) -> receive
{request,Return_PID} ->
io:format("SERVER ~w: Client request received from ~w~n", [self(), Return_PID]) ,
NewState = State + 1,
Return_PID ! {hit_count,NewState}, server(NewState) ;
{server_owner,Owner_PID} ->
.......
end.
Step 3: Modify start() to have a single owner process that constantly queries the server for its state. You should fill in the ....s and decide on what other messages are passed between the server and the owner. You should print out the value received from the server using io:format.
owner(Server_PID) ->
%% Use a random sleep in ms to simulate the owner traffic pattern. timer:sleep(random:uniform(100)),
Server_PID! .... , receive
{ .... } ->
..... , io:format(......) ,
owner(Server_PID) end.
Step 4: Give the owner the power to reset the server counter to 0. Modify the server as follows:
server(State) -> receive
{request,Return_PID} ->
io:format("SERVER ~w: Client request received from ~w~n", [self(), Return_PID]) ,
NewState = State + 1,
Return_PID ! {hit_count,NewState}, server(NewState) ;
reset ->
io:format( .... ) ,
server(....)
end.
Again print out messages to demonstrate that the server has been reset.
Step 5: Now modify the owner process to reset the server counter if it exceeds 5. Hint: look at how the Erlang if statement works or how to use guards in Erlang receive statements.
6 Test run
A test run of the finished program should look something similar to this:
SERVER <0.27.0>: Client request received from <0.29.0>
CLIENT <0.29.0>: Hit count was 1
SERVER <0.27.0>: Owner query received from <0.28.0>
SERVER <0.27.0>: Client request received from <0.30.0>
OWNER <0.28.0>: Hit count is 1
CLIENT <0.30.0>: Hit count was 2
SERVER <0.27.0>: Owner query received from <0.28.0>
SERVER <0.27.0>: Client request received from <0.31.0>
OWNER <0.28.0>: Hit count is 2 CLIENT <0.31.0>: Hit count was 3
SERVER <0.27.0>: Owner query received from <0.28.0>
SERVER <0.27.0>: Client request received from <0.32.0>
OWNER <0.28.0>: Hit count is 3
CLIENT <0.32.0>: Hit count was 4
SERVER <0.27.0>: Owner query received from <0.28.0>
SERVER <0.27.0>: Client request received from <0.33.0>
OWNER <0.28.0>: Hit count is 4
CLIENT <0.33.0>: Hit count was 5
SERVER <0.27.0>: Owner query received from <0.28.0>
SERVER <0.27.0>: Client request received from <0.34.0>
OWNER <0.28.0>: Hit count is 5
CLIENT <0.34.0>: Hit count was 6
SERVER <0.27.0>: Owner query received from <0.28.0>
SERVER <0.27.0>: Client request received from <0.35.0>
OWNER <0.28.0>: Hit count is 6, send reset message....
CLIENT <0.35.0>: Hit count was 7
SERVER <0.27.0>: Owner reset message received
SERVER <0.27.0>: Owner query received from <0.28.0>
SERVER <0.27.0>: Client request received from <0.36.0>
OWNER <0.28.0>: Hit count is 0
CLIENT <0.36.0>: Hit count was 1
SERVER <0.27.0>: Owner query received from <0.28.0>
SERVER <0.27.0>: Client request received from <0.37.0>
OWNER <0.28.0>: Hit count is 1
CLIENT <0.37.0>: Hit count was 2
SERVER <0.27.0>: Owner query received from <0.28.0>
SERVER <0.27.0>: Client request received from <0.38.0>
Note: The server hit count may not be immediately reset once the owner process discover the count to exceed the limit (5). This is due to client requests being received by the server before the owner reset message is received by the server.