[erlang-questions] gen_tcp:recv and hairloss, easy to run example here
Kevin
q2h46uw02@REDACTED
Sat Nov 8 20:45:15 CET 2008
I distilled my bug down to two easy to run and understand files in
appreciation for anybody who will take a look.
Two files, cut at lines.
compile with:
erlc *.erl
run with:
erl -s ...._gen_server start_link
telnet to:
localhost 8000
type anything and hit return to echo,
type "sssh" to change to inner recv, error message should echo back
The structure of having an inner recv might seem strange, but it makes
sense because the
outer loop processes commands in text mode, and then switches down into
a tight loop
that handles an unlimited stream of binary until the end. The FSM
controls this, and the main
loop is above the FSM, so the FSM only knows when to take over the
streaming.
The example code does a bad job justifying the use of a FSM and the
inner loop though :)
Can't wait until somebody points out how clueless I am :-)
%%-- cut
here------------------------------------------------------------------
-module(...._gen_server).
-behaviour(gen_server).
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-record(state, {lsocket, port}).
-export([acceptor/1]).
-define(TCP_OPTS, [binary,
{packet, raw},
{active, false}, % get data from gen_tcp:recv/2
{reuseaddr, true}]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, #state{}, []).
init(State) ->
case gen_tcp:listen(8000, ?TCP_OPTS) of
{ok, LSocket} ->
{ok, spawn_acceptor(State#state{lsocket=LSocket})};
{error, Reason} ->
erlang:display(Reason),
{stop, Reason}
end.
handle_cast({accepted}, State) ->
{noreply, spawn_acceptor(State)}.
spawn_acceptor(State = #state{lsocket=LSocket}) ->
proc_lib:spawn_link(?MODULE, acceptor, [{self(), LSocket}]),
State.
acceptor({Server, LSock}) ->
{ok, Sock} = gen_tcp:accept(LSock),
gen_server:cast(Server, {accepted}),
{ok, Fsm} = ...._fsm:start_link(),
gen_tcp:send(Sock, "hello, I will echo, until you type sssh\r\n"),
loop(Sock, Fsm).
loop(Sock, Fsm) ->
case gen_tcp:recv(Sock, 0) of
{ok, Request} ->
ok = gen_fsm:send_event(Fsm, {Request, Sock}),
loop(Sock, Fsm);
{error, Reason} ->
io:format("gen_server recv error: ~p~n", [Reason])
end.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_info(_Info, State) ->
{noreply, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, _State) ->
ok.
%%------------------------------------------------------------------------------
%%-- cut
here------------------------------------------------------------------
-module(...._fsm).
-behaviour(gen_fsm).
-compile(export_all).
%% gen_fsm stuf to stop the complaining
-export([init/1, handle_info/3, code_change/4,
handle_sync_event/4, terminate/3, handle_event/3]).
-record(state, {}).
start_link() ->
gen_fsm:start_link(?MODULE, [], []).
init([]) ->
{ok, start_state, #state{}}.
start_state({Request, Sock}, State) ->
case Request of
<<"sssh\r\n">> -> %% silent mode
gen_tcp:send(Sock, "silent until you type nosssh\r\n"),
sssh_loop(Sock),
{next_state, start_state, State};
_ ->
gen_tcp:send(Sock, Request),
{next_state, start_state, State}
end.
sssh_loop(Sock) ->
case gen_tcp:recv(Sock, 0) of
{ok, Request} ->
case Request of
<<"nosssh\r\n">> -> %% silent mode
nil;
_ ->
sssh_loop(Sock)
end;
{error, Reason} ->
gen_tcp:send(Sock,
"*** Sorry no silent mode, I keep getting error
\""
++ atom_to_list(Reason) ++ "\"\r\n")
end.
handle_event(stop, _StateName, State) ->
{stop, normal, State}.
terminate(_Reason, _StateName, _State) ->
ok.
handle_sync_event(_Event, _From, StateName, State) ->
{reply, ok, StateName, State}.
handle_info(_Info, StateName, State) ->
{next_state, StateName, State}.
code_change(_OldVsn, StateName, State, _Extra) ->
{ok, StateName, State}.
%%------------------------------------------------------------------------------
More information about the erlang-questions
mailing list