Today I am going to write a brief article about Erlang gen_tcp usage. You may be also interested to read about Erlang implementation of UDP server and client with gen_udp. Probably the reader already has experience with sockets programming. But just to remind server architecture. First of all listening socket should be created, than server is going into accept state waiting for new connection. Once new connection is accepted server creates new thread to process incoming packets. While server waits for another connection.
Erlang echo server code:
-module(tcp_echo).
-export([listen/1]).
-define(TCP_OPTIONS, [binary, {packet, 2}, {active, false}, {reuseaddr, true}]).
-define(PORT, 8080).
% Call echo:listen() to start the server.
listen(Port) ->
{ok, LSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS),
spawn(fun() -> accept(LSocket) end).
% Wait for incoming connections and spawn a process that will process incoming packets.
accept(LSocket) ->
{ok, Socket} = gen_tcp:accept(LSocket),
Pid = spawn(fun() ->
io:format("Connection accepted ~n", []),
loop(Socket)
end),
gen_tcp:controlling_process(Socket, Pid),
accept(LSocket).
% Echo back whatever data we receive on Socket.
loop(Sock) ->
inet:setopts(Sock, [{active, once}]),
receive
{tcp, Socket, Data} ->
io:format("Got packet: ~p~n", [Data]),
gen_tcp:send(Socket, Data),
loop(Socket);
{tcp_closed, Socket}->
io:format("Socket ~p closed~n", [Socket]);
{tcp_error, Socket, Reason} ->
io:format("Error on socket ~p reason: ~p~n", [Socket, Reason])
end.
Security consideration
If you are using active connection - {active, true} in TCP_OPTIONS (personally I prefer active), which is the default, everything received from the socket will be sent as messages to the receiving process.
A day ago I was suggested to use {active, false} in TCP_OPTIONS, because {active, true} may loose socket close events, if socket will be closed really fast before controlling process will be started (I was able to create test code to confirm that problem). So, proper way will be to set socket to {active, false} and once controlling process is started to switch to {active, once} via inet:setopts(Sock, [{active, once}]). This will prevent socket close events loss and will prevent from overload by fast clients, as can happen with {active, true}.
Now just compile and start echo server.
Eshell V5.8.4 (abort with ^G)
1> c(tcp_echo).
{ok,tcp_echo}
2> tcp_echo:listen(8080).
<0.38.0>
Connection accepted
Got packet: <<"Some Data">>
Now create client socket and send packet.
Eshell V5.8.4 (abort with ^G)
1> {ok, Sock} = gen_tcp:connect("localhost", 8080, [binary, {packet, 0}]).
{ok,#Port<0.614>}
2> gen_tcp:send(Sock, <<"Some Data">>).
ok
Hope someone will find this example helpful. You can read article about erlang implementation of Adobe policy server that contains more complicated example of sockets usage.
Ask questions here, if you have, I will try to answer...