[erlang-questions] "calling" a gen_fsm

Roger Lipscombe <>
Wed Sep 23 10:58:57 CEST 2015


On 23 September 2015 at 05:01, Garry Hodgson <> wrote:
> On 9/22/15 3:02 PM, Roger Lipscombe wrote:
>> I'd just add one point, though: I recently built something similar,
>> and I found it simpler to separate the client into two processes: one
>> gen_fsm to handle the state machine, and a separate gen_server to
>> handle the decoding and framing.
>
> i briefly considered that, but it wasn't obvious how to divide the
> work between the two. i'd be interested in how you did, if you'd
> care to elaborate.

Hmmm. I'll have a go. This is an approximation:

% client.erl -- lots of details ellided
-module(client).
-behaviour(gen_fsm).

init([Host, Port, User, Password]) ->
    {ok, C} = client_connection:start_link(self(), Host, Port),
    client_connection:send(C, {connect, User, Password}),
    {ok, connecting, #state{ conn = C }}.

handle_info({connected, Token}, connecting, StateData) ->
    {next_state, connected, StateData#state{ token = Token }};
handle_info({friends, Txn, Friends}, connected, StateData) ->
    {From, NewStateData} = pop_txn(Txn, StateData),
    gen_fsm:reply(From, {ok, Friends}),
    {next_state, connected, NewStateData};
% etc.

handle_sync_event(get_friends, _From, connecting, State) ->
    {reply, {error, not_connected}, connecting, State};
handle_sync_event(get_friends, From, connected, State) ->
    Txn = new_txn_id(),
    client_connection:send(C, {get_friends, Txn, State#state.token}),
    {next_state, connected, add_txn_to_state({Txn, From}, State)};
% etc.

% client_connection.erl
-module(client_connection).
-behaviour(gen_server).

start_link(Parent, Host, Port) ->
    % etc.
    {ok, #state{ parent = Parent, %etc }}.

send(C, Msg) ->
    Bin = client_codec:encode(Msg),
    gen_server:cast(C, {send, Bin}).

handle_info({tcp, Bin}, State = #state{ buffer = Buffer, parent = Parent }) ->
    case client_codec:decode(Buffer, Bin) of
        {need_more, NewBuffer} ->
            {noreply, State#state{ buffer = NewBuffer }};
        {ok, Msg, NewBuffer} ->
            Parent ! Msg,
            {noreply, State#state{ buffer = NewBuffer }}
    end

...something like that, anyway.


More information about the erlang-questions mailing list