Related
I have recently started learning Erlang and I am trying to implement a server-client sample program. I have created a registered process and I would like to send data to it from another process. The code is as follows.
-module(mine).
-export([alice/0, bob/2, startAlice/0, startBob/1]).
alice() ->
receive
{message, BobNode} ->
io:fwrite("Alice got a message \n"),
BobNode ! message,
alice()
finished -> io:fwrite("Alice is finished\n")
end.
bob(0, AliceNode) ->
{alice, AliceNode} ! finished,
io:fwrite("Bob is finished\n");
bob(N, AliceNode) ->
{alice, AliceNode} ! {message, self()},
receive
message -> io:fwrite("Bob got a message ~w \n",[N])
end,
bob(N-1, AliceNode).
startAlice() ->
register(alice, spawn(mine, alice, [])).
startBob(AliceNode) ->
spawn(mine, bob, [30000, AliceNode]).
Here, I would like to send some value say N, from bob to alice. I tried sending the data as
{alice, AliceNode, Nvalue} ! {message, self(), N} in bob(N, AliceNode) function, but got the error variable 'Nvalue' is unbound erl. I am sure I am missing something trivial here. Any help would be appreciated. Thanks in advance.
Is it possible to spawn a process p in a function funct1 of a module module1, to send a message to p in a function funct2 of module1 and to wait for a reply of p inside funct2, without having to spawn f2 that is therefore considered as self()? If so, what is the best way to implement the waiting part? You can see the code below to have an overview of what I am looking for.
Thanks in advance.
-module(module1)
...
funct1(...)
->
Pid = spawn(module2, function3, [[my_data]]),
...
funct2(...)
->
...
Pid ! {self(), {data1, data2}},
% wait here for the reply from Pid
% do something here based on the reply.
The Answer
Yes.
The Real Problem
You are conflating three concepts:
Process (who is self() and what is pid())
Function
Module
A process is a living thing. A process has its own memory space. These processes are the things making calls. This is the only identity that really matters. When you think about "who is self() in this case" you are really asking "what is the calling context?" If I spawn two instances of a process, they both might call the same function at some point in their lives -- but the context of those calls are completely different because the processes have their own lives and their own memory spaces. Just because Victor and Victoria are both jumping rope at the same time doesn't make them the same person.
Where people get mixed up about calling context the most is when writing module interface functions. Most modules are, for the sake of simplicity, written in a way that they define just a single process. There is no rule that mandates this, but it is pretty easy to understand what a module does when it is written this way. Interface functions are exported and available for any process to call -- and they are calling in the context of the processes calling them, not in the context of a process spawned to "be an instance of that module" and run the service loop defined therein.
There is nothing trapping a process "within" that module, though. I could write a pair of modules, one that defines the AI of a lion and another that defines the AI of a shark, and have a process essentially switch identities in the middle of its execution -- but this is almost always a really bad idea (because it gets confusing).
Functions are just functions. That's all they are. Modules are composed of functions. There is nothing more to say than this.
How to wait for a message
We wait for messages using the receive construct. It matches on the received message (which will always be an Erlang term) and selects what to do based on the shape and/or content of the message.
Read the following very carefully:
1> Talker =
1> fun T() ->
1> receive
1> {tell, Pid, Message} ->
1> ok = io:format("~p: sending ~p message ~p~n", [self(), Pid, Message]),
1> Pid ! {message, Message, self()},
1> T();
1> {message, Message, From} ->
1> ok = io:format("~p: from ~p received message ~p~n", [self(), From, Message]),
1> T();
1> exit ->
1> exit(normal)
1> end
1> end.
#Fun<erl_eval.44.87737649>
2> {Pid1, Ref1} = spawn_monitor(Talker).
{<0.64.0>,#Ref<0.1042362935.2208301058.9128>}
3> {Pid2, Ref2} = spawn_monitor(Talker).
{<0.69.0>,#Ref<0.1042362935.2208301058.9139>}
4> Pid1 ! {tell, Pid2, "A CAPITALIZED MESSAGE! RAAAR!"}.
<0.64.0>: sending <0.69.0> message "A CAPITALIZED MESSAGE! RAAAR!"
{tell,<0.69.0>,"A CAPITALIZED MESSAGE! RAAAR!"}
<0.69.0>: from <0.64.0> received message "A CAPITALIZED MESSAGE! RAAAR!"
5> Pid2 ! {tell, Pid1, "a lower cased message..."}.
<0.69.0>: sending <0.64.0> message "a lower cased message..."
{tell,<0.64.0>,"a lower cased message..."}
<0.64.0>: from <0.69.0> received message "a lower cased message..."
6> Pid1 ! {tell, Pid1, "Sending myself a message!"}.
<0.64.0>: sending <0.64.0> message "Sending myself a message!"
{tell,<0.64.0>,"Sending myself a message!"}
<0.64.0>: from <0.64.0> received message "Sending myself a message!"
7> Pid1 ! {message, "A direct message from the shell", self()}.
<0.64.0>: from <0.67.0> received message "A direct message from the shell"
{message,"A direct message from the shell",<0.67.0>}
A standalone example
Now consider this escript of a ping-pong service. Notice there is only one kind of talker defined inside and it knows how to deal with target, ping and pong messages.
#! /usr/bin/env escript
-mode(compile).
main([CountString]) ->
Count = list_to_integer(CountString),
ok = io:format("~p: Starting pingpong script. Will iterate ~p times.~n", [self(), Count]),
P1 = spawn_link(fun talker/0),
P2 = spawn_link(fun talker/0),
pingpong(Count, P1, P2).
pingpong(Count, P1, P2) when Count > 0 ->
P1 ! {target, P2},
P2 ! {target, P1},
pingpong(Count - 1, P1, P2);
pingpong(_, P1, P2) ->
_ = erlang:send_after(1000, P1, {exit, self()}),
_ = erlang:send_after(1000, P2, {exit, self()}),
wait_for_exit([P1, P2]).
wait_for_exit([]) ->
ok = io:format("~p: All done, Returing.~n", [self()]),
halt(0);
wait_for_exit(Pids) ->
receive
{exiting, Pid} ->
ok = io:format("~p: ~p is done.~n", [self(), Pid]),
NewPids = lists:delete(Pid, Pids),
wait_for_exit(NewPids)
end.
talker() ->
receive
{target, Pid} ->
ok = io:format("~p: Sending ping to ~p~n", [self(), Pid]),
Pid ! {ping, self()},
talker();
{ping, From} ->
ok = io:format("~p: Received ping from ~p. Replying with pong.~n", [self(), From]),
From ! pong,
talker();
pong ->
ok = io:format("~p: Received pong.~n", [self()]),
talker();
{exit, From} ->
ok = io:format("~p: Received exit message from ~p. Retiring.~n", [self(), From]),
From ! {exiting, self()}
end.
There are some details there, like use of erlang:send_after/3 that are used because message sending is so fast that it will beat the speed of the calls to io:format/2 that slow down the actual talker processes and result in a weird situation where the exit messages (usually) arrive before the pings and pongs between the two talkers.
Here is what happens when it is run:
ceverett#changa:~/Code/erlang$ ./pingpong 2
<0.5.0>: Starting pingpong script. Will iterate 2 times.
<0.61.0>: Sending ping to <0.62.0>
<0.62.0>: Sending ping to <0.61.0>
<0.61.0>: Sending ping to <0.62.0>
<0.62.0>: Sending ping to <0.61.0>
<0.61.0>: Received ping from <0.62.0>. Replying with pong.
<0.62.0>: Received ping from <0.61.0>. Replying with pong.
<0.61.0>: Received ping from <0.62.0>. Replying with pong.
<0.62.0>: Received ping from <0.61.0>. Replying with pong.
<0.61.0>: Received pong.
<0.62.0>: Received pong.
<0.61.0>: Received pong.
<0.62.0>: Received pong.
<0.61.0>: Received exit message from <0.5.0>. Retiring.
<0.62.0>: Received exit message from <0.5.0>. Retiring.
<0.5.0>: <0.61.0> is done.
<0.5.0>: <0.62.0> is done.
<0.5.0>: All done, Returing.
If you run it a few times (or on a busy runtime) there is a chance that some of the output will be in different order. That is just the nature of concurrency.
If you are new to Erlang the above code might take a while to sink in. Play with that pingpong script yourself. Edit it. Make it do new things. Create a triangle of pinging processes. Spawn a random circuit of talkers that do weird things. This will make sense suddenly once you mess around with it.
I have a function that does the broadcasting:
broadcast(Msg, Reason) ->
Fun = fun(P) -> P ! {self(), Msg, Reason} end, %line 27
lists:foreach(Fun, nodes()).
But it's not working,I get this error:
=ERROR REPORT==== 12-Apr-2014::15:42:23 ===
Error in process <0.45.0> on node 'sub#Molly' with exit value: {badarg,[{subscri
ber,'-broadcast/2-fun-0-',3,[{file,"subscriber.erl"},{line,27}]},{lists,foreach,
2,[{file,"lists.erl"},{line,1323}]},{subscriber,loop,0,[{file,"subscriber.erl"},
{line,38}]}]}
Line 38 is a line where I call the function
broadcast(Reason, Msg)
I can't wrap my head around the error. Why doesn't this work?
! takes the same arguments as erlang:send/2. The documentation specifies that the target can be one of:
a pid
a port
an atom, meaning a process registered on the local node
{RegName, Node}, for a process registered on a remote node
You're sending messages to the elements of the return value of nodes(). These are atoms, but they are node names, not locally registered processes. If the process you want to send the message to is registered as foo on the remote node, write {foo, P} ! {self(), Msg, Reason} instead.
On the other hand, if you have the pids of the processes on the remote node, there is no need to specify the node name, as the pids contain that information. Just send the message to the remote pid as you would for a local pid.
Node is just an atom, you can't send a message to it. What you need is a pid on that node. For example it could be a registered process and the pid could be obtained by calling rpc:call(Node, erlang, where, [Name]). Another option could be to use gproc.
I have built a very naive parallel ssl acceptor.
-module(multiserver).
-export([start/0,client/1]).
%% This is a dummy SSL Erlang server/client example
start() ->
spawn_link(fun() -> init([]) end).
init([]) ->
ssl:start(),
{ok, ListenSocket} = ssl:listen(9990, [{certfile, "cert.pem"}, {keyfile, "privkey.pem"} ,{reuseaddr, true},{active, true}, binary]),
Pid = self(),
spawn_link(fun() -> listener(ListenSocket, Pid, 1) end),
spawn_link(fun() -> listener(ListenSocket, Pid, 2) end),
loop().
loop() ->
receive
{new, _Pid} ->
%% Do stuff here
loop()
end.
listener(ListenSocket, Pid, Num) ->
{ok, ClientSocket} = ssl:transport_accept(ListenSocket),
ok = ssl:ssl_accept(ClientSocket),
io:format("listener ~p accepted ~n", [Num]),
ok = ssl:send(ClientSocket, "server"),
io:format("listener ~p sent~n", [Num]),
receive
X -> io:format("listener ~p: ~p ~n", [Num, X])
after 5000 ->
io:format("listener ~p timeout ~n", [Num]),
timeout
end,
ssl:close(ClientSocket),
listener(ListenSocket, Pid, Num).
client(Message) ->
ssl:start(),
{ok, Socket} = ssl:connect("localhost", 9990, [binary, {active,true}], infinity),
receive
X -> io:format("~p ~n", [X])
after 2000 ->
timeout
end,
ok = ssl:send(Socket, Message),
ssl:close(Socket),
io:format("client closed~n").
The probelm I have is that listener 2 does not seem to be able to receive any messages. A sample run of the program looks like this:
First I start the server in shell 1.
Shell 1:
1> multiserver:start().
<0.34.0>
Then I call the client/1 three times from a different shell.
Shell 2:
2> multiserver:client("client").
{ssl,{sslsocket,new_ssl,<0.51.0>},<<"server">>}
client closed
ok
3> multiserver:client("client").
{ssl,{sslsocket,new_ssl,<0.54.0>},<<"server">>}
client closed
ok
4> multiserver:client("client").
{ssl,{sslsocket,new_ssl,<0.56.0>},<<"server">>}
client closed
ok
This is the printouts to server shell.
Shell 1:
listener 1 accepted
listener 1 sent
listener 1: {ssl,{sslsocket,new_ssl,<0.51.0>},<<"client">>}
listener 2 accepted
listener 2 sent
listener 1 accepted
listener 1 sent
listener 1: {ssl,{sslsocket,new_ssl,<0.54.0>},<<"client">>}
listener 2 timedout
2>
I have spent some hours with this and I cant understand why it is not possible for listener 2 to receive any data. If I edit the code to use gen_tcp it works as expected.
Is there something I am missing?
Is it possible to do this with the current ssl module?
The reason for the timeout is that in the second process uses the socket option {active,false}, i.e. the receive will never get any message.
The erlang docs for the ssl module states that the socket created by calling transport_accept/1 should inherit the options set for the listener socket. The first process inherits the options when it does transport_accept/3, but for some reason the second process doesn't.
You can inspect the options with
ssl:getopts(ClientSocket,[mode, active])
I have no idea why this happens, but a workaround is to explicitly set the options on the newly accepted socket
ssl:setopts(ClientSocket, [{active,true}, {mode,binary}])
fib(N)->
P1 = spawn(fun concFib:conFib/0),
P2 = spawn(fun concFib:conFib/0),
X=rpc(P1,N-2),Y=rpc(P2,N-1),X+Y.
conFib()->
receive
{Client,N} -> Client ! regfib(N)
end.
rpc(Pid,Request)->
case erlang:is_process_alive(Pid) of
true -> begin
Pid ! {self(),Request},
receive
{Pid,Respond} -> Respond
end
end;
false -> io:format("~w process is dead.",[Pid])
end.
regfib(N)->
case N<2 of
true -> 1;
false -> regfib(N,1,1,1)
end.
regfib(N,N,X,_)-> X ;
regfib(N,M,X,Y)-> regfib(N,M+1,X+Y,X).
The idea is to divide the fib(N) process into two process one calculates fib(N-2) and the other one calc. fib(N-1) concurrently as fib(N)=fib(N-1)+fib(N-2). when i run the previous code nothing happen and the cursor stop as in finite loop or waiting for not arriving result.
plzzz i need help i'm a new Erlang programmer,thanks in advance :)
In your conFib you send an integer, but await a tuple in rpc. Should change it to:
conFib()->
receive
{Client,N} -> Client ! {self(), regfib(N)}
end.
You can evade such situations by using timeout with after in your receives.
To make the computation parallel you could do something like:
fib(N)->
P1 = spawn(fun test:conFib/0),
P2 = spawn(fun test:conFib/0),
P1 ! {self(), N - 2},
P2 ! {self(), N - 1},
receive
{_, R1} -> R1
end,
receive
{_, R2} -> R2
end,
R1 + R2.
The important part is that you send both of the requests before waiting for the answers.
The part waiting for the answers could of course be done in a more beautiful way.