diff options
| author | kartofen <mladenovnasko0@gmail.com> | 2024-02-05 23:18:00 +0200 | 
|---|---|---|
| committer | kartofen <mladenovnasko0@gmail.com> | 2024-02-05 23:18:00 +0200 | 
| commit | 13a086028ea66d26fbf390af70894cb9cd03aa23 (patch) | |
| tree | 9cc7b4b4a5b20588c6cec6f9e842ed7adea28851 | |
| parent | 3037bf26dc938f56c5ef63ddf04027a1f3f629c6 (diff) | |
updated
| -rw-r--r-- | server.erl | 139 | 
1 files changed, 99 insertions, 40 deletions
| @@ -1,82 +1,141 @@  -module(server). --export([server/2, manager/1, broadcast/2, broadcast/3, worker/2, create_workers/2]). +-export([server/0, server/2, worker/2, create_workers/2, acceptor/2, listener/1, manager/1, broadcast/2, broadcast/3, handle_data/3]). +server() -> +    server(1235, 10).  server(Port, NumWorkers) -> -    process_flag(trap_exit, true), +    ets:new(client_table, [named_table, public, {read_concurrency, true}]), +    ets:insert(client_table, {counter, 0}), +    ets:insert(client_table, {server, "Server"}), +      {ok, ServerSock} = gen_tcp:listen(Port, [list]),      spawn(?MODULE, create_workers, [NumWorkers, self()]),      supervisor(ServerSock, []).  create_workers(0, _) -> ok;  create_workers(Num, Supervisor) -> -    Supervisor ! create, +    Supervisor ! worker_new,      create_workers(Num - 1, Supervisor).  supervisor(ServerSock, Pids) ->      receive -        create -> -            io:format("Creating new worker\n"), -            NewPid = spawn_link(?MODULE, worker, [ServerSock, Pids]), -            ping_all(Pids, NewPid), +        worker_new -> +            {NewPid, _} = spawn_monitor(?MODULE, worker, [ServerSock, Pids]), +            io:format("Creating new worker ~p\n", [NewPid]), + +            message_all(Pids, {ping, NewPid}),              supervisor(ServerSock, [NewPid | Pids]); -        {'EXIT', From, Reason} -> -            io:format("~p exited due to ~p\n", [From, Reason]), + +        {'DOWN', _, process, From, Reason} -> +            io:format("Worker ~p exited due to ~p\n", [From, Reason]),              self() ! create, -            supervisor(ServerSock, lists:delete(From, Pids)) + +            message_all(lists:delete(From, Pids), {close, From}), +            supervisor(ServerSock, lists:delete(From, Pids)); +        {announce, Data} -> +            [Pid|_] = Pids, +            Pid ! {broadcast, "Server", ok, Data}, +            supervisor(ServerSock, Pids)      end. -ping_all([Pid|Pids], NewPid) -> -    Pid ! { ping, NewPid }, -    ping_all(Pids, NewPid); -ping_all([], _) -> ok. +message_all([Pid|Pids], Data) -> +    Pid ! Data, +    message_all(Pids, Data); +message_all([], _) -> ok.  worker(ServerSock, Pids) ->      process_flag(trap_exit, false), -    ManagerPid = self(), -    spawn_link( -        fun() -> -           process_flag(trap_exit, false), -           listener(ManagerPid, ServerSock) -        end), +    ListenerPid = spawn_link(?MODULE, listener, [self()]), +    spawn_link(?MODULE, acceptor, [ListenerPid, ServerSock]),      manager(Pids). -listener(ManagerPid, ServerSock) -> -    case gen_tcp:accept(ServerSock) of -        {ok, Sock} -> -            ok = gen_tcp:controlling_process(Sock, ManagerPid), -            ManagerPid ! {ping, Sock}, -            listener(ManagerPid, ServerSock); -        Other -> -            exit(Other) -    end. -  manager(Socks) ->      receive -        {tcp, Sock, Data} -> -            broadcast(lists:delete(Sock, Socks), Data), -            manager(Socks); -        {tcp_closed, Sock} ->  -            manager(lists:delete(Sock, Socks));          {ping, Sock} ->              manager([Sock | Socks]); +        {close, Sock} ->  +            manager(lists:delete(Sock, Socks));          {repeat, Data} ->              broadcast(Socks, Data, norepeat),              manager(Socks); -        Other -> -            exit(Other) +        {broadcast, FromIden, FromSock, Data} -> +            NewData = FromIden ++ ": " ++ Data, +            broadcast(lists:delete(FromSock, Socks), NewData), +            manager(Socks); +        {pm, FromIden, ToSock, Data} -> +            NewData = "(pm) " ++ FromIden ++ ": " ++ Data, +            broadcast([ToSock], NewData), +            manager(Socks) +    end. + +listener(ManagerPid) -> +    receive +        {tcp, Sock, Data} -> +            spawn(?MODULE, handle_data, [ManagerPid, Sock, Data]), +            listener(ManagerPid); +        {tcp_opened, Sock} -> +            ManagerPid ! {ping, Sock}, +            client_add(Sock), +            listener(ManagerPid); +        {tcp_closed, Sock} ->  +            ManagerPid ! {close, Sock}, +            client_del(Sock), +            listener(ManagerPid)      end. +acceptor(ListenerPid, ServerSock) -> +    case gen_tcp:accept(ServerSock) of +        {ok, Sock} -> +            ok = gen_tcp:controlling_process(Sock, ListenerPid), +            ListenerPid ! {tcp_opened, Sock}, +            acceptor(ListenerPid, ServerSock) +    end. + + +handle_data(ManagerPid, From, Data) -> +    case catch parse_data(ManagerPid, From, Data) of +        ok -> ok; +        _ ->  +            ManagerPid ! {pm, "Server", From, "ERR: Transaction Failed\n"} +    end. + +parse_data(ManagerPid, From, Data) -> +    case string:tokens(Data, " ") of +        ["/pm" | [ToIden | _]] -> +            NewData = string:slice(Data, string:length(ToIden) + 5), +            ManagerPid ! {pm, client_iden(From), client_sock(ToIden), NewData}; +        ["\n"] -> ok; +        _ ->  +            ManagerPid ! {broadcast, client_iden(From), From, Data} +    end, ok. +     + +broadcast(Socks, Data) -> +    broadcast(Socks, Data, ok).  broadcast([Pid|Socks], Data, norepeat) when is_pid(Pid) ->      broadcast(Socks, Data, norepeat);  broadcast([Pid|Socks], Data, Opt) when is_pid(Pid) ->      Pid ! {repeat, Data},      broadcast(Socks, Data, Opt); -  broadcast([Sock|Socks], Data, Opt)  ->      gen_tcp:send(Sock, Data),      broadcast(Socks, Data, Opt);  broadcast([], _, _) -> ok. -broadcast(Socks, Data) -> -    broadcast(Socks, Data, ok). +client_add(Sock) -> +    [{_, Count}|_] = ets:lookup(client_table, counter), +    Iden = "Anon"++integer_to_list(Count), +    ets:insert(client_table, {Iden, Sock}), +    ets:insert(client_table, {Sock, Iden}), +    ets:update_counter(client_table, counter, 1). +client_del(Sock) -> +    [{_, Iden}|_] = ets:lookup(client_table, Sock), +    ets:delete(client_table, Sock), +    ets:delete(client_table, Iden). +client_iden(Sock) -> +    [{_, Iden}|_] = ets:lookup(client_table, Sock), +    Iden. +client_sock(Iden) -> +    [{_, Sock}|_] = ets:lookup(client_table, Iden), +    Sock. | 
