Protohackers in Elixir - day 0

I followed a tutorial and created a gen_tcp server. The code is shown below. The server accepts connections, receives data but the function recv_until_closed (at the very bottom) never matches on {:error, :closed}. Consequently, the connections are never closed. Instead I eventually get timeout errors. I’m testing it by echoing a string to nc localhost 5001 as shown in the video (around 17:30)

This is the first video in the Photohackers in Elixir series

Is there some obvious error in my code that I’m failing to see?

defmodule EventListener.Server do
  use GenServer
  require Logger

  def start_link([] = _opts) do
    GenServer.start_link(__MODULE__, :no_state)
  end

  defstruct [:listen_socket]

  @impl true
  def init(:no_state) do

    listen_options = [
      ifaddr: {0, 0, 0, 0},
      mode: :binary,
      active: false,
      reuseaddr: true,
      exit_on_close: false
    ]

    case :gen_tcp.listen(5001, listen_options) do
      {:ok, listen_socket} ->

        state = %__MODULE__{listen_socket: listen_socket}
        {:ok, state, {:continue, :accept}}

      {:error, reason} ->
        {:stop, reason}
    end
  end

  @impl true
  def handle_continue(:accept, %__MODULE__{} = state) do
    case :gen_tcp.accept(state.listen_socket) do
      {:ok, socket} ->
        handle_connection(socket)
        {:noreply, state, {:continue, :accept}}
      {:error, reason} ->
        {:stop, reason}
    end
  end

  defp handle_connection(socket) do
    case recv_until_closed(socket, _buffer = "") do
      {:ok, data} -> 
        :gen_tcp.send(socket, data)
      {:error, reason} ->
        Logger.error("Failed to receive data: #{inspect(reason)}");
    end

    :gen_tcp.close(socket)
  end

  defp recv_until_closed(socket, buffer) do
    case :gen_tcp.recv(socket, 0, 10_000) do
      {:ok, data} -> 
        recv_until_closed(socket, [buffer, data])
      {:error, :closed} -> 
        {:ok, buffer} 
      {:error, reason} -> {:error, reason}
    end
  end
end
2 Likes

I’m testing it by echoing a string to nc localhost 5001 as shown in the video

-N helps. From man nc:

-N
shutdown(2) the network socket after EOF on the input. Some servers require this to finish their work.

1 Like

nevermind :slight_smile: