Erlang gen_statem 使用笔记

直接上代码:( flash_policy_server )

-module(tcp_client).
-behaviour(gen_statem).
-export([start_link/0, set_socket/2,callback_mode/0]).
-export([init/1, handle_event/4,terminate/3, code_change/4]).

-define(PACKET,0).
-define(PACKETTYPE,list).

start_link() ->
    gen_statem:start_link(?MODULE, [], []).

set_socket(Pid, Socket) when is_pid(Pid), is_port(Socket) ->
    gen_statem:cast(Pid, {socket_ready, Socket }).

init([]) ->
    process_flag(trap_exit, true),
    State = #{},
    {ok, wait_for_socket , State}.
callback_mode() -> handle_event_function.
handle_event(cast,{socket_ready,Socket},wait_for_socket,_State) ->
    inet:setopts(Socket, [?PACKETTYPE, {packet, ?PACKET}, {delay_send,false}, {active, once}]),
    {next_state,wait_for_data,
                #{socket => Socket, mode => connect },
                {timeout,5000,{conn_timeout}}
    };

handle_event(timeout,{conn_timeout},wait_for_data,State) ->
    {stop,normal,State};

handle_event(info,{tcp, Socket, _Bin},wait_for_data,#{socket := Socket} = State) ->
    gen_tcp:send(Socket,"\0"),
    {stop,normal,State};

handle_event(info,{tcp_error,_,_},_,State) ->
    {stop, normal, State};


handle_event(info,{tcp_closed,_,_},_,State) ->
    {stop, normal, State};

handle_event(info,_Event,_,State) ->  
    {stop, normal, State}.


terminate(_Reason, _StateName, _State) ->
    ok.

code_change(_OldVsn, OldState, StateData, _Extra) ->
    {ok,OldState,StateData}.

和gen_fsm的一些区别:
1. 没有 handle_info , handle_sync_event
2. init/1 返回值以CallbackMode 开始
OTP 19.1 以后,init 返回还原成 { ok,StateName,StateData }
3. code_change/4 返回值也是以 CallbackMode 开始
OTP 19.1 以后,code_change/4 返回还原成 {ok,OldState,StateData}
4. 增加 call cast ,代替 gen_fsm:send_event
5. StateData 可以使用任何数据结构

  1. OTP 19.1 以后,必须导出 callback_mode/0 返回
    handle_event_function 或者 state_functions

gen_statem 支持两种回调模式:
1. state_functions :和gen_fsm类似,自定义函数来完成状态传递。
2. handle_event_function : 使用handle_event完成状态传递,个人觉得这种模式比较好,不用定义一些乱七八糟的函数,让代码更整齐。

关于handle_event
gen_statem:call(Pid,{start}) .
对应回调 handle_event(call,{start},StateName,StateData)
gen_statem:cast(Pid,{start}.
对应回调 handle_event(cast,{start},StateName,StateData)

Pid ! {start} (常见socket收到消息)
对应回调 handle_event(info,{start},StateName,StateData)
和 gen_fsm 的 handle_info 类似

关于 handle_event_result
如果状态保持不变,可以使用 {keep_state ,StateData}
如果状态和数据都不变,可以使用 {keep_state_and_data}
这个比gen_fsm每次都调用 next_state 方便多了

关于Timeout
{next_state,StateName,StateData,Action}
现在设置timeout放在action里面,格式 {timeout,N,{timeout_type}}
这个 {timeout_type} 将传递给
handle_event(timeout,{timeout_type},StataName,StateData)

还有一些特性还没有去研究,总之gen_statem很不错,建议把gen_fsm都替换成gen_statem。而且性能上应该有所提高。

你可能感兴趣的:(erlang)