Erlang:如何在消息传递系统中跟踪发件人?
我是一个erlang新手,我正在尝试构建我的第一个消息传递应用程序。我的问题有两部分 当我在终端上打开两个单独的erlang控制台而不指定-sname属性时,两个erlang控制台如何具有相同的进程PID。它们实际上是同一个过程吗?如何Erlang:如何在消息传递系统中跟踪发件人?,erlang,messaging,pid,Erlang,Messaging,Pid,我是一个erlang新手,我正在尝试构建我的第一个消息传递应用程序。我的问题有两部分 当我在终端上打开两个单独的erlang控制台而不指定-sname属性时,两个erlang控制台如何具有相同的进程PID。它们实际上是同一个过程吗?如何 Terminal #1 -------------- $> erl Erlang R15B01 (erts-5.9.1) [source] [smp:4:4] [async-threads:0] [hipe] [kernel-poll:false] E
Terminal #1
--------------
$> erl
Erlang R15B01 (erts-5.9.1) [source] [smp:4:4] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9.1 (abort with ^G)
1> self().
0.31.0
2号航站楼也是如此
Erlang R15B01 (erts-5.9.1) [source] [smp:4:4] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9.1 (abort with ^G)
1> self().
0.31.0
如何在同一进程上同时运行这两个进程。这些是erlang进程而不是本机进程吗
如果我正在构建一个消息传递应用程序,并且我需要某种方法来跟踪每个用户发送的消息的信息。我尝试使用的基本设计是为每个用户创建一个新的聊天客户端。我生成了一个进程来跟踪接收消息的人,从而将他的昵称存储在那里
类似地,我创建了另一个用于跟踪谁发送消息的模块,但在本例中,我使用self()Pid跟踪发送者。这在chat\u client\u sender
模块中进行
-module(chat_client).
-export([start/0, stop/0, loop/1, login/3, logout/1, send_message/2]).
-define(SERVER, chat_client).
start() ->
message_router:start(),
chat_client_sender:start().
stop() ->
message_router:stop(),
chat_client_sender:stop().
login(Uid, Password, Nickname) ->
io:format("~p My Pid", [self()]),
Pid = spawn(chat_client, loop, [Nickname]),
case message_router:login(Uid, Password, Nickname, Pid) of
{ok, logged_in} ->
chat_client_sender:add_sender(self(), {Uid, Nickname}),
{ok, logged_in};
{error, invalid_uid_or_pwd} ->
{error, invalid}
end.
logout(Uid) ->
case message_router:logout(Uid) of
{ok, logged_out} ->
{ok, logged_out};
ignored ->
ignored;
_Someshit ->
io:format("Some Shit ~p", _Someshit)
end.
send_message(ToUid, MessageBody) ->
chat_client_sender:send_message(ToUid, MessageBody).
loop(Nickname) ->
receive
{print_msg, Messagebody, SenderNickname} ->
io:format("~p: ~p to ~p~n", [SenderNickname, Messagebody, Nickname]),
loop(Nickname);
stop ->
ok
end.
...
receive
{add_sender, SenderPid, {Uid, Nickname}} ->
case dict:find(SenderPid, MemberPids) of
{ok, {Uid, Nickname}} ->
io:format("Pid exists ~n"),
loop(MemberPids);
error ->
loop(dict:store(SenderPid, {Uid, Nickname}, MemberPids))
end;
...
聊天室客户端发送模块
-module(chat_client_sender).
-export([start/0, stop/0, add_sender/2, loop/1, get_sender/1, send_message/2]).
-define(SERVER, chat_client_sender).
start() ->
erlang:register(?SERVER, spawn(chat_client_sender, loop, [dict:new()])).
stop() ->
?SERVER ! stop.
add_sender(SenderPid, {Uid, Nickname}) ->
io:format("Adding Sender ~p ~p ~p ~n", [SenderPid, Uid, Nickname]),
?SERVER ! {add_sender, SenderPid, {Uid, Nickname}}.
get_sender(SenderPid) ->
?SERVER ! {get_sender, SenderPid}.
send_message(ToUid, MessageBody) ->
?SERVER ! {send_msg, ToUid, MessageBody}.
loop(MemberPids) ->
receive
{add_sender, SenderPid, {Uid, Nickname}} ->
case dict:find(SenderPid, MemberPids) of
{ok, {Uid, Nickname}} ->
io:format("Pid exists ~n"),
loop(MemberPids);
error ->
loop(dict:store(SenderPid, {Uid, Nickname}, MemberPids))
end;
{send_msg, ToUid, MessageBody, SenderPid} ->
case get_sender(SenderPid, MemberPids) of
{found, _Uid, Nickname} ->
message_router:send_message(ToUid, MessageBody, Nickname),
loop(MemberPids);
not_found ->
not_found,
loop(MemberPids)
end;
{remove_sender, SenderPid} ->
case get_sender(SenderPid, MemberPids) of
{found, _Uid, _Nickname} ->
loop(dict:erase(SenderPid, MemberPids));
not_found ->
ignored,
loop(MemberPids)
end;
{get_sender, SenderPid} ->
case get_sender(SenderPid, MemberPids) of
Any ->
io:format("GET SENDER ~p~n", [Any])
end,
loop(MemberPids);
stop ->
ok
end.
get_sender(SenderPid, MemberPids) ->
case dict:find(SenderPid, MemberPids) of
{ok, {Uid, Nickname}} ->
{found, {Uid, Nickname}};
error ->
not_found
end.
因此,我的应用程序在我的循环
方法中的add\u sender
子句开始失败,该方法存储从聊天室客户端
到达的SenderPid
这里有一个例子
chat_client:start().
true
9> chat_client_sender:add_sender(self(), {'sid@abc.com', 'sid'}).
Adding Sender 'sid@abc.com' sid
{add_sender,,{'sid@abc.com',sid}}
10> chat_client_sender:add_sender(self(), {'sid1@abc.com', 'sid1'}).
Adding Sender 'sid1@abc.com' sid1
{add_sender,,{'sid1@abc.com',sid1}}
11>
=ERROR REPORT==== 13-Oct-2012::19:12:42 ===
Error in process with exit value: {{case_clause,{ok,{'sid@abc.com',sid}}},[{chat_client_sender,loop,1,[{file,"chat_client_sender.erl"},{line,25}]}]}
根据我的理解,它应该在chat\u client\u sender
模块中的{ok,{Uid,昵称}}
子句上继续尾部递归
-module(chat_client).
-export([start/0, stop/0, loop/1, login/3, logout/1, send_message/2]).
-define(SERVER, chat_client).
start() ->
message_router:start(),
chat_client_sender:start().
stop() ->
message_router:stop(),
chat_client_sender:stop().
login(Uid, Password, Nickname) ->
io:format("~p My Pid", [self()]),
Pid = spawn(chat_client, loop, [Nickname]),
case message_router:login(Uid, Password, Nickname, Pid) of
{ok, logged_in} ->
chat_client_sender:add_sender(self(), {Uid, Nickname}),
{ok, logged_in};
{error, invalid_uid_or_pwd} ->
{error, invalid}
end.
logout(Uid) ->
case message_router:logout(Uid) of
{ok, logged_out} ->
{ok, logged_out};
ignored ->
ignored;
_Someshit ->
io:format("Some Shit ~p", _Someshit)
end.
send_message(ToUid, MessageBody) ->
chat_client_sender:send_message(ToUid, MessageBody).
loop(Nickname) ->
receive
{print_msg, Messagebody, SenderNickname} ->
io:format("~p: ~p to ~p~n", [SenderNickname, Messagebody, Nickname]),
loop(Nickname);
stop ->
ok
end.
...
receive
{add_sender, SenderPid, {Uid, Nickname}} ->
case dict:find(SenderPid, MemberPids) of
{ok, {Uid, Nickname}} ->
io:format("Pid exists ~n"),
loop(MemberPids);
error ->
loop(dict:store(SenderPid, {Uid, Nickname}, MemberPids))
end;
...
我真的很感激你能帮助我理解这里发生了什么。另外,如果您能查看我的代码并告诉我更多关于最佳实践和我可以做得更好的事情,我将非常感激。我的代码可在
谢谢第一个问题:
PID的第一部分表示erlang节点,进程在其中以0运行,这意味着进程正在本地节点中运行。现在,您得到的PID是shell的PID,它只是erlang进程;例如,它可以死亡(并自动重生):
因此,loop/1
接收一种消息,形式为{add\u sender,SPID,{UID,Nick}}
当它收到这样一条消息时,它会在字典中搜索一条键值为
SPID
字典会以错误
或{ok,{UID2,Nick2}}
的形式响应;您可以使用大小写
将它们分开。但是,当您尝试匹配
{ok,{UID2,Nick2}}
时,您不会使用新变量;您使用旧的、已经实例化的变量{UID,Nick}
,因此,如果这些变量不同,它将失败(因为没有大小写匹配的子句)
那你能做什么呢?因为您不使用它们,所以可以用下划线替换它们:
loop(MemberPids) ->
receive
{add_sender, SenderPid, {Uid, Nickname}} ->
case dict:find(SenderPid, MemberPids) of
{ok, {_, _}} ->
io:format("Pid exists ~n"),
loop(MemberPids);
error ->
loop(dict:store(SenderPid, {Uid, Nickname}, MemberPids))
end
end.
顺便说一句,由于您从不单独使用Uid
或昵称
,因此只需用变量替换tuple{Uid,昵称}
:
loop(MemberPids) ->
receive
{add_sender, SenderPid, Value} ->
case dict:find(SenderPid, MemberPids) of
{ok, _} ->
io:format("Pid exists ~n"),
loop(MemberPids);
error ->
loop(dict:store(SenderPid, Value, MemberPids))
end
end.
嗯,它不是完全等价的,因为如果字典返回一个像{ok,{a,b,c}}
这样的值,第一个版本就会失败,但是我真的不明白这个检查的意义
此外,最好(至少在语义上)使用dict:is_key/2
,因为您只想检查新元素是否已经存在。它也可以更快,这取决于实现,因为它只需要查看键是否存在,而不需要检索值;但是,我还没有测试过它,所以可能没有这样的优化。此外,对于一个小规模的项目来说,这可能没有什么区别
但请注意,有一种特殊情况:如果您尝试添加具有相同PID和相同值的用户(本质上是重新注册相同的用户),该怎么办。当前的实现仍然给出相同的错误。根据您的规格,您可能希望对此进行更改
玩得开心 回答第一个问题:erlang进程不是本机进程,而是“内部”进程。因此,当运行两个erlang节点时,每个节点都有自己的一组进程。在这两种情况下,调用
self()
的shell进程具有相同的pid(进程标识符)
没有隐含或内置的方式来确定谁发送了消息。标准方法是按照示例中的方法,显式地让消息包含发送者。我们发现这是最好的方法。谢谢。你的答案和投入是无价的。