Erlang - использование списков, когда размер постоянно меняется со временем
Я новичок в Erlang, и я учусь, создавая действительно небольшую программу чата на чистом Erlang.
Я хотел бы, чтобы клиенты могли подключаться к серверу, а затем отправлять сообщения друг другу. Но все это делается на локальной машине, а не по сети только для обучения.
У меня есть список всех клиентов, которые подключились к серверу.
Если клиент A отправляет сообщение клиенту B, я получаю желаемый вывод в терминале клиента A, но не могу понять, как получить сообщение от клиента A, которое будет отображаться на терминале клиента B.
Или я должен установить каждый клиент с его собственным мини-сервером
-module(server).
-export([start/0]).
-export([server/1]).
-export([connect/0]).
-export([sendMessage/2]).
%%
%% The Server
%%
start() ->
EmptyList = [],
Pid = spawn(server, server, [EmptyList]),
register(chatServe, Pid).
server(ListOfClients) ->
receive
{Client, connect} ->
Client ! {chatServe, connected},
List = clientList(ListOfClients, Client),
server(List);
{Client, message, MessageBody} ->
List = ListOfClients,
lists:foreach(fun(X) -> X ! {chatServe, new_message, MessageBody} end, List),
Client ! {chatServe, received},
server(List)
end.
%%
%% The client will call rpc:call(server@local, server, connect, [])
%% to connect
%%
connect() ->
chatServe ! {self(), connect},
receive
{chatServe, connected} -> connected
end.
%%
%% The send message method takes two args
%%
%%
sendMessage(SendTo, MessageBody) ->
chatServe ! {self(), message, MessageBody},
receive
{chatServe, received} -> received
end.
receiveMessage(SendTo, SendFrom, MessageBody) ->
receive
{}
end.
%%
%% Some helper functions
%%
clientList(List, Client) when length(List) =:= 0 ->
io:format("List Size = 1~n"),
[Client];
clientList(List, Client) ->
io:format("List size = ~p~n", [length(List) + 1]),
[Client | List].
forwardMessage(SendTo, SentFrom, MessageBody, [H | T]) when H =:= SendTo ->
SendTo ! {SentFrom, message, MessageBody};
forwardMessage(SendTo, SentFrom, MessageBody, [H | T]) ->
forwardMessage(SendTo, SentFrom, MessageBody, T);
forwardMessage(SendTo, SentFrom, MessageBody, []) -> [].
В терминале клиента я вызываю rpc:call(host, mod, function, args).
Таким образом, мой вопрос заключается в том, как я могу заставить клиента A отправить сообщение клиенту B через сервер C, при этом клиент A отображает успех, а клиент B отображает сообщение, которое было отправлено?
заранее спасибо
1 ответ
В этом примере мой сервер является узлом Erlang, и каждый клиент также является узлом Erlang.
Код:
-module(test).
-export([server_start/1, client_start/2]).
-export([server_new_message/2, client_new_message/2]).
server_start(ServerName) ->
{ok, _Pid} = net_kernel:start([ServerName, shortnames]),
erlang:register(server, erlang:self()),
io:format("Server '~p' started.~nMessages: ~n ~n", [erlang:node()]),
server_loop().
server_loop() ->
receive
{msg, Name, Text} ->
io:format("~p: ~p~n", [Name, Text]),
Receivers = lists:delete(Name, erlang:nodes()),
rpc:multicall(Receivers, ?MODULE, client_new_message, [Name, Text]),
server_loop()
end.
%% Server runs this function in client's node.
%% 'client' process in client's node will receive this message and print it
client_new_message(Name, Text) ->
client ! {msg, Name, Text}.
client_start(ServerName, ClientName) ->
{ok, _Pid} = net_kernel:start([ClientName, shortnames]),
pong = net_adm:ping(ServerName),
timer:sleep(1000), % wait for updating erlang:nodes()
Other = lists:delete(ServerName, erlang:nodes()),
io:format("Client '~p' connected to server '~p'.~nOnline users: ~p~n", [erlang:node(), ServerName, Other]),
erlang:register(client, spawn_link(fun print/0)),
client_loop(ServerName).
print() ->
receive
{msg, Name, Text} ->
io:format("~p: ~p~n", [Name, Text]),
print()
end.
client_loop(ServerName) ->
rpc:call(ServerName, ?MODULE, server_new_message, [erlang:node(), io:get_line(">>> ")]),
client_loop(ServerName).
%% Clients run this function in server's node
%% 'server' process will receive messages and print them and broadcast them
server_new_message(Name, Text) ->
server ! {msg, Name, Text}.
Бежать:
Я открываю 3 снаряда Эрланга. В оболочке 1 я запускаю сервер:
p@jahanbakhsh ~/Desktop $ erl
Erlang/OTP 19 [erts-8.2.2] [source-1ca84a4] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2.2 (abort with ^G)
1> test:server_start(local_chat_server).
Server 'local_chat_server@jahanbakhsh' started.
Messages:
Сервер ждет сообщений.
В оболочке 2 я запускаю клиент 1:
p@jahanbakhsh ~/Desktop $ erl
Erlang/OTP 19 [erts-8.2.2] [source-1ca84a4] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2.2 (abort with ^G)
1> test:client_start('local_chat_server@jahanbakhsh', client_1).
Client 'client_1@jahanbakhsh' connected to server 'local_chat_server@jahanbakhsh'.
Online users: []
>>>
Теперь я могу отправить сообщение с этого терминала, но подождите. Я запускаю клиент 2 в оболочке 3:
p@jahanbakhsh ~/Desktop $ erl
Erlang/OTP 19 [erts-8.2.2] [source-1ca84a4] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2.2 (abort with ^G)
1> test:client_start('local_chat_server@jahanbakhsh', client_2).
Client 'client_2@jahanbakhsh' connected to server 'local_chat_server@jahanbakhsh'.
Online users: [client_1@jahanbakhsh]
>>>
Я отправляю сообщение ("Тестовое сообщение") из оболочки 2 или клиента 1.
В оболочке 1 или на сервере у меня есть:
client_1@jahanbakhsh: "Test message\n" - sent to [client_2@jahanbakhsh]
В оболочке 3 или клиент 2 у меня есть:
client_1@jahanbakhsh: "Test message\n"
>>>