gen_server, gen_tcp question
Kent Boortz
kent@REDACTED
Wed Jul 17 09:46:42 CEST 2002
Eric Merritt <cyberlync@REDACTED> writes:
> As some of you may know I am fairly new to erlang. I
> have been poking around a bit getting my feet wet,
> messing with yaws, mnesia, etc. Well I am finally
> getting around to coding a fairly heft application as
> a learning exercise and I have hit a stumbling block.
> I am just begining to write the tcp comm part of the
> application, I would love to follow the otp practices
> and make use of gen_server. There even seems to be
> some arcane way for gen_tcp and gen_server to work
> together for a module. I can't, for the life of me,
> figure it out. The documentation is pretty sparse in
> this area as well.
If you talk about creating an application that listen to a port and
accept an connection then I think they don't fit together as well as
they should do. Some OTP applications bypass the gen_server module and
use the sys module to use gen_tcp and still fit into the supervision
model of Erlang/OTP. You can look at the source of ssl or the P9
rshell application for an example of this. There is an ascii drawing
in "$ERL_TOP/lib/rshell/src/rshell.erl" that show the supervisor
structure.
One problem is that we can't sit and wait for a connection in the
blocking call gen_tcp:accept() because the application may get a code
change system message. One way around this is to give a timeout of one
second to gen_tcp:accept() to leave the call and pick up system
messages if any and redo the accept call.
A second problem that gen_tcp solves in an unusual way (a bit ugly in
my opinion) is that after an accept of a connection in active mode
messages with TCP data may be received to the message queue of the
process calling gen_tcp:accept() before it has started a process to
handle the specific connection. There is a function
gen_tcp:controlling_process() that redirect these messages to a new
process and, from what I have been told, actually move the TCP
messages from the message queue of the first process to the new
controlling process,
kent
init(Parent, Options) ->
process_flag(trap_exit, true),
Deb = sys:debug_options(?DBG_OPTS ++ Options),
Port = get_port(Options),
AcceptTimeout =
case application:get_env(rshell, accept_timeout) of
{ok, AcceptT} ->
AcceptT;
undefined ->
1000 % XXX: Create a macro!!!
end,
Opts =
[
{nodelay, true},
{packet,raw},
{reuseaddr,true}
],
% We crash if we can't open the socket to listen
{ok, ListenSocket} = gen_tcp:listen(Port, Opts),
loop(Parent, Deb, ListenSocket, AcceptTimeout).
loop(Parent, Deb, ListenSocket, AcceptTimeout) ->
receive
{system, From, Request} ->
State = {ListenSocket, AcceptTimeout},
sys:handle_system_msg(Request, From, Parent, ?MODULE, Deb, State);
What ->
NewDeb = sys:handle_debug(Deb, {?MODULE, write_debug}, rshell_listener,
{in, What}),
loop(Parent, NewDeb, ListenSocket, AcceptTimeout)
case gen_tcp:accept(ListenSocket, AcceptTimeout) of
{ok, Sock} ->
case supervisor:start_child(rshell_connection_sup,
[Sock]) of
{ok,Pid} when pid(Pid) ->
gen_tcp:controlling_process(Sock, Pid),
loop(Parent, Deb, ListenSocket, AcceptTimeout);
Other ->
NewDeb = sys:handle_debug(Deb,
{?MODULE, write_debug},
rshell_listener,
{start_child_result,
Other}),
gen_tcp:close(Sock),
loop(Parent, NewDeb, ListenSocket, AcceptTimeout)
end;
{error, timeout} ->
%% This is what we want to enable system events
loop(Parent, Deb, ListenSocket, AcceptTimeout);
Other ->
NewDeb = sys:handle_debug(Deb,
{?MODULE, write_debug},
rshell_listener,
{accept_result,Other}),
loop(Parent, NewDeb, ListenSocket, AcceptTimeout)
end
end.
More information about the erlang-questions
mailing list