Nihal Harish
Nihal Harish

Reputation: 1060

How to send messages across two Erlang Nodes

The code in my two Erlang Modules are as follows:

recevier:

start() ->
    PID = spawn(?MODULE, message_box, []),
    PID.

message_box() ->
    sender:start_link(),
    receive
        {From, take_this_message} ->
            From ! "ok thanks !";
        {From, fish} ->
            From ! "So long and thanks for all the fish";
        _  ->
            io:format("Received Nothing~n")
        end.

sender:

-module(sender).

-compile(export_all).

start_link() ->
    PID = spawn_link(?MODULE, init, [self()]),
    PID.

init(PID) ->
    PID ! {self(), "Wassup!"}.

I want receiver to display Received Nothing after calling start_link(). Also how do I check the messages a module/process has received in runtime using the shell or otherwise?

**ERROR MESSAGE:**  

=ERROR REPORT==== 21-Jul-2014::12:42:44 ===
Error in process <0.40.0> with exit value: {undef,[{sender,start_link,[],[]},{receiver,message_box,0,[{file,"receiver.erl"},{line,14}]}]}

Upvotes: 2

Views: 2439

Answers (1)

Pascal
Pascal

Reputation: 14042

To be able to exchange messages across the 2 nodes, you must first start 2 nodes (erl -sname foo, erl -sname bar) associate them in a cluster. This can be done using the net_adm:ping/1 function: net_adm:ping(foo@domain) on the bar node. should return you pong. (If you get pang, then chek the spelling, check that the 2 nodes have the same cookie), or net_kernel:connect(foo@domain) should return ok.

Then you can launch the receiver process on one node, and get its Pid from the second node for example rpc:call(foo@domain,erlang,processes,[]). will return the list of all processes pid runing on the node foo.

You can then run the send function on the second node, and you will get the returned message in the shell message queue.

This because the send function use self() to give the pid where to return an answer, and it is run in the shell process (the is no new process spawned).

If you spawn your function without change, the return message will be lost, because the process will die immediately after the first message is sent. you need to modify it lije this for example:

-module(sender).

-compile(export_all).

init(PID,Mess) ->
    spawn(?MODULE,send_receive,[PID,Mess]).

send_receive(PID,Mess) ->
    PID ! {self(), Mess},
    R = receive
        M -> {ok,M}
    after 5000 -> {error,"no answer!"}
    end,
    io:format("~p~n",[R]).

-module (receiver).

-compile(export_all).

start() ->
    PID = spawn(?MODULE, message_box, []),
    PID.

message_box() ->
    receive
        {From, take_this_message} ->
            From ! "ok thanks !";
        {From, fish} ->
            From ! "So long and thanks for all the fish";
        _  ->
            io:format("Received Nothing~n")
        end.

in the shells:

(foo@W7FRR00423L)3> receiver:start().
<0.50.0>
Received Nothing    
(foo@W7FRR00423L)4> receiver:start().
<0.53.0>
(foo@W7FRR00423L)5> receiver:start().
<0.55.0>
Received Nothing    
(foo@W7FRR00423L)6> 



(bar@W7FRR00423L)1> net_kernel:connect(foo@W7FRR00423L).
true
(bar@W7FRR00423L)2> nodes().
[foo@W7FRR00423L]
(bar@W7FRR00423L)3> rpc:call(foo@W7FRR00423L,erlang,processes,[]).
[<6075.0.0>,<6075.3.0>,<6075.6.0>,<6075.7.0>,<6075.9.0>,
 <6075.10.0>,<6075.11.0>,<6075.12.0>,<6075.13.0>,<6075.14.0>,
 <6075.15.0>,<6075.16.0>,<6075.17.0>,<6075.18.0>,<6075.19.0>,
 <6075.20.0>,<6075.21.0>,<6075.22.0>,<6075.23.0>,<6075.24.0>,
 <6075.25.0>,<6075.26.0>,<6075.27.0>,<6075.28.0>,<6075.29.0>,
 <6075.30.0>,<6075.31.0>,<6075.32.0>,<6075.33.0>|...]
(bar@W7FRR00423L)4> sender:init(pid(6075,50,0),take_this_message).
<0.55.0>
{error,"no answer!"}
(bar@W7FRR00423L)5> sender:init(pid(6075,53,0),take_this_message).
<0.57.0>
{ok,"ok thanks !"}  
(bar@W7FRR00423L)6> sender:init(pid(6075,55,0),"Wassup!").        
<0.59.0>
{error,"no answer!"}
(bar@W7FRR00423L)7>

[edit]

As said in comment, generally the communication goes through registered processes (this is equally true on a single node) when the 2 processes are created independently, for example you can modify the receiver like this:

start() ->
    PID = spawn(?MODULE, message_box, []),
    register(?MODULE,PID),
    PID.

and then send the message using the syntax {name,node} to send the message: sender:init({receiver,bar@W7FRR00423L},take_this_message).

Or you can create the 2 processes from one single node like this:

(foo@W7FRR00423L)4> Pid = rpc:call(bar@W7FRR00423L,receiver,start,[]).           
<5974.54.0> 
(foo@W7FRR00423L)5> sender:init(Pid,fish).
<0.50.0>
{ok,"So long and thanks for all the fish"}
(foo@W7FRR00423L)6>

Upvotes: 4

Related Questions