I followed the book "Programming Erlang — Joe Armstrong" to try to build the communication between 2 Mac computers with Erlang (Chap 14):
% file: kvs.erl
-module(kvs).
-export([start/0, store/2, lookup/1]).
start() -> register(kvs, spawn(fun() -> loop() end)).
store(Key, Value) -> rpc({store, Key, Value}).
lookup(Key) -> rpc({lookup, Key}).
rpc(Q) ->
kvs ! {self(), Q},
receive
{kvs, Reply} ->
Reply
end.
loop() ->
receive
{From, {store, Key, Value}} ->
put(Key, {ok, Value}),
From ! {kvs, true},
loop();
{From, {lookup, Key}} ->
From ! {kvs, get(Key)},
loop()
end.
Set up Mac 1 (Mac Pro) and run a Erlang server:
$ sudo hostname this.is.macpro.com
$ hostname
this.is.macpro.com
$ ipconfig getifaddr en2
aaa.bbb.ccc.209
$ erl -name server -setcookie abcxyz
(server#this.is.macpro.com)> c("kvs.erl").
{ok,kvs}
(server#this.is.macpro.com)> kvs:start().
true
(server#this.is.macpro.com)> kvs:store(hello, world).
true
(server#this.is.macpro.com)> kvs:lookup(hello).
{ok,world}
I tried using both IP and hostname to make a RPC from another Mac but get {badrpc, nodedown}.
Set up Mac 2 (MacBook Pro) and try to call Mac 1:
$ sudo hostname this.is.macbookpro.com
$ hostname
this.is.macbookpro.com
$ ipconfig getifaddr en2
aaa.bbb.ccc.211 # different IP
$ erl -name client -setcookie abcxyz
% try using the hostname of Mac 1 but failed
(client#this.is.macbookpro.com)> rpc:call('server#this.is.macpro.com', kvs, lookup, [hello]).
{badrpc, nodedown}
% try using the IP address of Mac 1 but failed
(client#this.is.macbookpro.com)> rpc:call('server#aaa.bbb.ccc.209', kvs, lookup, [hello]).
{badrpc, nodedown}
How to set up my Mac computers and make them available for RCP with Erlang?
When using th -name, you should provide the full name. The syntax you are using is for -sname. Try this:
erl -name server#this.is.macpro.com -setcookie "abcxyz"
erl -name client#this.is.macbookpro.com -setcookie "abcxyz"
You can also specify the an IP after the # in both cases.
Then from one node, connect to the other node:
net_kernel:connect_node('client#this.is.macbookpro.com').
This should return true. If it returns false then you are not connected. You can very with nodes()..
(joe#teves-MacBook-Pro.local)3> net_kernel:connect_node('steve#Steves-MacBook-Pro.local').
true
(joe#teves-MacBook-Pro.local)4> nodes().
['steve#Steves-MacBook-Pro.local']
If this does not fix it, then you can check epmd on both systems to ensure they are registered.
epmd -names
Related
I'm running into trouble with an Erlang OTP + Cowboy app that does not allow me to open enough files simultaneously.
How do I change the number of open file handles allowed in the BEAM?
Potentially, I'll need about 500 small text files open at the same time, but it appears that the file limit is 224. I've got the value of 224 from this little test program:
-module(test_fds).
-export([count/0]).
count() -> count(1, []).
count(N, Fds) ->
case file:open(integer_to_list(N), [write]) of
{ok, F} ->
count(N+1, [F| Fds]);
{error, Err} ->
[ file:close(F) || F <- Fds ],
delete(N-1),
{Err, N}
end.
delete(0) -> ok;
delete(N) ->
case file:delete(integer_to_list(N)) of
ok -> ok;
{error, _} -> meh
end,
delete(N-1).
This gives
$ erl
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [kernel-poll:false]
Eshell V9.2 (abort with ^G)
1> c(test_fds).
{ok,test_fds}
2> test_fds:count().
{emfile,224}
3>
This seems to be an Erlang problem rather than a Mac OSX problem since from the command line, I get:
$ sysctl -h kern.maxfiles
kern.maxfiles: 49,152
$ sysctl -h kern.maxfilesperproc
kern.maxfilesperproc: 24,576
The number of open file descriptors is most likely being limited by your shell. You can increase it by running ulimit -n 1000 (or more) in your shell before invoking erl. On my system, the default value was 7168 and your script could open 7135 files before returning emfile. Here's the output of me running your script with different ulimit values:
$ ulimit -n 64; erl -noshell -eval 'io:format("~p~n", [test_fds:count()]), erlang:halt()'
{emfile,32}
$ ulimit -n 128; erl -noshell -eval 'io:format("~p~n", [test_fds:count()]), erlang:halt()'
{emfile,96}
$ ulimit -n 256; erl -noshell -eval 'io:format("~p~n", [test_fds:count()]), erlang:halt()'
{emfile,224}
$ ulimit -n 512; erl -noshell -eval 'io:format("~p~n", [test_fds:count()]), erlang:halt()'
{emfile,480}
erl is most likely opening 32 file descriptors before it starts evaluating our code which explains the constant difference of 32 in the ulimit and the output.
I'm testing the code in Getting Started with Erlang User's Guide Concurrent Programming Section.
In tut17.erl, I started a process with erl -sname ping, and another process with al -sname pong as the guide described.
-module(tut17).
-export([start_ping/1, start_pong/0, ping/2, pong/0]).
ping(0, Pong_Node) ->
{pong, Pong_Node} ! finished,
io:format("ping finished~n", []);
ping(N, Pong_Node) ->
{pong, Pong_Node} ! {ping, self()},
receive
pong ->
io:format("Ping received pong~n", [])
end,
ping(N - 1, Pong_Node).
pong() ->
receive
finished -> io:format("Pong finished~n", []);
{ping, Ping_PID} ->
io:format("Pong received ping~n", []),
Ping_PID ! pong,
pong()
end.
start_pong() ->
register(pong, spawn(tut17, pong, [])).
start_ping(Pong_Node) ->
spawn(tut17, ping, [3, Pong_Node]).
From the ping and pong process, I could invoke the start_ping and start_pong to check everything works fine.
(ping#smcho)1> tut17:start_ping(pong#smcho).
<0.40.0>
Ping received pong
Ping received pong
Ping received pong
ping finished
(pong#smcho)2> tut17:start_pong().
true
Pong received ping
Pong received ping
Pong received ping
Pong finished
I'm trying to run the same code from a command line; For a simple hello world example:
-module(helloworld).
-export([start/0]).
start() ->
io:fwrite("Hello, world!\n").
I use the following command line:
erlc helloworld.erl
erl -noshell -s helloworld start -s init stop
So, I just tried with the following, but ended up with a crash.
From ping node: erl -noshell -sname ping -s tut17 start_ping pong#smcho -s init stop
From pong node: erl -noshell -sname pong -s tut17 start_pong -s init stop
However, I got this error report from the ping when pong ends without printing anything.
=ERROR REPORT==== 6-Mar-2015::20:29:24 ===
Error in process <0.35.0> on node 'ping#smcho' with exit value:
{badarg,[{tut17,ping,2,[{file,"tut17.erl"},{line,9}]}]}
Compared to the REPL approach, with command line, each process does not wait for the counterpart to response, but stops after some time.
What might be wrong?
Arguments from the command line are received as a list of atoms when using the -s switch, and a list of strings when using the -run switch. With this in mind, let's think through what happens...
This command is issued from the shell:
erl -noshell -sname ping \
-s tut17 start_ping pong#smcho \
-s init stop
So start_ping/1 is being called with the argument [pong#smcho]. It is then calling ping/2 as ping(3, [pong#smcho]), which in its first line tries to do {pong, [pong#smcho]} ! {ping, self()}. Because a list is not a valid target for a message... your world explodes.
To run this both from the Erlang shell and the system shell comfortably you could add a clause to start_ping/1:
start_ping([Pong_Node]) ->
spawn(tut17, ping, [3, Pong_Node]);
start_ping(Pong_Node) ->
start_ping([Pong_Node]).
There are some changes needed.
start_ping
From zxq9's hint, I modified the start_ping function.
start_ping([Pong_Node]) ->
io:format("Ping started~n", []),
spawn(tut17, ping, [3, Pong_Node]);
start_ping(Pong_Node) ->
start_ping([Pong_Node]).
Invoke the init:stop() when process is finished.
I'm not sure this is absolutely necessary, but it seems to be working with this modification.
ping(0, Pong_Node) ->
{pong, Pong_Node} ! finished,
io:format("ping finished~n", []),
init:stop();
Then I could remove -s init stop from the shell command: erl -noshell -sname ping -s tut17 start_ping pong#smcho.
Execution order
Pong should be invoked before Ping is invoked.
The updated code
This is the revised code:
-module(tut17).
-export([start_ping/1, start_pong/0, ping/2, pong/0]).
ping(0, Pong_Node) ->
{pong, Pong_Node} ! finished,
io:format("ping finished~n", []),
init:stop();
ping(N, Pong_Node) ->
{pong, Pong_Node} ! {ping, self()},
receive
pong ->
io:format("Ping received pong~n", [])
end,
ping(N - 1, Pong_Node).
pong() ->
receive
finished ->
io:format("Pong finished~n", []),
init:stop();
{ping, Ping_PID} ->
io:format("Pong received ping~n", []),
Ping_PID ! pong,
pong()
end.
start_pong() ->
io:format("Pong started~n", []),
register(pong, spawn(tut17, pong, [])).
start_ping([Pong_Node]) ->
io:format("Ping started~n", []),
spawn(tut17, ping, [3, Pong_Node]);
start_ping(Pong_Node) ->
start_ping([Pong_Node]).
The shell commands:
ping: erl -noshell -sname ping -s tut17 start_ping pong#smcho
pong: erl -noshell -sname pong -s tut17 start_pong
When I run erl -sname foo, the node name created uses the hostname, rather than "localhost", so it is generated as, for example, foo#roger-pc.
I then register(shell, self()), and I can send messages to it from another node (erl -sname bar) as follows:
{shell, 'foo#roger-pc'} ! {hello, world}.
But it doesn't work if I use {shell, foo} ! {knock, knock}. The message is never received.
How do I connect to an Erlang node on the same PC which is using a short name? Or: how do I derive the "#roger-pc" part of the destination node name? Or: should I just use erl -name foo#localhost to register a long name?
Some background: I'm writing a an escript which spawns an erl process, and I'd like to be able to send messages from that OS process back to the original script.
you can specify 'localhost' explicitly for sname.
first shell
erl -sname ax#localhost
register(rcvr, self()).
second shell
erl -sname bx#localhost
net_kernel:connect_node(ax#localhost).
{rcvr, ax#localhost} ! hello.
and first shell again
(ax#localhost)7> flush().
Shell got hello
ok
sname still requires the '#' notation. You just don't have to input the full uri when sending messages.
c:\>erl -sname short
(short#hostname)1> {process, 'short#hostname'} ! Message.
c:\>erl -name short
(short#hostname.more.stuff)1> {process, 'short#hostname.more.stuff'} ! Message.
short is not the full name. sname and name merely decide how much of the rest of the path is required.
A node is an executing Erlang runtime system which has been given a name, using the command line flag -name (long names) or -sname (short names).
The format of the node name is an atom name#host where name is the name given by the user and host is the full host name if long names are used, or the first part of the host name if short names are used. node() returns the name of the node. Example:
% erl -name dilbert
(dilbert#uab.ericsson.se)1> node().
'dilbert#uab.ericsson.se'
% erl -sname dilbert
(dilbert#uab)1> node().
dilbert#uab
I am trying to implement a really simple example to get a first look into Distributed Erlang. So, I start two nodes on the same machine:
erl -sname dilbert
erl -sname dogbert
The task is that dogbert makes dilbert output Hello, World! using a simple fun () -> io:format("Hello, World!") end on node dilbert. I thought this would be easy with dogbert's shell:
(dogbert#centraldogma.fubar)1> spawn(dilbert, fun () -> io:format("HELLO!") end).
<0.39.0>
=ERROR REPORT==== 13-Jun-2012::17:49:04 ===
** Can not start erlang:apply,[#Fun<erl_eval.20.82930912>,[]] on dilbert **
In the same shell, using nodes(). outputs []. Well, dogbert obviously doesn't know about dilbert, but why is that the case? Or, how do I make nodes in distributed erlang get to know each other?
There are two problems:
You must set a common cookie for both nodes so they can interact.
erl -sname dilbert -setcookie pointyhairedboss
erl -sname dogbert -setcookie pointyhairedboss
You must specify the hostname of the node you want to connect to.
spawn('dogbert#yourhostname', fun () -> io:format("HELLO!") end).
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I prepare to develop one heartbeat program, which need to send udp packet every 5s.
How to sleep 5s in erlang or is there sleep(5) function to be used?
How to make it run in background?
If you want your application to send a udp packet I would recommend you to start with a gen_server(coz. you will obviously be having need to add other functionalities to your application).
1. For sending packets at regular interval.
timer:send_interval(5000,interval),
This will call "handle_call(interval,State)" callback of gen_server every 5 seconds from where you can send your packets
2. Making it run in background.
As already posted use "run_erl". I have used this myself to run my application successfully as a daemon.
run_erl -daemon /tmp "erl"
This will create two pipes "erlang.pipe.1.r" and "erlang.pipe.1.w" under "/tmp" dir of unix and you can write commands to write pipe for starting your application using perl or any scripting lang or even c/c++ :)
Recently I have been learning the erlang programming language. One task I gave myself was to write a linux daemon.
As you probably already know, daemons are used to run unix services. Services commonly controlled by daemons include database servers, web servers, web proxies etc. In this example the server is very simple, the client calls the function "say_hi" and the server responds with "hello".
In the linux environment daemons are controlled by scripts that are stored in places such as /etc/init.d. These scripts respond according to convention to the commands start, stop and restart.
Let us start with the shell script:
#!/bin/sh
EBIN=$HOME/Documents/Erlang/Daemon
ERL=/usr/bin/erl
case $1 in
start|stop|restart)
$ERL -detached -sname mynode \
-run daemon shell_do $1 >> daemon2.log
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0
This has to be one of the simplest shell scripts that you have ever seen. Daemon respond to three different commands, stop, start and restart. In this script the command is simply passed through to the daemon. One improvement would be to exit with the return code from the daemon execution.
So how about the daemon? Here it is...
%% PURPOSE
%% Author: Tony Wallace
%%
%% Manage an erlang daemon process as controlled by a shell scripts
%% Allow standard daemon control verbs
%% Start - Starts a daemon in detached mode and exits
%% Stop - Attaches to the daemon, monitors it, sends an EXIT message and waits for it to die
%% Restart - Calls stop and then start
%% Log events
%% Return UNIX compatible codes for functions called from shell scripts
%% Exit shell script calls so as to not stop the scripts from completing
%% Shell scripts expected to use shell_do to execute functions
%%
%% Allow interaction with daemon from other erlang nodes.
%% Erlang processes are expected to call functions directly rather than through shell_do
%%
%% MOTIVATION
%% Erlang is great, but as an application it needs to be managed by system scripts.
%% This is particularly for process that are expected to be running without user initiation.
%%
%% INVOCATION
%% See daemon.sh for details of calling this module from a shell script.
%%
%% TO DO
%% Define and use error handler for spawn call.
-module(daemon).
%-compile([{debug_info}]).
-export [start/0,start/1,stop_daemon/0,say_hi/0,kill/0,shell_do/1].
%%-define (DAEMON_NAME,daemon#blessing).
-define (DAEMON_NAME,list_to_atom("daemon#"++net_adm:localhost())).
-define (UNIX_OKAY_RESULT,0).
-define (TIMEOUT_STARTING_VM,1).
-define (VM_STARTED_WITHOUT_NAME,2).
-define (INVALID_VERB,3).
-define (COULD_NOT_CONNECT,4).
-define (TIMEOUT_WAITING_QUIT,5).
-define (TIMEOUT_STOPPING_VM,6).
wait_vm_start(_,0) -> ?TIMEOUT_STARTING_VM;
wait_vm_start(D,N) ->
net_kernel:connect(D),
Dl = lists:filter(fun(X) -> X==D end,nodes()),
if Dl =:= [] ->
receive after 1000 -> true end,
wait_vm_start(D,N-1);
Dl /= [] -> ?UNIX_OKAY_RESULT
end.
wait_vm_stop(_,0) -> ?TIMEOUT_STOPPING_VM;
wait_vm_stop(D,N) ->
net_kernel:connect(D),
Dl = lists:filter(fun(X) -> X==D end,nodes()),
if Dl /= [] ->
receive after 1000 -> true end,
wait_vm_start(D,N-1);
Dl == [] -> ?UNIX_OKAY_RESULT
end.
flush() ->
receive
_ ->
flush()
after
0 ->
true
end.
sd(Hdl) ->
MyNode=node(),
if
MyNode =:= nonode#nohost ->
info(stdout,"~s","Error: Erlang not started with a name. Use -sname <name>"),
?VM_STARTED_WITHOUT_NAME;
MyNode /= nonode#nohost ->
Atm_daemon = ?DAEMON_NAME,
Connected = net_kernel:connect(Atm_daemon),
case Connected of
true ->
info(Hdl,"~s",["daemon process already started"]),
?UNIX_OKAY_RESULT;
false ->
info(Hdl,"~s",["starting daemon process"]),
StartString = "erl -detached -sname daemon",
os:cmd(StartString),
Vm_daemon = wait_vm_start(Atm_daemon,10),
case Vm_daemon of
?UNIX_OKAY_RESULT ->
info(Hdl,"~s",["spawning main daemon process"]),
spawn(Atm_daemon,?MODULE,start,[]), ?UNIX_OKAY_RESULT;
A -> A
end
end % case Connected %
end.
say_hi() ->
Daemon = ?DAEMON_NAME,
Connected = net_kernel:connect(Daemon),
if Connected ->
{listener,Daemon} ! {hello,self()},
receive
Response -> Response
after 10000 -> timeout end;
not Connected -> could_not_connect
end.
stop_daemon() ->
Daemon = ?DAEMON_NAME,
Connected = net_kernel:connect(Daemon),
if Connected ->
flush(),
{listener,Daemon} ! {quit,self()},
receive
bye -> wait_vm_stop(Daemon,10)
after 10000 -> ?TIMEOUT_WAITING_QUIT
end;
not Connected -> ?COULD_NOT_CONNECT
end.
shell_do(Verb) ->
{A,Hdl} = file:open('daemon_client.log',[append]),
case A of
ok ->
info(Hdl,"~s",[Verb]);
error -> error
end,
Result = handle_verb(Hdl,Verb),
info(Hdl,"Return status ~.10B",[Result]),
init:stop(Result).
%%handle_verb(_,_) -> 0;
handle_verb(Hdl,["start"]) -> sd(Hdl);
handle_verb(_,["stop"]) -> stop_daemon();
handle_verb(Hdl,["restart"]) ->
stop_daemon(),
sd(Hdl);
handle_verb(Hdl,X) ->
info(Hdl,"handle_verb failed to match ~p",[X]),
?INVALID_VERB.
kill() ->
rpc:call(?DAEMON_NAME, init, stop, []).
start(Source) ->
Source ! starting,
start().
start() ->
register(listener,self()),
case {_,Hdl}=file:open("daemon_server.log",[append]) of
{ok,Hdl} -> server(Hdl);
{error,Hdl} -> {error,Hdl}
end.
info(Hdl,Fmt,D)->
io:fwrite(Hdl,"~w"++Fmt++"~n",[erlang:localtime()] ++ D).
server(Hdl) ->
info(Hdl,"~s",["waiting"]),
receive
{hello,Sender} ->
info(Hdl,"~s~w",["hello received from",Sender]),
Sender ! hello,
server(Hdl);
{getpid,Sender} ->
info(Hdl,"~s~w",["pid request from ",Sender]),
Sender ! self(),
server(Hdl);
{quit,Sender} ->
info(Hdl,"~s~w",["quit recevied from ",Sender]),
Sender ! bye,
init:stop();
_ ->
info(Hdl,"~s",["Unknown message received"])
after
50000 ->
server(Hdl)
end.
For the reader not used to reading erlang, there some of this code is run as a result of the shell script we saw above. Other code in this file is the daemon itself. Referring back to the shell script we see that the script calls procedure shell_do. Shell_do writes log entries, calls handle_verb and exits. Handle_verb implements the different behaviours for each verb. Starting the daemon is handled by function sd, which creates the daemon by an operating system call os:cmd, waits for the erlang virtual machine to initialise, and then spawns the server code called start, which in turn calls server.
Sleep is available in erlang, through the timer functions.
http://www.erlang.org/doc/man/timer.html
For the background process, you can use the -detached cli argument.
You can specify an entry point with -s
EDIT
You can also spawn a new process from your main program:
http://www.erlang.org/doc/reference_manual/processes.html
With respect to daemonizing, consider starting your erlang program with the run_erl utility that comes with OTP. Note in particular the -daemon command line flag.