1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
-module(server).
-export([server/2, manager/1, broadcast/2, broadcast/3, worker/2, create_workers/2]).
server(Port, NumWorkers) ->
process_flag(trap_exit, true),
{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,
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),
supervisor(ServerSock, [NewPid | Pids]);
{'EXIT', From, Reason} ->
io:format("~p exited due to ~p\n", [From, Reason]),
self() ! create,
supervisor(ServerSock, lists:delete(From, Pids))
end.
ping_all([Pid|Pids], NewPid) ->
Pid ! { ping, NewPid },
ping_all(Pids, NewPid);
ping_all([], _) -> ok.
worker(ServerSock, Pids) ->
process_flag(trap_exit, false),
ManagerPid = self(),
spawn_link(
fun() ->
process_flag(trap_exit, false),
listener(ManagerPid, ServerSock)
end),
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]);
{repeat, Data} ->
broadcast(Socks, Data, norepeat),
manager(Socks);
Other ->
exit(Other)
end.
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).
|