chat/lib/chat_server.ex

75 lines
2.2 KiB
Elixir
Raw Permalink Normal View History

2023-04-17 19:49:46 +00:00
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