Search code examples
erlanggen-tcp

Erlang TCP sockets get closed


To learn Erlang I am trying to implement a tiny web server based on gen_tcp. Unfortunately, my code seems to trigger some wired behaviour. To demonstrate the problem I have attached a minimised version of my implementation which is sufficient to reproduce the problem. It is just delivering a static 200 OK, no matter what the HTTP request was.

The problem arises when I try to run ab (Apache HTTP server benchmarking) against my web server (using loopback interface). Without any concurrent requests (-c) everything is running just fine. However, if I use -c 8 or -c 16, the call to gen_tcp:accept/1 seems to fail on some sockets as I see a number of request: closed lines in the shell.

What makes the whole story even weirder is, that I see different behaviours on different operating systems:

  • OS X+Erlang/OTP 18: ab reports "Connection reset by peer" almost immediately after starting.
  • Debian+Erlang R15B01: All but two of the HTTP requests seem to run through. But then, ab hangs for a few seconds and reports "The timeout specified has expired, Total of 4998 requests completed", when i run ab with -n 5000. Similarly, 14998 is reported when I run 15000 tests.

This one does not seem to be the problem. I am honestly quite lost and therefore appreciate any help! :) Thanks!

server(Port) ->
    Opt = [list, {active, false}, {reuseaddr, true}],
    case gen_tcp:listen(Port, Opt) of
        {ok, Listen} ->
            handler(Listen),
            gen_tcp:close(Listen),
            ok;
        {error, Error} ->
            io:format("init: ~w~n", [Error])
    end.

handler(Listen) ->
    case gen_tcp:accept(Listen) of
        {ok, Client} ->
            request(Client),
            handler(Listen);
        {error, Error} ->
            io:format("request: ~w~n", [Error])
    end.

request(Client) ->
    Recv = gen_tcp:recv(Client, 0),
    case Recv of
        {ok, _} ->
            Response = reply(),
            gen_tcp:send(Client, Response);
        {error, Error} ->
            io:format("request: ~w~n", [Error])
    end,
    gen_tcp:close(Client).


reply() ->
    "HTTP/1.0 200 OK\r\n" ++
    "Content-Length: 7\r\n\r\n"
    "static\n".

Solution

  • When you increase the number of concurrent requests sent with ab -c N it will immediately open multiple TCP sockets to the server.

    By default a socket opened with gen_tcp:listen/2 will support only five outstanding connection requests. Increase the number of connection requests outstanding with the {backlog, N} option to gen_tcp:listen/2.

    I tested your code on OS X with ab and saw this resolve the prolem with "Connection reset by peer".