Mariem
Mariem

Reputation: 133

How to disable two nodes to connect on the same port?

I'm making a multichat application and here is the code. In this application I made a server and here is the code :

defmodule Multichat.Server do
  require Logger

  def accept(port) do
    {:ok, socket} = :gen_tcp.listen(port, [:binary, packet: :line, active: true, reuseaddr: true])
    Logger.info "Accepting connections on port #{port}"
    loop_acceptor(socket)
  end

  defp loop_acceptor(socket) do
    {:ok, client} = :gen_tcp.accept(socket)
    {:ok, pid} = DynamicSupervisor.start_child(Multichat.Server.ConnectionSupervisor, {Multichat.ClientConnection, client})
    :ok = :gen_tcp.controlling_process(client, pid)
    loop_acceptor(socket)
  end
end

you can see in the code there is a client part :

defmodule Multichat.ClientConnection do
  use GenServer

  def start_link(socket), do: GenServer.start_link(__MODULE__, socket)
  def init(init_arg) do
    {:ok, init_arg}
  end

  def handle_call({:send, message}, _from, socket) do
    :gen_tcp.send(socket, message)
    {:reply, :ok, socket}
  end

  def handle_info({:tcp, _socket, message}, socket) do
    for {_, pid, _, _} <- DynamicSupervisor.which_children(Multichat.Server.ConnectionSupervisor) do
      if pid != self() do
        GenServer.call(pid, {:send, message})
      end
    end

    {:noreply, socket}
  end
end

I want when I start a node on a port, the other node can't but connect on another one. How is that possible ?

Upvotes: 1

Views: 69

Answers (1)

Jos&#233; M
Jos&#233; M

Reputation: 3509

In linux, listening sockets are bound to any of the device's IP or all of them (1), usually the default being listening in all of them (the 0.0.0.0 ip). Once a process is listening in one ip:port, no other socket is able to listen in the same ip:port (2)

(1) This is not entirely true, because you have network namespaces and the IP_FREEBIND option. (2) Again, not entirely true, you have the SO_REUSEPORT socket option.

So, in this case you have several options:

  1. Set different ports for each node to be started locally, for example, using environment variables
  2. Listen in different IPs for each node during development, for example, 127.0.0.1 and 127.0.0.2
  3. Set the SO_REUSEPORT option in the listening sockets
  4. Use docker or other container/namespace technology to be able to start different nodes in different network namespaces.

Socket options are explained here
In Erlang, it seems that the relatively new low-level socket API has the reuseport option available, whereas, from the inet documentation (gen_tcp), it seems that SO_REUSEPORT is only available as a raw option.

Upvotes: 1

Related Questions