Task

Write a server for a minimal text based chat.

People should be able to connect via ‘telnet’, sign on with a nickname, and type messages which will then be seen by all other connected users. Arrivals and departures of chat members should generate appropriate notification messages.




 
-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.
 