75 lines
2.2 KiB
Elixir
75 lines
2.2 KiB
Elixir
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
|