All pastes #1536576 Raw Edit

Miscellany

public text v1 · immutable
#1536576 ·published 2009-08-20 16:40 UTC
rendered paste body
-module(chat).
-author('Josiah Worcester <josiahw@gmail.com>').
-export([listen/1]).

-define(TCP_OPTIONS, [list, {packet, 0}, {active, false}, {reuseaddr, true}]).

listen(Port) ->
    {ok, LSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS),
    Server = spawn(fun () -> server([]) end),
    accept(LSocket, Server).

accept(LSocket, Server) ->
    {ok, Socket} = gen_tcp:accept(LSocket),
    spawn(fun () -> client(Socket, Server) end),
    accept(LSocket, Server).

client(Socket, Server) ->
    gen_tcp:send(Socket, "Your name?\r\n"),
    {ok, NameB} = gen_tcp:recv(Socket, 0),
    Name = NameB -- "\r\n",
    case Name of
	"SERVER" ->
	    gen_tcp:send(Socket, "Invalid name.\r\n"),
	    client(Socket,Server);
	_ ->
	    Messenger = spawn(fun () -> messageLoop(Socket) end),
	    Messenger ! {message, ("Hello, " ++ Name ++ "\r\n")},
	    Client = {Messenger, Name},
	    Server ! {newclient, Client},
	    tcpLoop(Socket, Server, Client)
    end.

messageLoop(Socket) ->
    receive
	{message, Message} ->
	    gen_tcp:send(Socket,Message),
	    messageLoop(Socket)
    end.

tcpLoop(Socket, Server, Messenger) ->
    case gen_tcp:recv(Socket, 0) of
	{ok, Data} ->
	    case Data of
		"/disconnect\r\n" ->
		    disconnect(Socket, Server, Messenger);
		_ ->
		    Server ! {broadcast, Data, Messenger},
		    tcpLoop(Socket, Server, Messenger)
	    end;
	{error, closed} ->
	    disconnect(Socket, Server, Messenger)
    end.

disconnect(Socket, Server, Messenger) ->
    Server ! {disconnect, Messenger},
    {MessengerPID, _} = Messenger,
    exit(MessengerPID, kill),
    gen_tcp:close(Socket).

server(Clients) ->
    receive
	{newclient, Client} ->
	    NewClients = [Client | Clients],
	    serverMessage(NewClients, Client, "has joined.\r\n"),
	    server(NewClients);
	{disconnect, Client} ->
	    NewClients = lists:delete(Client, Clients),
	    serverMessage(NewClients, Client, "has disconnected.\r\n"),
	    server(NewClients);
	{broadcast, Message, Source} ->
	    broadcast(Clients, Source, Message),
	    server(Clients)
    end.

serverMessage(Clients, Client, Message) ->
    Server = {{}, "SERVER"},
    {_, Name} = Client,
    broadcast(Clients, Server, "\"" ++ Name ++ "\" " ++ Message).

broadcast(Clients, Source, Message) ->
    {SourcePID, Name} = Source,
    Output = lists:append([Name, ": ", Message ]),
    io:fwrite(Output),
    lists:foreach(fun ({ClientPID, _}) when ClientPID /= SourcePID ->
			  ClientPID ! {message, Output};
		      (_) -> ok
		  end, Clients).