defmodule Bbox.Server do
use GenServer
require Logger
alias Bbox.Peers
def start_link(state) do
GenServer.start_link(__MODULE__, Keyword.put(state, :peers, []), name: __MODULE__)
end
@impl true
def init(state) do
port = Keyword.fetch!(state, :port)
Process.flag(:trap_exit, true)
{:ok, socket} = :gen_tcp.listen(port, [:binary, packet: :line, active: true, reuseaddr: true])
address = "127.0.0.1:#{port}"
Peers.insert(%{address: address})
Logger.info("Listening on #{address}")
send(self(), {:accept, socket})
{:ok, state}
end
@impl true
def handle_info({:accept, socket}, state) do
{:ok, peer} = :gen_tcp.accept(socket)
:gen_tcp.send(peer, "Welcome to Bbox\n")
peers =
state
|> Keyword.fetch!(:peers)
|> Enum.concat([peer])
send(self(), {:accept, socket})
{:noreply, Keyword.put(state, :peers, peers)}
end
@impl true
def handle_info({:tcp, _socket, packet}, state) do
state
|> Keyword.fetch!(:peers)
|> Enum.each(fn peer ->
:gen_tcp.send(peer, packet)
end)
{:noreply, state}
end
@impl true
def handle_info({:tcp_closed, _}, state), do: {:stop, :normal, state}
@impl true
def handle_info({:tcp_error, _}, state), do: {:stop, :normal, state}
@impl true
def handle_call(:peers, _from, state), do: {:reply, Keyword.fetch!(state, :peers), state}
def peers() do
GenServer.call(__MODULE__, :peers)
end
end