Erlang:如何在邮件系统中跟踪发件人?

Sid*_*Sid 2 erlang messaging pid

我是一个erlang新手,我正在尝试构建我的第一个消息传递应用程序.我的问题有2个部分

当我在终端上打开两个单独的erlang控制台而不指定-sname属性时,两个erlang控制台如何具有相同的进程pid.它们实际上是同一个过程,怎么样?


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


chat_client_sender模块


-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_sender在我的loop方法中的子句失败,该方法存储从中到达的SenderPid chat_client.

这是一个例子


 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}]}]} 

根据我的理解,它应该继续{ok, {Uid, Nickname}}chat_client_sender模块中的子句上进行尾递归.


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

我真的很感激一些帮助,了解这里发生了什么.如果你能查看我的代码并告诉我更多关于最佳实践以及我可以做得更好的事情,我也非常感激.我的代码可以在goo.gl/yY4kR上找到

谢谢

Tha*_*dis 6

第一个问题:

PID的第一部分表示erlang节点,其中进程以0运行,这意味着进程在本地节点中运行.现在,你得到的PID是shell的PID,它只是erlang进程; 例如,它可以死(并自动重生):

1> self().
<0.32.0>
2> [X] = [4,2].
** exception error: no match of right hand side value [4,2]
3> self().     
<0.35.0>
Run Code Online (Sandbox Code Playgroud)

第二个问题:

错误位于此处(顺便找到有问题的代码的最小部分是非常有用的,不仅是您自己进行调试,而且当您发布问题时 - 通常更多人尝试在您需要的代码时提供帮助研究很小.据我所知,SO并不专注于代码审查):

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
    end.
Run Code Online (Sandbox Code Playgroud)

因此,loop/1接收一种形式的消息,{add_sender, SPID, {UID, Nick}}
当它接收到这样的消息时,它在字典上搜索具有作为键的值SPID 的记录.字典error以表格或表格的形式响应{ok, {UID2, Nick2}}; 你case用来分开那些.
但是,当您尝试匹配时{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.
Run Code Online (Sandbox Code Playgroud)

顺便说一下,既然你从不使用UidNickname单独使用,你可以{Uid,Nickname}用变量替换tupple :

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.
Run Code Online (Sandbox Code Playgroud)

好吧,它不是完全等价的,因为如果字典返回一个类似的值,第一个版本将失败,{ok, {a,b,c}}但我真的没有看到这个检查的重点.

此外,它更好(至少在语义上)使用,dict:is_key/2因为您只想检查新元素是否已经存在.它也可以更快,具体取决于实现,因为它只需要查看密钥是否存在而不是检索值; 但是,我没有测试过,所以也许没有这样的优化.此外,它可能对小规模项目没有任何影响.

但请注意,有一种特殊情况:如果您尝试添加具有相同PID和相同值的人(基本上重新注册同一用户),该怎么办?当前的实现仍然给出相同的错误.根据您的规格,您可能希望更改它.

玩得开心!