Erlang Central

Getting started with processes

Revision as of 21:10, 4 August 2007 by Spammer (Talk | contribs)

Author

Doug Edmunds

More getting started with processes

The doc "Getting started with Erlang" has sample code called the "A Larger Example" and "The Larger Example with Robustness Added". I think it would be helpful to see how this example can be broken into two separate modules (one for the server and one for the clients). The code follows.

Please note that only the clients need to know the server's node information, since the clients initiate the contact with the server. You will have to modify the server_node() function in the m_client.erl file to correspond to your own server location. I developed this on windows xp, so I used a 'shortcut' to werl.exe, adding the -sname flag to the application line, i.e., c:\somepath\erlang\bin\werl -sname m_server_node.

I have purposely renamed various parts to show which is which (instead of referring to 'messenger' everywhere.

For someone new to the use of processes, the more examples which show variations on the basic principles, the better.

-- Doug Edmunds

code for m_server.erl:

%% show how different files can be on different nodes,
%% and still communicate

-module(m_server).
-export([start_server/0, server/0]).

%%% This is the server process for the "the_messenger"
%%% the user list has the format [{ClientPid1, Name1},{ClientPid22, Name2},...]
server() ->
    process_flag(trap_exit, true),
    server([]).

server(User_List) ->
    receive
        {From, logon, Name} ->
            New_User_List = server_logon(From, Name, User_List),
            server(New_User_List);
        {'EXIT', From, _} ->
            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(the_messenger, spawn(m_server, 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 ! {the_messenger, stop, user_exists_at_other_node},  %reject logon
            User_List;
        false ->
            From ! {the_messenger, logged_on},
            link(From),
            [{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 ! {the_messenger, stop, you_are_not_logged_on};
        {value, {_, 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 ! {the_messenger, receiver_not_found};
        {value, {ToPid, To}} ->
            ToPid ! {message_from, Name, Message}, 
            From ! {the_messenger, sent} 
    end.


code for m_client.erl:


%% show how different files can be on different nodes,
%% and still communicate

-module(m_client).
-export([logon/1, logoff/0, message/2, client/2]).

%%% Change the function below to return the name of the node where the
%%% messenger server runs
%%% Client needs to know this location
server_node() ->
    m_server_node@sparky.


%%% User Commands
logon(Name) ->
    case whereis(mess_client) of 
        undefined ->
            register(mess_client, 
                     spawn(m_client, 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 user node
client(Server_Node, Name) ->
    {the_messenger, Server_Node} ! {self(), logon, Name},
    await_result(),
    client(Server_Node).

client(Server_Node) ->
    receive
        logoff ->
            exit(normal);
        {message_to, ToName, Message} ->
            {the_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
        {the_messenger, stop, Why} -> % Stop the client 
            io:format("~p~n", [Why]),
            exit(normal);
        {the_messenger, What} ->  % Normal response
            io:format("~p~n", [What])
    after 5000 ->
            io:format("No response from server~n", []),
            exit(timeout)
    end.