[erlang-questions] Comunication between 2 gen_server's

Ladislav Lenart <>
Fri Apr 13 10:09:05 CEST 2012


I don't understand the following:

     You can't register the client because this would be generic.

You implement chat_server and chat_client as gen_server modules.
Something like the following (WARNING: The code is incomplete and

%%% SERVER %%%


%%% API

start(SrvName) ->
     gen_server:start({local, SrvName}, ?MODULE, [], []).

start_link(SrvName) ->
     gen_server:start_link({local, SrvName}, ?MODULE, [], []).

stop(Srv) ->
     gen_server:cast(Srv, stop).

%% Connect a chat client process to a chat server.
connect_client(Srv, Client) ->
     gen_server:call(Srv, {connect_client, Client}).

%% Send Msg (a string) from a Nick to all currently connected clients.
broadcast_message(Srv, Nick, Msg) ->
     gen_server:cast(Srv, {broadcast_message, Nick, Msg}).

%%% Callbacks

init([]) ->
     %% This way the server will be informed about termination
     %% of its clients (see handle_info/2 below).
     erlang:process_flag(trap_exit, true),
     {ok, []}.

handle_call({connect_client, Client}, Clients) ->
     %% Establish a two-way relationship between the server and Client.
     %% Because of process_flag/2 above, the server will be notified if
     %% Client terminates for whatever reason.
     {reply, ok, [Client | Clients]}.

handle_cast({broadcast_message, SenderNick, Msg}, Clients) ->
     lists:foreach(fun (Client) ->
                       chat_client:receive_message(Client, SenderNick, Msg)
     {noreply, Clients};
handle_cast(stop, Clients) ->
     %% This will automatically terminate all connected clients.
     {stop, shutdown, Clients}.

handle_info({'EXIT', Client, _Reason}, Clients) ->
     {noreply, lists:delete(Client, Clients)}.

%%% CLIENT %%%

-record(state, {nick, server}).

%%% API

start(Nick, Srv) ->
     gen_server:start(?MODULE, [Nick, Srv], []).

start_link(Nick, Srv) ->
     gen_server:start_link(?MODULE, [Nick, Srv], []).

stop(Client) ->
     %% This will inform the server about the end of my life.
     exit(Client, shutdown).

say(Client, Msg) ->
     gen_server:cast(Client, {say, Msg}).

%% Called by the chat_srv during its broadcast_message/2 to
%% deliver Msg to each connected client.
receive_message(Client, SenderNick, Msg) ->
     gen_server:cast(Client, {receive_message, SenderNick, Msg}).

%%% Callbacks

init([Nick, Srv]) ->
     self() ! connect,
     {ok, #state{nick=Nick, server=Srv}}.

handle_cast({say, Msg}, #state{server=Srv, nick=Nick} = State) ->
     chat_server:broadcast_message(Srv, Nick, Msg),
     {noreply, State};
handle_cast({receive_message, SenderNick, Msg}, State) ->
     io:format("~p: ~p~n", [SenderNick, Msg]),
     {noreply, State};

handle_info(connect, State) ->
     ok = chat_server:connect_client(State#state.server, self()),
     {noreply, State}.

%%% USAGE %%%

srv_node> chat_srv:start(chat).
{ok, ...}

client_1> {ok, AdamPid} = chat_client:start("Adam", {chat, srv_node}).
{ok, AdamPid}

client_2> {ok, EvePid} = chat_client:start("Eve", {chat, srv_node}).
{ok, EvePid}

client_1> chat_client:say(AdamPid, "Hello!").
Adam: Hello!

Adam: Hello!

client_2> chat_client:say(EvePid, "Oh, hi handsome!").
Eve: Oh, hi handsome!

Eve: Oh, hi handsome!

Call chain to start new client (server must be already running):
1. [Client shell] chat_client:start(Nick, Srv).
2. [Chat client]  chat_client:init([Nick, Srv]).
3. [Chat client]  chat_client:handle_info(connect,...).
4. [Chat client]  chat_server:connect_client(Srv, self()).
5. [Chat server]  chat_server:handle_call({connect_client, Client},...).

Call chain to send a message (server and client are running):
1. [Client shell] chat_client:say(Client, Msg).
2. [Chat client]  chat_client:handle_cast({say, Msg},...).
3. [Chat client]  chat_server:broadcast_message(Srv, SenderNick, Msg).
4. [Chat server]  chat_server:handle_cast({broadcast_message, SenderNick, Msg),...).
5. [Chat server]  For each connected client:
5.1 [Chat server]  chat_client:receive_message(EachClient, SenderNick, Msg).
5.2 [Chat client]  chat_client:handle_cast({receive_message, SenderNick, Msg},...).

What's missing:
* Some of the server internals.
* Some of the client internals.
* Tests.
* Store messages on clients for further processing instead of just
   printing them.
* You can generate and broadcast automatic connect / disconnect

Hope this helps,

Ladislav Lenart

On 12.4.2012 23:39, megalomania wrote:
> up!
> Well im trying make a gen_server for a client, but i dont now how get the
> messages that server sends, i cant register the client cuz this would be
> generic, any idea?
> --
> View this message in context: http://erlang.2086793.n4.nabble.com/Comunication-between-2-gen-server-s-tp4551836p4553168.html
> Sent from the Erlang Questions mailing list archive at Nabble.com.
> _______________________________________________
> erlang-questions mailing list
> http://erlang.org/mailman/listinfo/erlang-questions

More information about the erlang-questions mailing list