82 lines
2.5 KiB
Prolog
82 lines
2.5 KiB
Prolog
:- initialization chat_server(5000).
|
|
|
|
chat_server(Port) :-
|
|
tcp_socket(Socket),
|
|
tcp_bind(Socket, Port),
|
|
tcp_listen(Socket, 5),
|
|
tcp_open_socket(Socket, AcceptFd, _),
|
|
dispatch(AcceptFd).
|
|
|
|
dispatch(AcceptFd) :-
|
|
tcp_accept(AcceptFd, Socket, _),
|
|
thread_create(process_client(Socket, _), _, [detached(true)]),
|
|
dispatch(AcceptFd).
|
|
|
|
process_client(Socket, _) :-
|
|
setup_call_cleanup(
|
|
tcp_open_socket(Socket, Str),
|
|
handle_connection(Str),
|
|
close(Str)).
|
|
|
|
% a connection was made, get the username and add the streams so the
|
|
% client can be broadcast to.
|
|
handle_connection(Str) :-
|
|
send_msg(Str, msg_welcome, []),
|
|
repeat,
|
|
send_msg(Str, msg_username, []),
|
|
read_line_to_string(Str, Name),
|
|
connect_user(Name, Str), !.
|
|
|
|
% connections are stored here
|
|
:- dynamic(connected/2).
|
|
|
|
connect_user(Name, Str) :-
|
|
connected(Name, _),
|
|
send_msg(Str, msg_username_taken, []),
|
|
fail.
|
|
connect_user(Name, Str) :-
|
|
\+ connected(Name, _),
|
|
send_msg(Str, msg_welcome_name, Name),
|
|
|
|
% make sure that the connection is removed when the client leaves.
|
|
setup_call_cleanup(
|
|
assert(connected(Name, Str)),
|
|
(
|
|
broadcast(Name, msg_joined, Name),
|
|
chat_loop(Name, Str), !,
|
|
broadcast(Name, msg_left, Name)
|
|
),
|
|
retractall(connected(Name, _))
|
|
).
|
|
|
|
% wait for a line to be sent then broadcast to the rest of the clients
|
|
% finish this goal when the client disconnects (end of stream)
|
|
chat_loop(Name, Str) :-
|
|
read_line_to_string(Str, S),
|
|
dif(S, end_of_file),
|
|
broadcast(Name, msg_by_user, [Name, S]),
|
|
chat_loop(Name, Str).
|
|
chat_loop(_, Str) :- at_end_of_stream(Str).
|
|
|
|
% send a message to all connected clients except Name (the sender)
|
|
broadcast(Name, Msg, Params) :-
|
|
forall(
|
|
(connected(N, Str), dif(N, Name)),
|
|
(send_msg(Str, Msg, Params), send_msg(Str, msg_new_line, []))
|
|
).
|
|
|
|
send_msg(St, MsgConst, Params) :-
|
|
call(MsgConst, Msg),
|
|
format(St, Msg, Params),
|
|
flush_output(St).
|
|
|
|
% constants for the various message types that are sent
|
|
msg_welcome('Welcome to Chatalot\n\r').
|
|
msg_username('Please enter your nickname: ').
|
|
msg_welcome_name('Welcome ~p\n\r').
|
|
msg_joined(' -- "~w" has joined the chat --').
|
|
msg_left(' -- "~w" has left the chat. --').
|
|
msg_username_taken('That username is already taken, choose another\n\r').
|
|
msg_new_line('\n\r').
|
|
msg_by_user('~w> ~w').
|