RosettaCodeData/Task/Chat-server/Erlang/chat-server.erl

64 lines
2.0 KiB
Erlang

-module(chat).
-export([start/0, start/1]).
-record(client, {name=none, socket=none}).
start() -> start(8080).
start(Port) ->
register(server, spawn(fun() -> server() end)),
{ok, LSocket} = gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]),
accept(LSocket).
% main loop for message dispatcher
server() -> server([]).
server(Clients) ->
receive
{join, Client=#client{name = Name, socket = Socket}} ->
self() ! {say, Socket, "has joined." ++ [10, 13]},
server(Clients ++ [Client]);
{leave, Socket} ->
{value, #client{name = Name}, List} = lists:keytake(Socket, 3, Clients),
self() ! {say, none, Message = "has left."},
server(List);
{say, Socket, Data} ->
{value, #client{name = From}, List} = lists:keytake(Socket, 3, Clients),
Message = From ++ " : " ++ Data,
lists:map(fun(#client{socket = S}) ->
gen_tcp:send(S, Message)
end, List)
end,
server(Clients).
% accepts connections then spawns the client handler
accept(LSocket) ->
{ok, Socket} = gen_tcp:accept(LSocket),
spawn(fun() -> connecting(Socket) end),
accept(LSocket).
% when client is first connect send prompt for user name
connecting(Socket) ->
gen_tcp:send(Socket, "What is your name? "),
case listen(Socket) of
{ok, N} ->
Name = binary_to_list(N),
server ! {join, #client{name = lists:sublist(Name, 1, length(Name) - 2), socket = Socket} },
client(Socket);
_ -> ok
end.
% main client loop that listens for data
client(Socket) ->
case listen(Socket) of
{ok, Data} ->
server ! {say, Socket, binary_to_list(Data)},
client(Socket);
_ -> server ! {leave, Socket}
end.
% utility function that listens for data on a socket
listen(Socket) ->
case gen_tcp:recv(Socket, 0) of
Response -> Response
end.