I can't get multiple clients to communicate with my concurrent echo server. Here are my results:
Server Terminal window:
1> c(s).
{ok,s}
2> s:init(15555).
Server started on port: 15555
Client Terminal window:
1> c(cl).
{ok,cl}
2> cl:test().
[<0.64.0>,<0.65.0>,<0.66.0>,<0.67.0>,<0.68.0>]
3>
=ERROR REPORT==== 21-May-2017::21:21:39 ===
Error in process <0.68.0> with exit value:
{{badmatch,{error,econnrefused}},[{cl,client,2,[{file,"cl.erl"},{line,5}]}]}
=ERROR REPORT==== 21-May-2017::21:21:39 ===
Error in process <0.67.0> with exit value:
{{badmatch,{error,econnrefused}},[{cl,client,2,[{file,"cl.erl"},{line,5}]}]}
...
...
In the shell, if I start my server, then connect one client, my client and server can communicate back and forth without error:
Server Terminal window:
1> c(s).
{ok,s}
2> s:init(15555).
Server started on port: 15555
Client Terminal window:
1> c(cl).
{ok,cl}
2> cl:client(1, 15555).
Client1: sent -->hello
Client1: received <--hello
ok
In my failing concurrent test, I do this:
test() ->
Port = 1555,
[spawn(?MODULE, client, [Id, Port]) || Id <- lists:seq(1, 5)].
Here's my echo server:
-module(s).
-compile(export_all).
init(Port) ->
{ok, ServerSocket} = gen_tcp:listen(Port, [binary, {active,true},
{packet,4}, {reuseaddr,true}] ),
io:format("Server started on port: ~w~n", [Port]),
server(ServerSocket).
server(ServerSocket) ->
{ok, ClientSocket} = gen_tcp:accept(ServerSocket),
spawn(?MODULE, server, [ServerSocket]),
timer:sleep(rand:uniform(3) * 1000),
loop(ClientSocket).
loop(ClientSocket) ->
receive
{tcp, ClientSocket, CompleteMsg} ->
io:format("Server: received ~s~n", [CompleteMsg]),
gen_tcp:send(ClientSocket, CompleteMsg);
{tcp_closed, ClientSocket} ->
io:format("Server: client closed socket.~n")
end,
loop(ClientSocket).
Here's my client:
-module(cl).
-compile(export_all).
client(Id, Port) ->
{ok, Socket} = gen_tcp:connect(localhost, Port, [binary, {active,true},
{packet,4},
{reuseaddr, true}] ),
Msg = "hello",
gen_tcp:send(Socket, Msg),
io:format("Client~w: sent -->~s~n", [Id, Msg]),
receive
{tcp, Socket, CompleteMsg} ->
io:format("Client~w: received <--~s~n", [Id, CompleteMsg]);
{tcp_closed, Socket} ->
io:format("Server closed the socket.~n")
end,
gen_tcp:close(Socket).
test() ->
Port = 1555,
[spawn(?MODULE, client, [Id, Port]) || Id <- lists:seq(1, 5)].
It's my understanding that a TCP socket is nothing but a tuple containing four numbers:
{senderPort, senderAddress, destinationPort, destinationAddress}
I think that each of my clients must have the same senderPort and senderAddress, therefore they are all trying to use the same TCP socket. But I would think that one of my clients would be able to successfully establish a TCP connection while the rest got a connection refused error, yet all of my concurrent clients get a connection refused error.
It seems to me that gen_tcp:connect() somehow picks a senderPort and retrieves the senderAddress, which along with the ServerAddress and ServerPort arguments, defines the socket. How can I make each of my clients use a different senderPort so that each client creates a unique socket?
Is there a way to programmatically test a concurrent server with multiple clients on localhost?
Related
I am stuck in a bit of a fix trying to run gen_server on another node. So I have a common gen_server class which looks like this
start(FileName) ->
start_link(node(), FileName).
start_link(ThisNode, FileName) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [ThisNode, FileName], []).
init([ThisNode, FileName]) ->
process_flag(trap_exit, true),
{ok, Terms} = file:consult(FileName),
{A1, B1, C1} = lists:nth(1,Terms),
place_objects(A1, B1, C1).
Now I want to start multiple nodes that will run the same gen_server and somehow communicate with each other, and use a another node to orchestrate that. (All these nodes are started on my local terminal).
So I start a new node in one terminal using erl -sname bar where I intend to run the gen_server, and compile the gen_server module on this node. Then I start another node called 'sup' which I intend to use as a coordinator for all the other nodes. If I run the command my_gen_server:start("config_bar.txt"). on bar, it successfully returns but when I run the command rpc:call('bar#My-MacBook-Pro', my_gen_server, start, ["config_bar.txt"]). on sup, it successfully returns from the init method (I checked this by putting in the logs) but immediately after that, I get this error:
{ok,<9098.166.0>}
(sup#My-MacBook-Pro)2> =ERROR REPORT==== 21-Feb-2022::11:12:30.443051 ===
** Generic server my_gen_server terminating
** Last message in was {'EXIT',<9098.165.0>,
{#Ref<0.3564861827.2990800899.137513>,return,
{ok,<9098.166.0>}}}
** When Server state == {10,10,#Ref<9098.1313723616.3973185546.82660>,
'bar#My-MacBook-Pro'}
** Reason for termination ==
** {#Ref<0.3564861827.2990800899.137513>,return,{ok,<9098.166.0>}}
=CRASH REPORT==== 21-Feb-2022::11:12:30.443074 ===
crasher:
initial call: my_gen_server:init/1
pid: <9098.166.0>
registered_name: my_gen_server
exception exit: {#Ref<0.3564861827.2990800899.137513>,return,
{ok,<9098.166.0>}}
in function gen_server:decode_msg/9 (gen_server.erl, line 481)
ancestors: [<9098.165.0>]
message_queue_len: 0
messages: []
links: []
dictionary: []
trap_exit: true
status: running
heap_size: 1598
stack_size: 29
reductions: 3483
neighbours:
I can't seem to figure out what causes the error and if there's anything I need to add to my gen_server code to fix it. Would really appreciate some help on this one!
The gen_server in the remote node is linked to an ephemeral process created for the rpc call.
As this ephemeral process exits with a term that's different from normal (the actual result of the rpc call), the exit signal propagates to the gen_server, killing it.
You can use gen_server:start instead of gen_server:start_link or, if you want the gen_server to be part of the supervission tree, instruct its supervisor to spawn it:
rpc:call('bar#My-MacBook-Pro', my_gen_sup, start_child, ["config_bar.txt"]).
tried to integrate the Aerospike with Ejabberd, and after little bit of struggle and I had hit another issue which says
" DEBUG:[util_extract_common_lead_parms()]:Connection Handle is bad C(0) DEBUG:[util_extract_common_lead_parms()]:Connection Handle is bad C(32680) " I made the connection the following way,
-behaviour(gen_mod).
-define(CONN_NAME, 'ejabberd_aerospike_client').
init()->
Host = "127.0.0.1",
Port = 3000,
case aerospike:connect( Host, Port ) of
{ok, C } ->
register(?CONN_NAME,C),
ok;
{error, _} = Err ->
?ERROR_MSG("failed to start aerospike client: ~p", [Err]),
Err
end.
I try to start Inets httpd server and have some documents in the document_root, but I can not start up the server. How can I start it?
This is the config I start it with:
>{ok, Pid} = inets:start(httpd, [{port, 8070}, {server_name, "myhttpd"},
>{server_root, "/Users/jonas/code"},
>{document_root, "/Users/jonas/code/mydocs"},
>{bind_address, "localhost"}]).
** exception error: no match of right hand side value {error, inets_not_started}
You should first call inets:start/0.
I'm trying to create two instances of webmachine in one erlang application. Each instance is to run on a different port and has its own specific configuration. Following the webmachine doc here, I have added the following processes to be started in my supervisor spec (application_sup.erl):
{
webmachine_instance_1,
{ webmachine_mochiweb, start,
[
[
{ ip, "0.0.0.0"},
{ port, 8000},
{ dispatch, [ {["*"], file_resource, []} ] }
]
]
},
permanent,
5000,
worker,
dynamic
},
{
webmachine_instance_2,
{ webmachine_mochiweb, start,
[
[
{ ip, "0.0.0.0"},
{ port, 8080},
{ dispatch, [ {["*"], file_resource, []} ] }
]
]
},
permanent,
5000,
worker,
dynamic
}
When I include both instances, I get a start error and cannot start my erlang application. After just trying to run the application with a single instance of webmachine (webmachine_instance_1 OR webmachine_instance_2), my application starts up fine.
Here is the specific error:
=PROGRESS REPORT==== 11-Mar-2014::17:00:31 ===
supervisor: {local, application_sup}
started: [{pid,<0.230.0>},
{name,webmachine_instance_1},
{mfargs,
{webmachine_mochiweb,start,
[[{ip,"0.0.0.0"},
{port,8000},
{dispatch, [{['*'],
file_resource,
[]
}]}]
]
}
},
{restart_type,permanent},
{shutdown,5000},
{child_type,worker}]
=SUPERVISOR REPORT==== 11-Mar-2014::17:00:31 ===
Supervisor: {local, application_sup}
Context: start_error
Reason: {'EXIT',
{undef,
[{webmachine_mochiweb,start,
[{ip,"0.0.0.0"},
{port,8080},
{dispatch,[{["*"],file_resource,[]}]}],
[]},
{supervisor,do_start_child,2,
[{file,"supervisor.erl"},{line,303}]},
{supervisor,start_children,3,
[{file,"supervisor.erl"},{line,287}]},
{supervisor,init_children,2,
[{file,"supervisor.erl"},{line,253}]},
{gen_server,init_it,6,
[{file,"gen_server.erl"},{line,304}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]}}
Offender: [{pid,undefined},
{name,webmachine_instance_2},
{mfargs,
{webmachine_mochiweb,start,
[{ip,"0.0.0.0"},
{port,8080},
{dispatch,[{["*"],file_resource,[]}]}]}},
{restart_type,permanent},
{shutdown,5000},
{child_type,worker}]
I am fairly new to erlang and might not quite understand the underlying issues here - according to the webmachine doc we should be able to start two instances of the same application but with different configurations in an erlang app.
Thanks for any help/discussion on this issue!
Your child process config should have the following form:
{Name,
{webmachine_mochiweb, start, [WebConfig]},
permanent, 5000, worker, [mochiweb_socket_server]}
where Name and WebConfig must be unique to your webmachine instance. The WebConfig part should include name and dispatch_group properties. For example:
WebConfig = [{name,instance1},
{dispatch_group,instance1},
{ip, Ip},
{port, Port},
{log_dir, "priv/log"},
{dispatch, Dispatch}],
So, for multiple instances you might have something like this for your supervisor spec:
WebConfig1 = [{name,instance1},
{dispatch_group,instance1},
{ip, Ip},
{port, Port},
{log_dir, "priv/log"},
{dispatch, Dispatch}],
WebConfig2 = [{name,instance2},
{dispatch_group,instance2},
{ip, Ip},
{port, Port+1},
{log_dir, "priv/log"},
{dispatch, Dispatch}],
Web1 = {instance1,
{webmachine_mochiweb, start, [WebConfig1]},
permanent, 5000, worker, [mochiweb_socket_server]},
Web2 = {instance2,
{webmachine_mochiweb, start, [WebConfig2]},
permanent, 5000, worker, [mochiweb_socket_server]},
Processes = [Web1, Web2],
{ok, { {one_for_one, 10, 10}, Processes} }.
One other thing: judging from the name application_sup appearing in your question, you may have run the webmachine ./scripts/new_webmachine.sh and specified the application name as application. If so, don't do this, since application is the name of a key Erlang OTP module, and your code will clash with it and cause all kinds of problems.
I wrote a simple application in Erlang, but it refuses to work with the following error:
=SUPERVISOR REPORT==== 18-Jan-2012::15:03:27 ===
Supervisor: {<0.60.0>,my_sup}
Context: start_error
Reason: {'EXIT',{undef,[{my,start,[{8077,none}]},
{supervisor,do_start_child,2},
{supervisor,start_children,3},
{supervisor,init_children,2},
{gen_server,init_it,6},
{proc_lib,init_p_do_apply,3}]}}
Offender: [{pid,undefined},
{name,my},
{mfa,{my,start,[{8077,none}]}},
{restart_type,permanent},
{shutdown,brutal_kill},
{child_type,worker}]
=INFO REPORT==== 18-Jan-2012::15:03:27 ===
application: my
exited: {shutdown,{my_app,start,[normal,[noarg]]}}
type: temporary {error,{shutdown,{my_app,start,[normal,[noarg]]}}}
And the modules:
my.erl
-module(my).
-export([start/2, stop/0]).
start(Port,_arg) ->
io:format("starting my").
stop() ->
ok.
my_app.erl Application module, perform application behavior.
-module(my_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_Type, _Args) ->
io:format("my server starting~n"),
my_sup:start_link().
stop(_State) ->
io:format("my server terminating~n"),
ok.
my_sup.erl Supervisor logic
-module(my_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
start_link() ->
supervisor:start_link(my_sup, []).
init(_Args) ->
{ok, {
{one_for_one, 10, 60},
[{my, {my, start, [{8077,none}]
},
permanent, brutal_kill, worker, [my]}]}}.
Config file (my.app):
{application, my,
[
{description, "Demo"},
{vsn, "1.0"},
{id, "hello"},
{modules, [my,my_sup]},
{registered, [my,my_sup]},
{applications, [kernel, stdlib]},
%%
%% mod: Specify the module name to start the application, plus args
%%
{mod, {my_app, [noarg]}},
{env, []}
]
}.
I changed child spec like you recommend, but the problem still remains.
=SUPERVISOR REPORT==== 19-Jan-2012::00:34:21 ===
Supervisor: {<0.96.0>,my_sup}
Context: start_error
Reason: <0.97.0>
Offender: [{pid,undefined},
{name,my},
{mfa,{my,start,[8077,none]}},
{restart_type,permanent},
{shutdown,brutal_kill},
{child_type,worker}]
=ERROR REPORT==== 19-Jan-2012::00:34:21 ===
Error in process <0.97.0> with exit value:
{{badmatch,{error,eaddrinuse}},[{my,'- start/2-fun-0-',1}]}
=INFO REPORT==== 19-Jan-2012::00:34:21 ===
application: my
exited: {shutdown,{my_app,start,[normal,[noarg]]}}
type: temporary
{error,{shutdown,{my_app,start,[normal,[noarg]]}}}
The my:start/2 should take two arguments but in the child specification you only give it one argument ({8077,none}). You could change the child spec to:
{my, {my, start, [8077,none]}, permanent, brutal_kill, worker, [my]}
As an aside your indentation and line breaking of the Supervisor Specification is a bit off making it difficult to see what belongs to what.
EDIT: Comment to new error
This is not the same problem as before. You are getting a new error, eaddrinuse, which normally means you are trying to use an IP address/port which is already in use. This would imply you are doing some socket programming in your code.
Your supervisor specifies that my:start should take one argument, but it takes two. You probably want to change the child spec to:
{one_for_one, 10, 60},[{my, {my, start, [8077,none]}
Your my:start/2 provides a wrong return value.
Supervisor expects tuple {ok,Pid}, while your function simply returns ok (the last call is to io:format, which returns ok, so your function also does).
If you want to use a supervisor, you must spawn a new process and link it to the supervisor. For example
start(_) -> {ok,spawn_link(fun() -> io:format("~w started~n",[self()]),timer:sleep(5000),io:format("~w exiting~n",[self()]) end)}.
should do the job for you.