So, i spent LOTS of time and still didn't find the answer.
I have simple tcp server in gen_server, which accepts messages via telnet and print them in console:
-module(st_server).
-behaviour(gen_server).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
%% --------------------------------------------------------------------
%% External exports
-export([start_link/0, start_link/1, stop/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-define(DEFAULT_PORT, 1055).
-record(state, {lsock, port}).
%% ====================================================================
%% External functions
%% ====================================================================
%%--------------------------------------------------------------------
%% #doc Starts the server.
%%
%% #spec start_link(Port::integer()) -> {ok, Pid}
%% where
%% Pid = pid()
%% #end
%%--------------------------------------------------------------------
start_link(Port) ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []),
io:format("Server name: ~w, port: ~w.~n", [?SERVER, Port]).
%% #spec start_link() -> {ok, Pid}
%% #doc Calls `start_link(Port)' using the default port.
start_link() -> start_link(?DEFAULT_PORT).
%%--------------------------------------------------------------------
%% #doc Stops the server.
%% #spec stop() -> ok
%% #end
%%--------------------------------------------------------------------
stop() ->
gen_server:cast(?SERVER, stop).
%% ====================================================================
%% Server functions
%% ====================================================================
%% --------------------------------------------------------------------
%% Function: init/1
%% Description: Initiates the server
%% Returns: {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% --------------------------------------------------------------------
init([Port]) ->
{ok, LSock} = gen_tcp:listen(Port, [{active, true}]),
{ok, #state{lsock = LSock, port = Port}, 0}.
%% --------------------------------------------------------------------
%% Function: handle_cast/2
%% Description: Handling cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%% --------------------------------------------------------------------
handle_cast(stop, State) ->
{stop, normal, State}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
%% --------------------------------------------------------------------
%% Function: handle_info/2
%% Description: Handling all non call/cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%% --------------------------------------------------------------------
handle_info({tcp, _Socket, Data}, State) ->
io:format("Incoming info: ~s", [Data]),
{noreply, State};
handle_info({tcp_closed, _Socket}, #state{lsock = LSock} = State) ->
{ok, _Sock} = gen_tcp:accept(LSock),
{noreply, State};
handle_info(timeout, #state{lsock = LSock} = State) ->
{ok, _Sock} = gen_tcp:accept(LSock),
{noreply, State}.
%% --------------------------------------------------------------------
%% Function: terminate/2
%% Description: Shutdown the server
%% Returns: any (ignored by gen_server)
%% --------------------------------------------------------------------
terminate(Reason, _State) ->
io:format("~nServer shutdown. Reason: ~s.~n", [Reason]),
ok.
%% --------------------------------------------------------------------
%% Func: code_change/3
%% Purpose: Convert process state when code is changed
%% Returns: {ok, NewState}
%% --------------------------------------------------------------------
code_change(_OldVsn, _State, _Extra) ->
{ok, _State}.
%% --------------------------------------------------------------------
%%% Internal functions
%% --------------------------------------------------------------------
And supervisor:
-module(st_sup).
-behaviour(supervisor).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
%% --------------------------------------------------------------------
%% External exports
%% --------------------------------------------------------------------
-export([]).
%% --------------------------------------------------------------------
%% Internal exports
%% --------------------------------------------------------------------
-export([
init/1,
start_link/0
]).
%% --------------------------------------------------------------------
%% Macros
%% --------------------------------------------------------------------
-define(SERVER, ?MODULE).
%% --------------------------------------------------------------------
%% Records
%% --------------------------------------------------------------------
%% ====================================================================
%% External functions
%% ====================================================================
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%% ====================================================================
%% Server functions
%% ====================================================================
%% --------------------------------------------------------------------
%% Func: init/1
%% Returns: {ok, {SupFlags, [ChildSpec]}} |
%% ignore |
%% {error, Reason}
%% --------------------------------------------------------------------
init([]) ->
Server = {st_server, {st_server, start_link, []},
permanent, 2000, worker, [st_server]},
Children = [Server],
RestartStrategy = {one_for_one, 0, 1},
{ok, {RestartStrategy, Children}}.
%% ====================================================================
%% Internal functions
%% ====================================================================
I start this in erlang console:
st_sup:start_link().
And get this:
Server name: st_server, port: 1055.
** exception exit: shutdown
This means, that supervisor shuts down server for some reason.
If I start server by itself (st_server:start_link.) it works just fine.
So, the question is how to make this work without shutdowns?
At a glance, it's probably because the return value of st_server:start_link/1 is the return value of the call to io:format/2.
This is fine when you call st_server:start_link() directly in the REPL, but the supervisor is probably expecting a return value more like {ok, Pid}.
You can fix this just by swapping the two lines in st_server:start_link/1, so that it returns the value of the call to gen_server:start_link/4:
start_link(Port) ->
io:format("Server name: ~w, port: ~w.~n", [?SERVER, Port]),
gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).
A strange value is also the supervisor's RestartStrategy which is set to {one_for_one,0,1}. This means that the supervisor will give up up after more than zero restarts in 1 second, in effect it will give up after the first crash by a child. Which is not usually not what you want a supervisor to do.
Related
In Erlang, how can I receive a message sent from a specific pid?
Here are a couple of ways:
1) Send the target process a secret code, which it can send back with all its messages:
-module(my).
-compile(export_all).
worker(TargetPid, Msg, SecretId) ->
TargetPid ! {Msg, SecretId}.
get_msg(SecretId) ->
receive
{Msg, SecretId} -> Msg
end.
test() ->
SecretId = "A1!cd!",
spawn(my, worker, [self(), hello, "Xy*d1"]),
spawn(my, worker, [self(), goodbye, SecretId]),
io:format("~w~n", [get_msg(SecretId)]).
In the shell:
6> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
7> my:test().
goodbye
ok
8>
2) Instead of matching against the secret code in the receive clause, have the target process send a message tagged with its Pid:
-module(my).
-compile(export_all).
get_msg(TargetPid) ->
receive
{Msg, TargetPid} -> Msg
end.
worker(TargetPid, Msg) ->
TargetPid ! {Msg, self()}.
test() ->
spawn(my, worker, [self(), hello]),
Pid = spawn(my, worker, [self(), goodbye]),
io:format("~w~n", [get_msg(Pid)]).
In the shell:
3> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
4> my:test().
goodbye
ok
5>
You need to include the sender's pid in the message and match against it in the receiving process.
There is a simple implementation of the factorial function in an 'escript' in the Erlang docs. The factorial function is given as:
fac(0) -> 1;
fac(N) -> N * fac(N-1).
That's all fine, I can get this to work, no problem.
I would however like to know how I can implement this same, simple factorial function in an 'OTP way' using rebar3?
Just to be clear, my questions are:
Where does the code go?
How would I call it from the shell?
Could I also run it from the command line like I do via the escript example?
FYI, I have gotten started with rebar3. Here is where I am at:
rebar3 new app factorial
creates a few files but specifically the code is in 3 files in a src directory. I can see that a supervisor is being used, seems fine.
I can interact with this project from the shell:
$ rebar3 shell
1> application:which_applications().
[{factorial,"An OTP application","0.1.0"},
{inets,"INETS CXC 138 49","7.0.3"},
{ssl,"Erlang/OTP SSL application","9.1.1"},
{public_key,"Public key infrastructure","1.6.4"},
{asn1,"The Erlang ASN1 compiler version 5.0.8","5.0.8"},
{crypto,"CRYPTO","4.4"},
{stdlib,"ERTS CXC 138 10","3.7"},
{kernel,"ERTS CXC 138 10","6.2"}]
2> application:stop(factorial).
=INFO REPORT==== 21-Jan-2019::12:42:07.484244 ===
application: factorial
exited: stopped
type: temporary
ok
3> application:start(factorial).
ok
Where does the code go?
To 'call code in the OTP way', you can put it behind a gen_server.
For this simple factorial function, I added a new file factorial.erl within the src directory which is pretty much a standard gen_server skeleton with my factorial function as one of the callbacks:
% factorial.erl
-module(factorial).
-behaviour(gen_server).
-export([start_link/0, stop/0, calc/1]).
<boilerplate gen_server stuff here, like init, etc.>
calc(N) ->
{ok, Result} = gen_server:call(?SERVER, {calc, N}),
{ok, Result}.
handle_call({calc, N}, _From, State) ->
Factorial = factorial(N),
Reply = {ok, Factorial},
{reply, Reply, State};
factorial(0) ->
1;
factorial(N) ->
N * factorial(N-1).
Since my rebar3 new app factorial created a supervisor, I modified the supervisor's init so that it calls my factorial module:
% factorial_sup.erl
<skeleton supervisor stuff here>
init([]) ->
Server = {factorial, {factorial, start_link, []},
permanent, 2000, worker, [factorial]},
Children = [Server],
RestartStrategy = {one_for_one, 0, 1},
{ok, {RestartStrategy, Children}}.
How do I call it from the shell?
$ rebar3 shell
<Enter>
1> factorial:calc(5).
{ok,120}
Since this is running under a supervisor, we can still stop and restart it:
2> application:stop(factorial).
=INFO REPORT==== 22-Jan-2019::13:31:29.243520 ===
application: factorial
exited: stopped
type: temporary
ok
3> factorial:calc(5).
** exception exit: {noproc,{gen_server,call,[factorial,{calc,5}]}}
in function gen_server:call/2 (gen_server.erl, line 215)
in call from factorial:calc/1 (/Users/robert/git/factorial/src/factorial.erl, line 32)
4> application:start(factorial).
ok
5> factorial:calc(5).
{ok,120}
How do I create an executable?
Work in progress :-).
I am new to Erlang and I am learning by building a really small chat program in pure Erlang.
I would like for clients to be able to connect to a server and then send messages to each other. But this is all done on a local machine and not over a network just for learning.
I have a list of all the clients that have connected to the server.
If client A sends a message to client B, I get the desired output in client A's terminal but I can't work out how to get the message from client A to be displayed on client B's terminal.
Or do I have to set each client with it's own mini server
-module(server).
-export([start/0]).
-export([server/1]).
-export([connect/0]).
-export([sendMessage/2]).
%%
%% The Server
%%
start() ->
EmptyList = [],
Pid = spawn(server, server, [EmptyList]),
register(chatServe, Pid).
server(ListOfClients) ->
receive
{Client, connect} ->
Client ! {chatServe, connected},
List = clientList(ListOfClients, Client),
server(List);
{Client, message, MessageBody} ->
List = ListOfClients,
lists:foreach(fun(X) -> X ! {chatServe, new_message, MessageBody} end, List),
Client ! {chatServe, received},
server(List)
end.
%%
%% The client will call rpc:call(server#local, server, connect, [])
%% to connect
%%
connect() ->
chatServe ! {self(), connect},
receive
{chatServe, connected} -> connected
end.
%%
%% The send message method takes two args
%%
%%
sendMessage(SendTo, MessageBody) ->
chatServe ! {self(), message, MessageBody},
receive
{chatServe, received} -> received
end.
receiveMessage(SendTo, SendFrom, MessageBody) ->
receive
{}
end.
%%
%% Some helper functions
%%
clientList(List, Client) when length(List) =:= 0 ->
io:format("List Size = 1~n"),
[Client];
clientList(List, Client) ->
io:format("List size = ~p~n", [length(List) + 1]),
[Client | List].
forwardMessage(SendTo, SentFrom, MessageBody, [H | T]) when H =:= SendTo ->
SendTo ! {SentFrom, message, MessageBody};
forwardMessage(SendTo, SentFrom, MessageBody, [H | T]) ->
forwardMessage(SendTo, SentFrom, MessageBody, T);
forwardMessage(SendTo, SentFrom, MessageBody, []) -> [].
In the terminal of the client I am calling
rpc:call(host, mod, function, args).
So my question is how can I get Client A to send a message to Client B through server C, with Client A displaying success, and Client B displaying the message that was sent?
Thanks in advance
In this example my server is an Erlang node and every client is an erlang node too.
Code:
-module(test).
-export([server_start/1, client_start/2]).
-export([server_new_message/2, client_new_message/2]).
server_start(ServerName) ->
{ok, _Pid} = net_kernel:start([ServerName, shortnames]),
erlang:register(server, erlang:self()),
io:format("Server '~p' started.~nMessages: ~n ~n", [erlang:node()]),
server_loop().
server_loop() ->
receive
{msg, Name, Text} ->
io:format("~p: ~p~n", [Name, Text]),
Receivers = lists:delete(Name, erlang:nodes()),
rpc:multicall(Receivers, ?MODULE, client_new_message, [Name, Text]),
server_loop()
end.
%% Server runs this function in client's node.
%% 'client' process in client's node will receive this message and print it
client_new_message(Name, Text) ->
client ! {msg, Name, Text}.
client_start(ServerName, ClientName) ->
{ok, _Pid} = net_kernel:start([ClientName, shortnames]),
pong = net_adm:ping(ServerName),
timer:sleep(1000), % wait for updating erlang:nodes()
Other = lists:delete(ServerName, erlang:nodes()),
io:format("Client '~p' connected to server '~p'.~nOnline users: ~p~n", [erlang:node(), ServerName, Other]),
erlang:register(client, spawn_link(fun print/0)),
client_loop(ServerName).
print() ->
receive
{msg, Name, Text} ->
io:format("~p: ~p~n", [Name, Text]),
print()
end.
client_loop(ServerName) ->
rpc:call(ServerName, ?MODULE, server_new_message, [erlang:node(), io:get_line(">>> ")]),
client_loop(ServerName).
%% Clients run this function in server's node
%% 'server' process will receive messages and print them and broadcast them
server_new_message(Name, Text) ->
server ! {msg, Name, Text}.
Run:
I open 3 Erlang shells.
In shell 1 i run server:
p#jahanbakhsh ~/Desktop $ erl
Erlang/OTP 19 [erts-8.2.2] [source-1ca84a4] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2.2 (abort with ^G)
1> test:server_start(local_chat_server).
Server 'local_chat_server#jahanbakhsh' started.
Messages:
Server is waiting for messages.
In shell 2 i run client 1:
p#jahanbakhsh ~/Desktop $ erl
Erlang/OTP 19 [erts-8.2.2] [source-1ca84a4] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2.2 (abort with ^G)
1> test:client_start('local_chat_server#jahanbakhsh', client_1).
Client 'client_1#jahanbakhsh' connected to server 'local_chat_server#jahanbakhsh'.
Online users: []
>>>
Now i can send message from this terminal, but wait.
I run client 2 in shell 3:
p#jahanbakhsh ~/Desktop $ erl
Erlang/OTP 19 [erts-8.2.2] [source-1ca84a4] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2.2 (abort with ^G)
1> test:client_start('local_chat_server#jahanbakhsh', client_2).
Client 'client_2#jahanbakhsh' connected to server 'local_chat_server#jahanbakhsh'.
Online users: [client_1#jahanbakhsh]
>>>
I send a message ("Test message") from shell 2 or client 1.
In shell 1 or server i have:
client_1#jahanbakhsh: "Test message\n" - sent to [client_2#jahanbakhsh]
In shell 3 or client 2 i have:
client_1#jahanbakhsh: "Test message\n"
>>>
I have a test module and a simple_one_for_one supervisor.
test.erl
-module(test).
-export([
run/1,
do_job/1
]).
run(Fun) ->
test_sup:start_child([Fun]).
do_job(Fun) ->
Pid = spawn(Fun),
io:format("started ~p~n", [Pid]),
{ok, Pid}.
test_sup.erl
-module(test_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
-export([start_child/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init(_Args) ->
SupFlags = #{strategy => simple_one_for_one, intensity => 2, period => 20},
ChildSpecs = [#{id => test,
start => {test, do_job, []},
restart => permanent,
shutdown => brutal_kill,
type => worker,
modules => [test]}],
{ok, {SupFlags, ChildSpecs}}.
start_child(Args) ->
supervisor:start_child(?MODULE, Args).
I start supervisor in shell by command test_sup:start_link(). After that i run this command: test:run(fun() -> erlang:throw(err) end). I except the function do_job restart 2times but it never does. What is the problem?
Here is shell:
1> test_sup:start_link().
{ok,<0.36.0>}
2> test:run(fun() -> erlang:throw(err) end).
started <0.38.0>
{ok,<0.38.0>}
3>
=ERROR REPORT==== 16-Dec-2016::22:08:41 ===
Error in process <0.38.0> with exit value:
{{nocatch,err},[{erlang,apply,2,[]}]}
Restarting children is contrary to how simple_one_for_one supervisors are defined. Per the supervisor docs:
Functions delete_child/2 and restart_child/2 are invalid for simple_one_for_one supervisors and return {error,simple_one_for_one} if the specified supervisor uses this restart strategy.
In other words, what you're asking for can never happen. That's because a simple_one_for_one is intended for dynamic children that are defined on the fly by passing in additional startup args when you request the child. Other supervisors are able to restart their children because the startup args are statically defined in the supervisor.
Basically, this type of supervisor is strictly for ensuring a tidy shutdown when you need to have a dynamic pool of workers.
I get the following error when I try to run my program through shell using erlang.mk.
=INFO REPORT==== 5-May-2016::05:47:57 ===
application: rad
exited: {bad_return,
{{rad_app,start,[normal,[]]},
{'EXIT',
{noproc,{gen_server,call,[rad_config,{lookup,port}]}}}}}
type: permanent
Eshell V6.4 (abort with ^G)
(rad#127.0.0.1)1> {"Kernel pid terminated",application_controller,"{application_start_failure,rad,{bad_return,{{rad_app,start,[normal,[]]},{'EXIT',{noproc,{gen_server,call,[rad_config,{lookup,port}]}}}}}}"}
Kernel pid terminated (application_controller) ({application_start_failure,rad,{bad_return,{{rad_app,start,[normal,[]]},{'EXIT',{noproc,{gen_server,call,[radheart: Thu May 5 05:47:58 2016: _coErlang is crashing .. (waiting for crash dump file)nf
ig,{lookup,porheart: Thu May 5 05:47:58 2016: tWould reboot. Terminating.}
]}}}}}})
make: *** [run] Error 1
rad.app.src
{application, rad,
[
{description, "Awesome server written in Erlang"},
{vsn, "0.0.1"},
{registered, [rad_sup, rad_config]},
{modules, []},
{applications, [
kernel,
stdlib,
cowboy
]},
{mod, {rad_app, []}},
{env, []}
]}.
rad_config.erl
-module(rad_config).
-behaviour(gen_server).
%% API
-export([start_link/0]).
%% Gen Server Callbacks
-export([init/1 , handle_call/3 , handle_cast/2 , handle_info/2 , terminate/2 , code_change/3]).
-export([lookup/1]).
-define(SERVER, ?MODULE).
-record(state, {conf}).
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
init([]) ->
{ok, Conf} = file:consult("config/rad.cfg"),
{ok, #state{conf = Conf}}.
handle_call({lookup, Tag} , _From , State) ->
Reply = case lists:keyfind(Tag, 1, State#state.conf) of
{Tag, Value} ->
Value;
false ->
{error, noinstance}
end,
{reply, Reply, State};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg , State) ->
{noreply, State}.
handle_info(_Info , State) ->
{noreply, State}.
terminate(Reason , _State) ->
io:format("~n Server shutdown. Reason: ~s.~n", [Reason]),
ok.
code_change(_OldVsn , State , _Extra) ->
{ok, State}.
lookup(Tag) ->
gen_server:call(?SERVER, {lookup, Tag}).
rad_app.erl
-module(rad_app).
-behaviour(application).
-export([start/2]).
-export([stop/1]).
%% First we need to define and compile the dispatch list, a list of routes
%% that Cowboy will use to map requests to handler modules. Then we tell
%% Cowboy to listen for connections.
start(_Type, _Args) ->
Port = rad_config:lookup(port),
Dispatch = cowboy_router:compile([
{'_', [{"/", route_handler, []}]}
]),
{ok, _} = cowboy:start_http(rad_http_listener, 100,
[{port, Port}], [{env, [{dispatch, Dispatch}]}]),
%% Start the supervisor
rad_sup:start_link().
stop(_State) ->
ok.
rad_sup.erl
-module(rad_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1, shutdown/0]).
-define(SERVER, ?MODULE).
%% Helper macro for declaring children of supervisor
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
%% ===================================================================
%% API functions
%% ===================================================================
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%% ===================================================================
%% Supervisor callbacks
%% ===================================================================
init([]) ->
RestartStrategy = simple_one_for_one,
MaxRestarts = 10,
MaxSecondsBwRestarts = 5,
SupFlag = {RestartStrategy, MaxRestarts, MaxSecondsBwRestarts},
Processes = [?CHILD(rad_config, worker)],
{ok, {SupFlag, Processes}}.
%% Supervisor can be shutdown by calling exit(SupPid,shutdown)
%% or, if it's linked to its parent, by parent calling exit/1.
shutdown() ->
exit(whereis(?MODULE), shutdown).
So basically I've two questions related to the error that is thrown here:
Is this error thrown because my gen_server is not able to start?
The line in rad_config corresponding to file:consult/1, I want to ask from where does this function fetches the file as in the parameter that I've passed to it is config/rad.cfg but all the .erl files are stored in src folder. And both these folders src and config are at the same directory level. So, the parameter that I've passed to file:consult/1, is it correct? Although I've tried to pass the parameter as ../config/rad.cfg also. I still get the same error.
Please help me out. I'm new to Erlang and I'm trying to solve this error for quite some time. Btw, I using Erlang 17.5.
First, it seems like when you run rad_app.erl your rad_config server is not yet started. so when your get to this line:
Port = rad_config:lookup(port)
You are actually calling:
lookup(Tag) ->
gen_server:call(?SERVER, {lookup, Tag}).
And the gen_server is not started so you are getting a noproc error.
In addition to this, even if the server was started already, you are not able to make a gen_server:call to your self. The best way to handle a case that you want to send yourself an event is to open a new process using spawn and from inside the spawned process make the call.
Your should read more about gen_server and OTP.