what about little-endians?

Edwin emofine@REDACTED
Sat Mar 13 01:56:23 CET 2010


It's not actually that difficult to decode it yourself. Switch to
{active, false} and spawn a process to extract messages from the TCP
stream using gen_tcp:read/2,3 and send them to your gen_fsm/
gen_server.

Here's something to start you off. It does work, but I'm sure there
may be bugs so no guarantees and I am certain someone else could write
it better than this.

-module(demux).
-behaviour(gen_server).

%% API
-export([start/2, start_link/1, start_link/2, stop/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
     terminate/2, code_change/3]).

-export([wait_recv/3, recv_loop/3]).

-define(SERVER, ?MODULE).

-record(state, {
        host,
        port,
        recv_pid,
        socket
    }).

%%====================================================================
%% API
%%====================================================================
start(Host, Port) when is_integer(Port) ->
    gen_server:start(?MODULE, [Host, Port], []).

start_link(Host, Port) when is_integer(Port) ->
    gen_server:start_link(?MODULE, [Host, Port], []).

start_link([Host, Port]) when is_list(Port) ->
    NPort = list_to_integer(Port),
    gen_server:start_link(?MODULE, [Host, NPort], []).

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

%%====================================================================
%% gen_server callbacks
%%====================================================================

% Connect to host:port and wait for it to send stuff
init([Host, Port]) ->
    {ok, Sock} = gen_tcp:connect(Host, Port, [binary, {packet, 0},
{active, false}]),
    Self = self(),
    Pid = spawn_link(fun() -> wait_recv(Self, Sock, <<>>) end),
    {ok, #state{host = Host, port = Port, recv_pid = Pid, socket =
Sock}}.

handle_call(_Request, _From, State) ->
    {reply, {error, invalid_request}, State}.

handle_cast(stop, State) ->
    (catch gen_tcp:close(State#state.socket)),
    {stop, normal, State};
handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info({tcp_packet, Packet}, State) ->
    io:format("Got a packet: ~p~n", [Packet]),
    % Do something with packet
    {noreply, State};
handle_info({recv_error, {_Error, _Socket} = Err}, State) ->
    io:format("Got a receive error: ~p~n", [Err]),
    {stop, Err, State};
handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, State) ->
    io:format("Closing socket~n"),
    (catch gen_tcp:close(State#state.socket)),
    ok.

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

%%--------------------------------------------------------------------
%% Internal
%%--------------------------------------------------------------------
wait_recv(SvrRef, Socket, Buffer) ->
    case gen_tcp:recv(Socket, 0) of
        {ok, Input} ->
            io:format("wait_recv: got ~p~n", [Input]),
            B = handle_input(SvrRef, <<Buffer/bytes, Input/bytes>>),
            ?MODULE:recv_loop(SvrRef, Socket, B);
        Error ->
            SvrRef ! {recv_error, {Error, Socket}}
    end.

recv_loop(SvrRef, Socket, Buffer) ->
    case gen_tcp:recv(Socket, 0, 0) of
        {ok, Input} ->                    % Some input waiting
already
            io:format("recv_loop: got ~p~n", [Input]),
            B = handle_input(SvrRef, <<Buffer/bytes, Input/bytes>>),
            ?MODULE:recv_loop(SvrRef, Socket, B);
        {error, timeout} ->               % No more socket data
immediately available
            B = handle_input(SvrRef, Buffer),
            ?MODULE:wait_recv(SvrRef, Socket, B);
        Error ->
            SvrRef ! {recv_error, {Error, Socket}}
    end.

handle_input(SvrRef, <<Length:32/little-integer, Data/bytes>>) ->
    case Data of
        <<Packet:Length/bytes, Rest/bytes>> ->
            io:format("Sending tcp_packet to ~p: ~p~n", [SvrRef,
Packet]),
            SvrRef ! {tcp_packet, Packet},
            handle_input(SvrRef, Rest);
        TooShort ->
            TooShort
    end;

handle_input(_SvrRef, <<_Data/bytes>> = B) ->
    B.

To test it, run it in one Erlang node, and run this small test program
in another:

-module(demux_test).
-compile([export_all]).

go(Port) when is_integer(Port) ->
    Opts = [binary, {packet, 0}],
    {ok, LSock} = gen_tcp:listen(Port, Opts),
    {ok, Sock} = gen_tcp:accept(LSock),
    Msg = <<"Hello, sock.\n">>,
    send_msg(Sock, Msg, 100).

send_msg(Sock, Msg, Count) when Count > 0 ->
    lists:foreach(
        fun(X) ->
                N = integer_to_list(X),
                NumberedMsg = list_to_binary([N, $:, Msg]),
                Len = byte_size(NumberedMsg),
                gen_tcp:send(Sock, <<Len:32/integer-little,
NumberedMsg/bytes>>)
        end,
        lists:seq(1, Count)
    ).

Hope this helps.

On Mar 12, 2:45 pm, Camilo Cerchiari <camilo.cerchi...@REDACTED>
wrote:
> i'm ok with writing it just for myself, if it would be so.
> some advice to get started with the code is then what i'm looking for.
> where should i start?
>
>
>
> On Fri, Mar 12, 2010 at 17:20, Max Lapshin <max.laps...@REDACTED> wrote:
> > I think, you will be refused for this feature, because big-endian is
> > standard for network transmission.
> > In this case you will have to write your own buffered demuxer.


More information about the erlang-questions mailing list