Last week I was speaking at the London Erlang Factory Lite where I presented a live demonstration of building an FTP server using Ranch. As there was no slide, you should use this article as a reference instead.
The goal of this article is to showcase how to use Ranch for writing a network protocol implementation, how Ranch gets out of the way to let you write the code that matters, and the common techniques used when writing servers.
Let's start by creating an empty project. Create a new directory and
then open a terminal into that directory. The first step is to add Ranch
as a dependency. Create the
rebar.config file and add the
following 3 lines.
This makes your application depend on the last Ranch version available on the master branch. This is fine for development, however when you start pushing your application to production you will want to revisit this file to hardcode the exact version you are using, to make sure you run the same version of dependencies in production.
You can now fetch the dependencies.
This will create a
deps/ folder containing Ranch.
We don't actually need anything else to write the protocol code.
We could make an application for it, but this isn't the purpose of this
article so let's just move on to writing the protocol itself. Create
ranch_ftp_protocol.erl and open it in your favorite
Let's start with a blank protocol module.
When Ranch receives a connection, it will call the
function with the listener's pid, socket, transport module to be used,
and the options we define when starting the listener. We don't need options
for the purpose of this article, so we don't pass them to the process we are
Let's open a shell and start a Ranch listener to begin accepting connections. We only need to call one function. You should probably open it in another terminal and keep it open for convenience. If you quit the shell you will have to repeat the commands to proceed.
Also note that you need to type
to recompile and reload the code for the protocol. You do not need to
restart any process however.
This starts a listener named
my_ftp that runs your very own
ranch_ftp_protocol over TCP, listening on port
The last argument is the options given to the protocol that we ignored
To try your code, you can use the following command. It should be able to connect, the server will print a message in the console, and then the client will print an error.
Let's move on to actually writing the protocol.
Once you have created the new process and returned the pid, Ranch will give ownership of the socket to you. This requires a synchronization step though.
Now that you acknowledged the new connection, you can use it safely.
When an FTP server accepts a connection, it starts by sending a
welcome message which can be one or more lines starting with the
200. Then the server will wait for the client
to authenticate the user, and if the authentication went successfully,
which it will always do for the purpose of this article, it will reply
As you can see we don't need complex parsing code. We can simply match on the binary in the argument!
Next we need to loop receiving data commands and optionally execute them, if we want our server to become useful.
We will replace the
ok. line with the call to
the following function. The new function is recursive, each call
receiving data from the socket and sending a response. For now
we will send an error response for all commands the client sends.
With this we are almost ready to start implementing commands. But with code like this we might have errors if the client doesn't send just one command per packet, or if the packets arrive too fast, or if a command is split over multiple packets.
To solve this, we need to use a buffer. Each time we receive data, we will append to the buffer, and then check if we have received a command fully before running it. The code could look similar to the following.
The implementation of
split/1 is left as an exercice
to the reader. You will also probably want to handle the
command, which must stop any processing and close the connection.
The attentive reader will also take note that in the case of text-
based protocols where commands are separated by line breaks, you can
set an option using
Transport:setopts/2 and have all the
buffering done for you for free by Erlang itself.
As you can surely notice by now, Ranch allows us to build network applications by getting out of our way entirely past the initial setup. It lets you use the power of binary pattern matching to write text and binary protocol implementations in just a few lines of code.