Erlang Echo Server
May 19th, 2010
In this post, I’ll be looking at an example of a simple Erlang program: an echo server. An echo server accepts connections from clients and simply responds to any data received with the same data. While the example given here is quite simple, it is able to deal with multiple simultaneous client connections and handles most errors in a sensible way.
The Module Header
1 2 3 4 5 6 | -module(echo). -export([start/1, handle_connections/1, handle_client/1]). -define(TCP_OPTIONS, [binary, {packet, raw}, {active, false}, {reuseaddr, true}]). |
First off I define the module, which is named echo and so it should go in the file “echo.erl”. Then I export the functions that make up the server. The last line of the header defines a constant called TCP_OPTIONS with a value which is a list of options which will be used by the sockets in the server program. This constant may be used in code later as ?TCP_OPTIONS (note the question mark). The options are as follows:
binary- Data received through the sockets should be in the form of a binary object. There’s no need for me to cover binary terms right now, as they aren’t really covered in this example.
{packet, raw}- The data received should just be the raw data. It is possible to have the data delivered with a header specifying how much data was received, but I don’t want that here.
{active, false}- Data will only be read when requested by the program. It is possible to have the data delivered to a process in the form of a message, but again that isn’t what is wanted here.
{reuseaddr, true}- This makes sure that the listening socket can be bound even if there is already a socket bound there. This prevents errors if the server crashes and is restarted before the original socket becomes properly closed.
Starting the Server
8 9 10 11 12 13 14 | start(Port) -> case gen_tcp:listen(Port, ?TCP_OPTIONS) of {ok, LSock} -> spawn(?MODULE, handle_connections, [LSock]); {error, Reason} -> io:fwrite("Error: ~p~n", [Reason]) end. |
This function starts the server. It takes one argument which is the port to listen for connections on. First it calls the function listen from the gen_tcp library with the port number and the TCP options constant as defined in the header. This attempts to open the listening socket.
If this succeeds, spawn is called to create a new process. The three arguments to this function are the module in which the function to run in this new process resides, the name of the function and a list of arguments to the function. In this case the function is called handle_connections and it is in the current module (?MODULE is a macro which is replaced with the name of the current module). We pass the listening socket opened with gen_tcp:listen to the function.
If opening the listening socket fails, we call io:fwrite to write an error message containing the reason for the error.
Handling Connections
16 17 18 19 20 21 22 23 | handle_connections(LSock) -> case gen_tcp:accept(LSock) of {ok, Sock} -> spawn(?MODULE, handle_client, [Sock]); {error, Reason} -> io:format("Error: ~p~n", [Reason]) end, handle_connections(LSock). |
This is the function that is run in a process created by the start function. I takes as an argument a listening socket to listen for connections on. This function calls gen_tcp:accept to accept a connection from the listening socket. Again if this results in an error we print a message. If it is successful, a new process is started to handle the client that connected. This runs the function handle_client in the current module and passes it the client socket as an argument.
Once this function has completed, it calls itself again with the listening socket. As Erlang provides no looping constructs, loops are created by recursive calls. As long as the call to the function is the last line executed in that function, this will not cause stack problems as it would in many languages such as C and its derivatives. This function keeps accepting connections and spawning client handler processes until the process it is running in is killed.
Handling Clients
25 26 27 28 29 30 31 32 33 34 | handle_client(Sock) -> case gen_tcp:recv(Sock, 0) of {ok, Data} -> gen_tcp:send(Sock, Data), handle_client(Sock); {error, closed} -> ok; {error, Reason} -> io:fwrite("Error: ~p~n", [Reason]) end. |
This is the function spawned whenever a client connects to the server. It first attempts to receive some data from the client socket passed to it with the gen_tcp:recv function which takes the socket to receive from and a number of bytes to receive. As we want to receive all available data, we pass 0 which means there is no specified maximum number of bytes to accept.
If successful, the data is sent back to the client (this is what the echo server is designed to do) with the gen_tcp:send function. The function then calls itself to create a loop as with the connection handler. Here the recursive call is not the last line of the function, but as none of the rest of the code in the case statement will be run if the data is successfully received, it is the last line executed. There are therefore again no problems with the call stack overflowing.
If the result {error, closed} is obtained, this means that the client has closed the connection. This is normal behaviour, so no error message is printed, the function simply exits and the process will stop. Unfortunately, the case statement must evaluate to a value, so it is necessary to give a value here. In this case I have chosen ok although any value would do.
The final part of the case statement prints an error message again if there was a problem receiving data from the client. It is assumed that this error is fatal and so the process stops.
The Complete Program
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | -module(echo). -export([start/1, handle_connections/1, handle_client/1]). -define(TCP_OPTIONS, [binary, {packet, raw}, {active, false}, {reuseaddr, true}]). start(Port) -> case gen_tcp:listen(Port, ?TCP_OPTIONS) of {ok, LSock} -> spawn(?MODULE, handle_connections, [LSock]); {error, Reason} -> io:fwrite("Error: ~p~n", [Reason]) end. handle_connections(LSock) -> case gen_tcp:accept(LSock) of {ok, Sock} -> spawn(?MODULE, handle_client, [Sock]); {error, Reason} -> io:format("Error: ~p~n", [Reason]) end, handle_connections(LSock). handle_client(Sock) -> case gen_tcp:recv(Sock, 0) of {ok, Data} -> gen_tcp:send(Sock, Data), handle_client(Sock); {error, closed} -> ok; {error, Reason} -> io:fwrite("Error: ~p~n", [Reason]) end. |
Here is the complete code for the program, put this in a file called “echo.erl”. To run it start an Erlang shell in the directory where you have saved the file and run the following two commands to get it started:
echo:start(1234).
This will start the server listening for connections on port 1234. You can then connect to it via telnet or something similar, anything you send to the server should be echoed back to you. There is no way of stopping the server appart from killing all the processes that are handling connections and clients so it’s probably easier to just to close Erlang down.
Category: Programming | Tags: echo, erlang, sockets

Hi there, just became alert to your blog through Google, and found that it’s really informative. I am going to watch out for brussels. I?ll be grateful if you continue this in future. Numerous people will be benefited from your writing. Cheers!
Hey! This post could not be written any better! Reading this post reminds me of my previous room mate! He always kept talking about this. I will forward this post to him. Fairly certain he will have a good read. Many thanks for sharing!
This is a very good article for beginners as myself. I really enjoy haskell, and I have heard a lot of good things about erlang.
I’m developing a distributed client-server system, in which there is a master server which balances the load between several slave servers. The calculations’ results which the slaves computes should be backed up in some of the other servers. In case one of them crashes, the operation does not have to be repeated.
The project has a lot of concurrency and communication, so I thought erlang would suit it nicely. ‘no side-effects’ is a plus, since I won’t have to worry about mutexes or monitors.
I have some Java and C RMI/Sockets programming experience. But it seems more fun with erlang.
Anyways, is it possible to transmit lists through the socket and have them become lists automatically on the other side? Is there a way to use RPC in erlang? And finally, have you any experience multicasting messages (in erlang)?
Thanks for the article.