This commit is contained in:
Emin Arslan
2025-02-16 19:14:35 +03:00
commit 1503ca4fab
9 changed files with 194 additions and 0 deletions

19
lib/client.ex Normal file
View File

@@ -0,0 +1,19 @@
defmodule Broadcast.ClientHandler do
use Task
def start(sock) do
Task.start_link(__MODULE__, :handle, [sock])
end
def handle(sock) do
case :gen_tcp.recv(sock, 0) do
{:ok, line} ->
Broadcast.Server.broadcast("Someone said: " <> line)
handle(sock)
{:error, _} ->
:gen_tcp.close(sock)
Broadcast.Server.remove_client()
end
end
end

25
lib/main.ex Normal file
View File

@@ -0,0 +1,25 @@
defmodule Broadcast do
@moduledoc """
A simple broadcast server
"""
use Application
def console_loop() do
case IO.read(:stdio, :line) do
"mem\n" ->
IO.inspect :erlang.memory()
_ -> IO.puts "unknown command entered"
end
console_loop()
end
def main(_args \\ []) do
IO.puts "started"
{:ok, _} = Broadcast.Server.start(8080)
console_loop()
end
def start(_type, _args) do
main()
end
end

65
lib/server.ex Normal file
View File

@@ -0,0 +1,65 @@
defmodule Broadcast.Server do
@moduledoc """
Implements the server. The server spawns a new process
for every client - the processes usually don't really talk to
each other, so this is trivial.
"""
use GenServer
use Agent
def init(port) do
{:ok, sock} = :gen_tcp.listen(port, [:binary, packet: :line, active: false, reuseaddr: true])
spawn_link(fn -> accept_loop(sock) end)
{:ok, {sock, %{}}}
end
defp accept_loop(sock) do
case :gen_tcp.accept(sock) do
{:ok, client_socket} ->
{:ok, pid} = Broadcast.ClientHandler.start(client_socket)
GenServer.cast(__MODULE__, {:new_client, pid, client_socket})
accept_loop(sock)
{:error, _} ->
accept_loop(sock)
end
end
def handle_cast({:new_client, pid, client_sock}, {sock, clients}) do
{:noreply, {sock, clients |> Map.put(pid, client_sock)}}
end
def handle_cast({:remove, client}, {sock, clients}) do
new_clients = Map.delete(clients, client)
{:noreply, {sock, new_clients}}
end
def handle_cast({:broadcast, msg, sender}, {sock, clients}) do
clients |> Enum.map(fn {p, s} ->
if sender != p do
:gen_tcp.send(s, msg)
end
end)
{:noreply, {sock, clients}}
end
# Print out the memory usage for the client processes specifically
# From this point onwards is the actual API for this module,
# that should be called by ClientHandlers and main
@spec start(integer()) :: pid()
def start(port) when is_integer(port) do
GenServer.start_link(__MODULE__, port, name: __MODULE__)
end
def broadcast(msg) do
GenServer.cast(__MODULE__, {:broadcast, msg, self()})
end
def remove_client() do
GenServer.cast(__MODULE__, {:remove, self()})
end
end