create project skeleton:

mkdir eco cd eco wget https://github.com/rebar/rebar/wiki/rebar chmod u+x rebar ./rebar create-app appid=eco

let's add some dependencies, ranch to accept tcp connections and lager for logging, for that open rebar.config with your text editor and enter this:

{deps, [ {lager, "2.1.0", {git, "https://github.com/basho/lager", {tag, "2.1.0"}}}, {ranch, "1.1.0", {git, "https://github.com/ninenines/ranch", {tag, "1.1.0"}}} ]}. {erl_opts, [debug_info, {parse_transform, lager_transform}]}.

Note if you put lager dep after ranch you will get an error when compiling, that's sad

now let's try compiling it:

./rebar get-deps ./rebar compile

we can start our app from the shell, which won't be really useful, but why not:

erl -pa ebin/ deps/*/ebin

and we run:

1> application:start(eco). ok

now let's use ranch and lager for something, first we create a protocol implementation, open a file called eco_protocol.erl and put the following content in it:

- module ( eco_protocol ). - behaviour ( ranch_protocol ). - export ([ start_link / 4 ]). - export ([ init / 4 ]). start_link ( Ref , Socket , Transport , Opts ) -> Pid = spawn_link ( ? MODULE , init , [ Ref , Socket , Transport , Opts ]), { ok , Pid }. init ( Ref , Socket , Transport , _ Opts = []) -> ok = ranch : accept_ack ( Ref ), loop ( Socket , Transport ). loop ( Socket , Transport ) -> case Transport : recv ( Socket , 0 , 5000 ) of { ok , Data } -> lager : info ( "echoing ~p " , [ Data ]), Transport : send ( Socket , Data ), loop ( Socket , Transport ); _ -> ok = Transport : close ( Socket ) end .

edit the start function in src/eco_app.erl so it looks like this:

start (_ StartType , _ StartArgs ) -> { ok , _} = ranch : start_listener ( eco , 1 , ranch_tcp , [{ port , 1883 }], eco_protocol , []), eco_sup : start_link ().

and add the apps we need in eco.app.src by adding ranch and lager to the applications entry like this:

{ applications , [ kernel , stdlib , ranch , lager ]},

now let's compile and try again:

./rebar compile

Erlang/OTP 17 [erts-6.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] Eshell V6.3 (abort with ^G) 1> application:start(eco). {error,{not_started,ranch}} 2> application:start(ranch). ok 3> application:start(eco). {error,{not_started,lager}} 4> application:start(lager). {error,{not_started,goldrush}} 5> application:start(goldrush). {error,{not_started,syntax_tools}} 6> application:start(syntax_tools). ok 7> application:start(goldrush). {error,{not_started,compiler}} 8> application:start(compiler). ok 9> application:start(goldrush). ok 10> application:start(lager). ok 11> 21:05:52.373 [info] Application lager started on node nonode@nohost 11> application:start(eco). ok 21:06:09.335 [info] Application eco started on node nonode@nohost

Note user Cloven from reddit noted that instead of starting all the applications by hand in order you could use: application:ensure_all_started(eco). I was sure there was a way to do it since each app specified the dependencies, you can tell from the fact that each app tells you which one it needs before starting, but I didn't know which was the function to call. thanks to him!

now let's send some data:

telnet localhost 1883 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. asd asd

(I wrote the first asd, the second is the reply)

in the console you should see this log line:

21:10:05.098 [info] echoing <<"asd\r

">>

now let's build a release so others can use our server (?):

mkdir rel cd rel ../rebar create-node nodeid=eco

add the following two lines to rebar.config:

{sub_dirs, ["rel"]}. {lib_dirs, ["deps"]}.

and edit rel/reltool.config, change the lib_dirs entry to this:

{lib_dirs, ["../deps"]},

add ranch and lager in the rel entry:

{rel, "eco", "1", [ kernel, stdlib, sasl, ranch, lager, eco ]},

and change the app, echo entry to look like this:

{app, eco, [{mod_cond, app}, {incl_cond, include}, {lib_dir, ".."}]}

now let's try to build a release:

./rebar compile ./rebar generate

now let's start our server:

./rel/eco/bin/eco console

you should see some output like this:

Erlang/OTP 17 [erts-6.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] =INFO REPORT==== 5-Feb-2015::22:15:22 === inet_parse:"/etc/resolv.conf":4: erroneous line, SKIPPED 21:15:22.393 [info] Application lager started on node 'eco@127.0.0.1' 21:15:22.394 [info] Application eco started on node 'eco@127.0.0.1' Eshell V6.3 (abort with ^G) (eco@127.0.0.1)1>

now let's telnet again:

telnet localhost 1883 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. lala! lala!

on the console again you should see some log like this:

21:16:01.540 [info] echoing <<"lala!\r

">>

and that's it, now evolve your echo server into an actual server :)