Powered by AppSignal & Oban Pro

Local Multicast

livebooks/local-multicast.livemd

Local Multicast

Mix.install([], extra_applications: [:logger])

Section

defmodule Multicasting.BroadcasterReceiver do
  require Logger
  use GenServer

  @port (case Mix.env() do
           :test -> 49_002
           _ -> 49_001
         end)

  @active 1
  @multicast_group_ip {239, 2, 3, 4}
  @udp_options [
    :binary,
    active: @active,
    add_membership: {@multicast_group_ip, {0, 0, 0, 0}},
    multicast_loop: true
  ]

  def start_link(options \\ []) do
    GenServer.start_link(__MODULE__, {}, options)
  end

  def init(_opts) do
    {:ok, socket} = :gen_udp.open(@port, @udp_options)
    send(self(), :broadcast)
    {:ok, %{socket: socket}}
  end

  @broadcast_interval 15_000
  @message_prefix "multitastic1"

  def handle_info(:broadcast, %{socket: socket} = state) do
    Process.send_after(self(), :broadcast, @broadcast_interval)
    :ok = :gen_udp.send(socket, @multicast_group_ip, @port, "#{@message_prefix}#{hostname()}")
    {:noreply, state}
  end

  def handle_info({:udp, _port, ip, _port_number, @message_prefix <> hostname}, state) do
    Logger.info("Broadcast received from #{hostname} 1 on #{format_ip(ip)}")
    {:noreply, state}
  end

  def handle_info({:udp, _port, ip, _port_number, message}, state) do
    Logger.info("Unknown broadcast received from #{format_ip(ip)}: #{message}")
    {:noreply, state}
  end

  def handle_info({:udp_passive, _}, %{socket: socket} = state) do
    :inet.setopts(socket, active: @active)
    {:noreply, state}
  end

  defp format_ip(ip_tuple) do
    ip_tuple |> Tuple.to_list() |> Enum.join(".")
  end

  defp hostname do
    {:ok, name} = :inet.gethostname()
    List.to_string(name)
  end
end
{:ok, pid} = Multicasting.BroadcasterReceiver.start_link()