Initial
This commit is contained in:
19
lib/client.ex
Normal file
19
lib/client.ex
Normal 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
25
lib/main.ex
Normal 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
65
lib/server.ex
Normal 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
|
Reference in New Issue
Block a user