defmodule ChatServer do use GenServer require Logger def start_link(_) do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) end def init(:ok) do {:ok, %{clients: %{}}} end # Run in main proccess def start_listening(port \\ 0) do Logger.info("Starting chat server on port #{port}") {:ok, socket} = :gen_tcp.listen(port, [:binary, packet: :line, active: false, reuseaddr: true]) # Add this line {:ok, port} = :inet.port(socket) {:ok, _} = Task.start(fn -> loop_acceptor(socket) end) {:ok, port} end # New clients # infinity loop for accept new clients defp loop_acceptor(socket) do {:ok, client_socket} = :gen_tcp.accept(socket) Logger.info("New client connected: #{inspect(client_socket)}") {:ok, client_pid} = ChatClient.start_link(client_socket) :gen_tcp.controlling_process(client_socket, client_pid) add_client(client_pid, client_socket) loop_acceptor(socket) end def add_client(client_pid, client_socket) do Logger.info("Add client: #{inspect(client_socket)}") GenServer.cast(__MODULE__, {:add_client, client_pid, client_socket}) end def handle_cast({:add_client, client_pid, client_socket}, %{clients: clients} = state) do Logger.info("Registering client: #{inspect(client_pid)}") new_clients = Map.put(clients, client_pid, {client_pid, client_socket}) {:noreply, %{state | clients: new_clients}} end # remove client def handle_cast({:remove_client, client_pid}, %{clients: clients} = state) do Logger.info("Unregistering client: #{inspect(client_pid)}") new_clients = Map.delete(clients, client_pid) {:noreply, %{state | clients: new_clients}} end def remove_client(client_pid) do Logger.info("Remove client") GenServer.cast(__MODULE__, {:remove_client, client_pid}) end # broadcast def broadcast(message) do clients = GenServer.call(__MODULE__, :get_clients) Enum.each(clients, fn {client_pid, _client_socket} -> send(client_pid, {:send_data, message}) end) end # get clients def handle_call(:get_clients, _from, state) do Logger.info("Get clients") {:reply, Map.values(state.clients), state} end end