aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--step-04/messenger.erl166
1 files changed, 166 insertions, 0 deletions
diff --git a/step-04/messenger.erl b/step-04/messenger.erl
new file mode 100644
index 0000000..06288dd
--- /dev/null
+++ b/step-04/messenger.erl
@@ -0,0 +1,166 @@
+%%% Message passing utility.
+%%% User interface:
+%%% logon(Name)
+%%% One user at a time can log in from each Erlang node in the
+%%% system messenger: and choose a suitable Name. If the Name
+%%% is already logged in at another node or if someone else is
+%%% already logged in at the same node, login will be rejected
+%%% with a suitable error message.
+%%% logoff()
+%%% Logs off anybody at that node
+%%% message(ToName, Message)
+%%% sends Message to ToName. Error messages if the user of this
+%%% function is not logged on or if ToName is not logged on at
+%%% any node.
+%%%
+%%% One node in the network of Erlang nodes runs a server which maintains
+%%% data about the logged on users. The server is registered as "messenger"
+%%% Each node where there is a user logged on runs a client process registered
+%%% as "mess_client"
+%%%
+%%% Protocol between the client processes and the server
+%%% ----------------------------------------------------
+%%%
+%%% To server: {ClientPid, logon, UserName}
+%%% Reply {messenger, stop, user_exists_at_other_node} stops the client
+%%% Reply {messenger, logged_on} logon was successful
+%%%
+%%% To server: {ClientPid, logoff}
+%%% Reply: {messenger, logged_off}
+%%%
+%%% To server: {ClientPid, logoff}
+%%% Reply: no reply
+%%%
+%%% To server: {ClientPid, message_to, ToName, Message} send a message
+%%% Reply: {messenger, stop, you_are_not_logged_on} stops the client
+%%% Reply: {messenger, receiver_not_found} no user with this name logged on
+%%% Reply: {messenger, sent} Message has been sent (but no guarantee)
+%%%
+%%% To client: {message_from, Name, Message},
+%%%
+%%% Protocol between the "commands" and the client
+%%% ----------------------------------------------
+%%%
+%%% Started: messenger:client(Server_Node, Name)
+%%% To client: logoff
+%%% To client: {message_to, ToName, Message}
+%%%
+%%% Configuration: change the server_node() function to return the
+%%% name of the node where the messenger server runs
+
+-module(messenger).
+-export([start_server/0, server/1, logon/1, logoff/0, message/2, client/2]).
+
+%%% Change the function below to return the name of the node where the
+%%% messenger server runs
+server_node() ->
+ messenger@akili.
+
+%%% This is the server process for the "messenger"
+%%% the user list has the format [{ClientPid1, Name1},{ClientPid22, Name2},...]
+server(User_List) ->
+ receive
+ {From, logon, Name} ->
+ New_User_List = server_logon(From, Name, User_List),
+ server(New_User_List);
+ {From, logoff} ->
+ New_User_List = server_logoff(From, User_List),
+ server(New_User_List);
+ {From, message_to, To, Message} ->
+ server_transfer(From, To, Message, User_List),
+ io:format("list is now: ~p~n", [User_List]),
+ server(User_List)
+ end.
+
+%%% Start the server
+start_server() ->
+ register(messenger, spawn(messenger, server, [[]])).
+
+
+%%% Server adds a new user to the user list
+server_logon(From, Name, User_List) ->
+ %% check if logged on anywhere else
+ case lists:keymember(Name, 2, User_List) of
+ true ->
+ From ! {messenger, stop, user_exists_at_other_node}, %reject logon
+ User_List;
+ false ->
+ From ! {messenger, logged_on},
+ [{From, Name} | User_List] %add user to the list
+ end.
+
+%%% Server deletes a user from the user list
+server_logoff(From, User_List) ->
+ lists:keydelete(From, 1, User_List).
+
+
+%%% Server transfers a message between user
+server_transfer(From, To, Message, User_List) ->
+ %% check that the user is logged on and who he is
+ case lists:keysearch(From, 1, User_List) of
+ false ->
+ From ! {messenger, stop, you_are_not_logged_on};
+ {value, {From, Name}} ->
+ server_transfer(From, Name, To, Message, User_List)
+ end.
+%%% If the user exists, send the message
+server_transfer(From, Name, To, Message, User_List) ->
+ %% Find the receiver and send the message
+ case lists:keysearch(To, 2, User_List) of
+ false ->
+ From ! {messenger, receiver_not_found};
+ {value, {ToPid, To}} ->
+ ToPid ! {message_from, Name, Message},
+ From ! {messenger, sent}
+ end.
+
+
+%%% User Commands
+logon(Name) ->
+ case whereis(mess_client) of
+ undefined ->
+ register(mess_client,
+ spawn(messenger, client, [server_node(), Name]));
+ _ -> already_logged_on
+ end.
+
+logoff() ->
+ mess_client ! logoff.
+
+message(ToName, Message) ->
+ case whereis(mess_client) of % Test if the client is running
+ undefined ->
+ not_logged_on;
+ _ -> mess_client ! {message_to, ToName, Message},
+ ok
+end.
+
+
+%%% The client process which runs on each server node
+client(Server_Node, Name) ->
+ {messenger, Server_Node} ! {self(), logon, Name},
+ await_result(),
+ client(Server_Node).
+
+client(Server_Node) ->
+ receive
+ logoff ->
+ {messenger, Server_Node} ! {self(), logoff},
+ exit(normal);
+ {message_to, ToName, Message} ->
+ {messenger, Server_Node} ! {self(), message_to, ToName, Message},
+ await_result();
+ {message_from, FromName, Message} ->
+ io:format("Message from ~p: ~p~n", [FromName, Message])
+ end,
+ client(Server_Node).
+
+%%% wait for a response from the server
+await_result() ->
+ receive
+ {messenger, stop, Why} -> % Stop the client
+ io:format("~p~n", [Why]),
+ exit(normal);
+ {messenger, What} -> % Normal response
+ io:format("~p~n", [What])
+ end.