[erlang-questions] tcp data in a gen_fsm

Jesper Louis Andersen jesper.louis.andersen@REDACTED
Sun Nov 29 14:59:53 CET 2015


One possible way is to message yourself. The following needs some help
because it never loops over the unpacking, which it should. But surely you
get the idea. The unpack function is now tasked with returning a list of
decoded packets, not just the first one:

handle_info({tcp, Socket, RawData}, State_name, #state{buffer_state =
BufState} = State) ->
    inet:setopts(Socket, [{active, once}]),
    case unpack(BufState, RawData) of
        {ok, Datas, BufState2} ->
            [gen_fsm:send_event(self(), {packet, D}) || D <- Datas],
            {next_state, State_name, State#state { buffer_state = BufState2
}};
        {more, BufState2} ->
            {next_state, State_name, State#state { buffer_state = BufState2
}}
    end;

Now, you can build two variants of the unpack/2 function. Rather than
returning {more, State} you can simply return {ok, [], State} which
eliminates the need for a separate 'more-variant'. But some times, it is
clever to return {more, NeededBytes, State} or something such in order to
tell the layer how many bytes it needs before it can produce a new packet.
In active mode, this allows you to expect the right number of bytes
directly, which sometimes helps since you can move more or the load into
the Erlang runtime.

Now, the beauty is that the messages will sit in your own mailbox, and in
order. So you just handle them as any other messages and move the state
accordingly. And when you get new stuff in, your decode it in your
handle_info block and inject it into the mailbox. It is a fairly modular
solution as well: you effectively decoupled the TCP state from the rest of
your code, so you can now freely move it out of the FSM should you prefer
doing so. Vice versa, it also decouples the protocol decoding from your FSM
state, so it can worry about the semantic impact of the protocol rather
than the syntactic impact.

On Sun, Nov 29, 2015 at 2:27 PM, Frans Schneider <schneider@REDACTED>
wrote:

> Dear list,
>
> I have a gen_fsm representing the tcp connection to a remote server with 6
> states defined. Tcp data is handled by handle_info with active = once.Tcp
> packets can contain more than one commands/data items.
> I would like to feed the data into the different FSM states in the same
> manner as with send_event() and not handle the different states in separate
> handle_info clauses for the different states or a massive handle_info
> clause with a huge case statement.
> Question is, can I call the state clauses like this or do I brake OTP
> behavior?This is a client and blocking incoming data is not an issue.
>
> handle_info({tcp, Socket, Raw_data}, State_name, #state{buffer = Buffer} =
> State) ->
>     {Datas, Buffer1} = unpack(<<Buffer/binary, Raw_data/binary>>),
>     lists:foldl(fun(Data, {StateName_in, State_in}) ->
>                         {next_state, StateName_out, State_out} =
> ?MODULE:StateName_in({recv, Data}, State_in),
>             {StateName_out, State_out}
>         end, {State_name, State}, Datas),
>     inet:setopts(Socket, [{active, once}]),
>     {next_state, active, State#state{buffer = Buffer1}};
>
> I can of course use a seperate process for handling the tcp stuff which
> makes the gen_fsm calls, but was just wondering if this could work as well.
>
> Thanks,
>
> /Frans
> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://erlang.org/mailman/listinfo/erlang-questions
>



-- 
J.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20151129/bc0e9fde/attachment.htm>


More information about the erlang-questions mailing list