Im working on erlang for the first time. everytime i try to run the erlang process it gets stuck and does not take input. Im using erlide plugin in eclipse to test the erlang code.
CODE IS::
-module(message_router).
%% ====================================================================
%% API functions
%% ====================================================================
%%-compile(export_all).
-export([start/0]).
-export([stop/1]).
-export([send_chat_message/3]).
-export([route_messages/0]).
%% ====================================================================
%% Internal functions
%% ====================================================================
start() ->
spawn(message_router, route_messages, []).
stop(RouterPid) ->
RouterPid ! shutdown.
send_chat_message(RouterPid, Addressee, MessageBody) ->
io:format("send_chat_msg FROM:: ~p TO:: ~p ~n", [RouterPid, Addressee]),
RouterPid ! {send_chat_msg, Addressee, MessageBody}.
route_messages() ->
receive
{send_chat_msg, Addressee, MessageBody} ->
io:format("recv_chat_msg PID:: ~p ~n", [Addressee]),
Addressee ! {recv_chat_msg, MessageBody},
route_messages();
{recv_chat_msg, MessageBody} ->
io:format("Received: ~p~n", [MessageBody]);
shutdown ->
io:format("Shutting down ~n");
Oops ->
io:format("Warning! Received: ~p~n", [Oops]),
route_messages()
end.
When i hit try to run the code like in the shell
Eshell V5.10.4
(nodename#pa)1> P1 = message_router:start().
<0.1308.0>
(nodename#pa)2> P2 = message_router:start().
<0.1373.0>
(nodename#pa)3> chat_client:send_message(P1, P2, "FIRST Msg").
Sending chat message from chat_client
send_chat_msg FROM:: <0.1308.0> TO:: <0.1373.0>
Every thing i enter in the shell after this has on effect. Also could anyone explain how loops are handled in erlang and best practices.
[edit]
chat client code:
-module(chat_client).
-export([send_message/3]).
send_message(RouterPid, Addressee, MessageBody) ->
io:format("Sending chat message from chat_client~n"),
message_router:send_chat_message(RouterPid, Addressee, MessageBody).
Problem in module. Call this message_router:send_chat_message(P1, P2, "FIRST Msg")
Look at this example and compare with his:
Have module call observer it registers two process loop/0 and trans/0
-module(observer).
-export([observer/0, loop/0, trans/0]).
observer() ->
register(loop, spawn(fun observer:loop/0)),
register(trans, spawn(fun observer:trans/0)).
loop() ->
receive
{'PRINT', Msg} ->
io:format("print from ~p~n~p~n", [self(), Msg]), loop();
{'EXIT', _FromPid} ->
io:format("Exit ~nFrom ~p", [_FromPid]), exit(self(), kill)
end.
trans() ->
receive
{Command, Msg} -> io:format("transports from: ~p~n~p~n", [self(), Msg]),
loop ! {Command, Msg}
end.
and have module send_messenger one function send_message that sends messages through trans:
-module(send_messenger).
-export([send_message/1]).
send_message({Command, Msg}) ->
trans ! {Command, Msg},
ok.
I hope this has helped you!
Related
I am making an example when using gen_sctp create soscket and associates in server-client model.
In server side:
{ok,serverSocket} = gen_sctp:open(1234,[{ip,{127,0,0,1}},{reuseaddr,true},{active,true}]).
ok = gen_sctp:listen(S,true).
In client side:
{ok,Client} = gen_sctp:open(1243,[{ip,{127,0,0,1}},{reuseaddr,true}]).
{ok,Ass} = gen_sctp:connect(S,{127,0,0,1},1234,[{active,true}]).
And then client send message to server by send/4:
gen_sctp:send(S,Ass,2,<<"hellooooo">>).
And message receive in server side:
{sctp,#Port<0.6126>,
{127,0,0,1},
1243,
{[{sctp_sndrcvinfo,2,1,[],0,0,0,1409953138,0,18}],
<<"hellooooo">>}}
So how can server can reply message to client by send/3?
Thanks and Best Regards,
Tran.
gen_sctp:send/3 is like gen_sctp:send/4 but you can set more flags and options. You have already used gen_sctp:send/4 in client code (while you messed around with client and server sockets):
{ok, Assoc} = gen_sctp:connect(ClientSocket, {127,0,0,1}, 1234,[{active,true}]).
gen_sctp:send(ClientSocket, Assoc, 2, <<"hellooooo">>).
And Assoc is of sctp_assoc_change record type while gen_sctp:send/4 looks just for assoc_id if you provide sctp_assoc_change. So currently providing #sctp_assoc_change{} or just association id behave exactly same.
And how one can find out association id of client in server? It's provided in message which server received:
{sctp,#Port<0.6126>,
{127,0,0,1},
1243,
{[{sctp_sndrcvinfo,2,1,[],0,0,0,1409953138,0,18}],
<<"hellooooo">>}}
#sctp_sdnrcvinfo{} record has fields telling association id and stream number which data is received from. You can get current association id from assoc_id field and pass it to another gen_sctp:send/4:
gen_sctp:send(ServerSocket, AssocID, 2, <<"welcome!">>).
Stream number of 2 probably won't make it fail because by default gen_sctp:open makes 10 incoming and outgoing streams, but you can safely provide 0 as stream number.
Here is an example of sending and receiving data with sctp:
#!/usr/bin/escript
-include_lib("kernel/include/inet_sctp.hrl").
server_loop(Socket) ->
receive
{sctp, Socket, _FromIP, _FromPort, {[#sctp_sndrcvinfo{assoc_id=AssocID}],
Payload}} ->
gen_sctp:send(Socket, #sctp_sndrcvinfo{assoc_id=AssocID, stream=0},
<<"pong">>),
% or less complex gen_sctp:send/4
gen_sctp:send(Socket, AssocID, 0, <<"pong">>);
Rest ->
io:format("server got unhandled message ~w~n", [Rest])
end,
server_loop(Socket).
create_server_socket() ->
{ok, Socket} = gen_sctp:open(1234, [{ip,{127,0,0,1}}, {reuseaddr,true},
{active,true}]),
gen_sctp:listen(Socket, true),
{ok, Socket}.
run_server() ->
Spawner = self(),
spawn_link(fun() ->
{ok, Socket} = create_server_socket(), Spawner ! ready, server_loop(Socket)
end),
receive
ready ->
io:format("server is up~n"),
ok
after 100 ->
throw(server_timeout)
end.
ping_server() ->
{ok, Socket} = gen_sctp:open(1243, [{ip,{127,0,0,1}}, {reuseaddr, true}]),
{ok, AssocChange} = gen_sctp:connect(Socket, {127,0,0,1}, 1234, [{active, true}]),
gen_sctp:send(Socket, AssocChange, 2, <<"ping">>),
receive
{sctp, Socket, _FromIP, _FromPort, {[#sctp_sndrcvinfo{}], Payload}} ->
io:format("client got payload ~p~n", [Payload])
after 2000 ->
throw(client_timeout)
end.
main([]) ->
run_server(),
ping_server().
Writing a very simple TCP server for starters, but for the life of me, gen_tcp:accept in my 'accept_loop' function keeps returning {error, closed} immediately when it's called. So far I've been able to get everything else working thus far, but this one is really stumped me. Wondering if I fell into a beginner trap.
Anyway here is the code.
-module(doom_server).
-behaviour(gen_server).
-export([start_link/1, handle_client/1, accept_loop/1, accept/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(TCP_OPTIONS, ).
-record(server_state, {
port,
ip=any,
timeout=10,
lsocket=null}).
start_link(_Args) ->
gen_server:start_link(?MODULE, _Args, []).
init(_Args) ->
% Get configuration from doom.config and create a new server state.
Port = proplists:get_value(port, _Args),
BindAddress = proplists:get_value(bind_address, _Args),
Timeout = proplists:get_value(timeout, _Args),
case gen_tcp:listen(Port, [binary,
{packet, 0},
{active, false},
{reuseaddr, true}]) of
{ok, ListenSocket} ->
% Construct our server state.
NewState = #server_state {
lsocket = ListenSocket,
port = Port,
ip=BindAddress,
timeout=Timeout
},
{ok, accept(NewState)};
{error, Reason} ->
io:format("Failed: ~w~n", [Reason]),
{stop, Reason}
end.
handle_cast({accepted, _Pid}, State=#server_state{}) ->
{noreply, accept(State)}.
accept_loop({Server, ListenSocket, Timeout}) ->
case gen_tcp:accept(ListenSocket, Timeout) of
{ok, Socket} ->
% Let the server spawn a new process and replace this loop
gen_server:cast(Server, {accepted, self()}),
handle_client(Socket);
{error, Type} ->
io:format("Error: ~w~n", [Type])
end.
% To be more robust we should be using spawn_link and trapping exits
accept(State = #server_state{lsocket=ListenSocket, timeout=Timeout}) ->
proc_lib:spawn(?MODULE, accept_loop, [{self(), ListenSocket, Timeout}]),
State.
handle_client(Socket)->
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
gen_tcp:send(Socket, Data),
handle_client(Socket);
{error, closed} ->
ok
end.
% We get compile warnings from gen_server unless we define these
handle_call(_Msg, _Caller, State) -> {noreply, State}.
handle_info(_Message, Library) -> {noreply, Library}.
terminate(_Reason, _Library) -> ok.
code_change(_OldVersion, Library, _Extra) -> {ok, Library}.
I've ran erlang:port_info(ListenSocket) just after the gen_tcp:listen and inside the accept_loop. Both output the same data, and if I put gen_tcp:accept inside the main thread or right after the gen_tcp:listen it works as expected. Am I doing something wrong when I spawn a new process?
P.S I'm using this tutorial as point of reference: http://20bits.com/article/erlang-a-generalized-tcp-server
I am following the book "Learn me some Erlang". I have installed eclipse on my windows machine in order to run the scripts, and I came across an error on runtime:
** exception error: bad argument
Apparently this error is caused by nodes having the same name on repeated restarts within the judge2 function, third line.
However, I am not sure how to correct the problem.
Here is my code:
start_critic2() ->
spawn(?MODULE, restarter, []).
restarter() ->
process_flag(trap_exit, true),
Pid = spawn_link(?MODULE, critic, []),
receive
{'EXIT', Pid, normal} -> % not a crash
ok;
{'EXIT', Pid, shutdown} -> % manual termination, not a crash
ok;
{'EXIT', Pid, _} ->
restarter()
end.
judge2(Band, Album) ->
Ref = make_ref(),
critic ! {self(), Ref, {Band, Album}},
receive
{Ref, Criticism} -> Criticism
after 2000 ->
timeout
end.
critic2() ->
receive
{From, Ref, {"Rage Against the Turing Machine", "Unit Testify"}} ->
From ! {Ref, "They are great!"};
{From, Ref, {"System of a Downtime", "Memoize"}} ->
From ! {Ref, "They're not Johnny Crash but they're good."};
{From, Ref, {"Johnny Crash", "The Token Ring of Fire"}} ->
From ! {Ref, "Simply incredible."};
{From, Ref, {_Band, _Album}} ->
From ! {Ref, "They are terrible!"}
end,
critic2().
I Wrote this module for a simple server program that receives a request and send a response to client. It works fine But when if recompile this module while a client connected, the client disconnects and if i reconnect client to this server and send a request, there is no response. Is there any idea?!
Here is my module:
-module(controller).
-export([start/1, loop/1, response/2]).
-include_lib("types.hrl").
-define(END_CHAR, "$").
-spec start(Port) -> no_return() when
Port :: char().
-spec loop(Listen) -> no_return() when
Listen :: port().
-spec handler(Socket) -> no_return() when
Socket :: port().
-spec response(HandlerPID, Data) -> {send_msg, Msg} when
HandlerPID :: pid(),
Data :: string(),
Msg :: response().
start(Port) ->
{ok, Listen} = gen_tcp:listen(Port, [{active, once}]),
spawn(?MODULE, loop, [Listen]).
loop(Listen) ->
{ok, Socket} = gen_tcp:accept(Listen),
spawn(?MODULE, loop, [Listen]),
handler(Socket).
handler(Socket) ->
receive
{tcp, Socket, Data} ->
%io:format("recv ~p~n", [Data]),
spawn(?MODULE, response, [self(), Data]),
inet:setopts(Socket, [{active, once}]),
handler(Socket);
{tcp_closed, Socket} ->
%io:format("disconnected~n", []),
gen_tcp:close(Socket);
{send_msg, Msg} ->
gen_tcp:send(Socket, lists:flatten(io_lib:format("~p", [Msg])) ++ ?END_CHAR),
handler(Socket)
end.
response(PID, Data) ->
[Req|Args] = string:tokens(Data, ?END_CHAR),
{ReqPID, ReqRef} = spawn_monitor(view, request, [list_to_atom(Req), self(), Args]),
receive
{'DOWN', ReqRef, process, ReqPID, {function_clause, _}} -> PID ! {send_msg, invalid_request};
{'DOWN', ReqRef, process, ReqPID, {{case_clause, _}, _}} -> PID ! {send_msg, bad_args};
{'DOWN', ReqRef, process, ReqPID, {{badmatch, _}, _}} -> PID ! {send_msg, bad_args};
Resp -> PID ! {send_msg, Resp}
end.
Note
{tcp_closed, Socket} ->
%io:format("disconnected~n", []),
gen_tcp:close(Socket);
in handler. It doesn't call handler again, it just returns and so the server process stops.
Your loop function name is also misleading, handler is the actual loop.
I would recommend to take a look at Ranch - A socket acceptor pool for the TCP connections.
i use ranch to listen socket, but in a short time about five seconds, ranch closed the socket, and my setting of socket is above, so what' wrong?
{ok, _} = ranch:start_listener(server,200, ranch_tcp, [{port, 5555},{active, once}, {max_connections, 1024}], server_protocol, []), %% start the listener
the protocol file is below, the ranch listen to accept a socket, and the reverse the receive data, but what's wrong is that, when send data back to the client, after about five seconds, the client receive the message says that the socket is closed by server, i don't know if is ranch's default settings cause this?
-module(reverse_protocol).
-behaviour(gen_server).
-behaviour(ranch_protocol).
%% API.
-export([start_link/4]).
%% gen_server.
-export([init/1]).
-export([init/4]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).
-define(TIMEOUT, 5000).
-record(state, {socket, transport}).
%% API.
start_link(Ref, Socket, Transport, Opts) ->
proc_lib:start_link(?MODULE, init, [Ref, Socket, Transport, Opts]).
%% gen_server.
%% This function is never called. We only define it so that
%% we can use the -behaviour(gen_server) attribute.
init([]) -> {ok, undefined}.
init(Ref, Socket, Transport, _Opts = []) ->
ok = proc_lib:init_ack({ok, self()}),
ok = ranch:accept_ack(Ref),
ok = Transport:setopts(Socket, [{active, once}]),
gen_server:enter_loop(?MODULE, [],
#state{socket=Socket, transport=Transport},
?TIMEOUT).
handle_info({tcp, Socket, Data}, State=#state{
socket=Socket, transport=Transport}) ->
Transport:setopts(Socket, [{active, once}]),
Transport:send(Socket, reverse_binary(Data)),
{noreply, State, ?TIMEOUT};
handle_info({tcp_closed, _Socket}, State) ->
{stop, normal, State};
handle_info({tcp_error, _, Reason}, State) ->
{stop, Reason, State};
handle_info(timeout, State) ->
{stop, normal, State};
handle_info(_Info, State) ->
{stop, normal, State}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% Internal.
reverse_binary(B) when is_binary(B) ->
[list_to_binary(lists:reverse(binary_to_list(
binary:part(B, {0, byte_size(B)-2})
))), "\r\n"].
So your problem is because your gen_server process is timing out and shutting down. The socket being closed is a side effect of this because ranch links the Socket to the spawned handler process.
Once the new process enters the gen_server loop with the call to gen_server:enter_loop, it has ?TIMEOUT milliseconds to receive a message before it is sent a timeout message.
-define(TIMEOUT, 5000).
init(Ref, Socket, Transport, _Opts = []) ->
ok = proc_lib:init_ack({ok, self()}),
ok = ranch:accept_ack(Ref),
ok = Transport:setopts(Socket, [{active, once}]),
gen_server:enter_loop(?MODULE, [],
#state{socket=Socket, transport=Transport},
?TIMEOUT). %% timeout because of this!
handle_info({tcp, Socket, Data}, State=#state{
socket=Socket, transport=Transport}) ->
Transport:setopts(Socket, [{active, once}]),
Transport:send(Socket, reverse_binary(Data)),
{noreply, State, ?TIMEOUT}; %% timeout because of this!
So when those five second pass and the gen_server hasn't received any messages in that time it sends itself a timeout message, which is then handled by handle_info
handle_info(timeout, State) ->
{stop, normal, State};
Your handle_info tells the gen_server to stop, which causes the Socket to close because the two are linked together.
You can either remove the timeouts completely, or just stop the timeout from causing the process to close.
Here is how I would change the handle_info timeout code:
handle_info(timeout, State) ->
io:format("the socket is idle~n"),
{noreply,State};