[erlang-questions] Writing gen_servers with TCP listeners

Eric Maland <>
Mon May 12 02:15:50 CEST 2008


Thanks, this and the oserl code was good reading and extremely  
helpful.  Serge's tutorial is a bit terrifying given the use of  
prim_inet, though.

With your help I did find the solution and it had nothing to do  
(directly) with the relationship between the listening and accepting  
processes.

Here is what my start() looked like (in module 'sissie'):

start() ->
     sissie_db:start(),
     sissie_supervisor:start_link(1042, fun session_manager/1),

This worked fine from the erl console but when run with:

     erl -boot start_sasl -s sissie start

The socket closed immediately, and all the accept()ors failed.

I had made the assumption that since the supervisor starts my  
gen_server and the gen_server creates the listener, as well as spawns  
the accept()ors, that my gen_server's lifetime was what was important  
to the listening socket's longevity.

It turns out my parent module, 'sissie' was the important process due  
to its linkage to the supervisor.  When starting the server from the  
command line rather than the shell, 'sissie' would exit and that exit  
would chain down to the supervisor, its gen_servers, etc.

Any of these changes 'fixes' the problem:

start() ->
     sissie_db:start(),
     sissie_supervisor:start_link(1042, fun session_manager/1),
     receive after infinity -> true end. %%

start() ->
     sissie_db:start(),
     spawn(fun() ->
                   sissie_supervisor:start_link(1042, fun  
session_manager/1),
                   receive after infinity -> true end
           end).

start() ->
     sissie_db:start(),
     {ok, Pid} = sissie_supervisor:start_link(1042, fun  
session_manager/1),
     unlink(Pid).

The latter seems the most explicit, but I have to ask myself if  
there's not a better/prescribed method of starting a system like this  
other than what I'm doing.  I went through Joe Armstrong's book a  
little and found at least one example similar to the above, where the  
'starter' is unlinked from the supervisor at startup, so perhaps this  
is just normal and something that I must get used to.

Clearly I am new to Erlang and OTP.  Thanks again for your help.

Cheers
Eric

On May 11, 2008, at 3:48 PM, Dave Peticolas wrote:

> Eric Maland wrote:
>> Hello,
>> I am having some trouble writing a gen_server that is a tcp  
>> listener  that works outside the shell.
>> My goal has been to write a gen_server that starts listening on a   
>> socket and manages a connection pool of accept()-ors.  This seems  
>> like  a pretty standard behaviour and is very easy to implement,  
>> but every  implementation I've produced runs perfectly in the shell  
>> but not in an  erl instance without a shell.
>> This sort of problem seems to be referenced in a previous thread (http://www.erlang.org/pipermail/erlang-questions/2008-January/032400.html 
>>    ), but I have found 0 examples of how to apply the information  
>> there  to a gen_server - clearly the server needs to live forever,  
>> but how do  you get a gen_server to hang around?  Is it acceptable  
>> to implement a  handle_call() that just does a receive after  
>> infinity -> foo end ???   Who would call that?  I have tried that,  
>> and it seemed like after 10  or so requests the server died  
>> (presumably on some timeout from the  supervisor insantiating it??).
>> I found a great example of a gen_server tcp listener at http://www.duomark.com/erlang/tutorials/proxy.html 
>>     but this also exhibits the same problem outside the shell - it   
>> serves one request and then the socket is closed.
>> Does anyone have or know of a great tcp listener example that  
>> works  outside the shell (eg started with: 'erl -boot start_sasl -s  
>> mymodule  start'), or another interesting way to resolve this,  
>> specifically when  writing  gen_server?
>> Here are some models I have gone through, all of which work in the   
>> shell but not outside the shell:
>> gen_server:init listen()'s ->
>>   spawn()'s a connection pool ->
>>     each pool process accept()'s and spawns another pool process
>> In the shell: works fine
>> Outside the shell: 2nd and subsequent accept() fails
>> gen_server:init listen()'s ->
>>   gen_server:init calls gen_server:cast(accept_loop) ->
>>     accept_loop loops forever, spawning handlers on each request  
>> (and  gen_tcp:controlling_process the associated connections over  
>> to the  processes)
>> In the shell: works fine
>> Outside the shell: 2nd and subsequent accept() fails
>> These all work great in the shell but serve 1-2 requests (depending  
>> on  your platform) outside the shell before the socket closes.
>> Any advice on how to properly get the owner of the listening socket  
>> to  stick around in a gen_server would be appreciated.
>
> A gen_server will 'hang around' indefinitely by design, unless you  
> tell
> it to stop or one of its callbacks fail. I don't know if this is the
> problem, but you need to make sure that the listening socket itself
> always has an owner too, not just the sockets created by the listening
> socket.
>
> There's an example, by Serge Aleynikov, of how to do a generic tcp
> server here:
>
> http://trapexit.org/Building_a_Non-blocking_TCP_server_using_OTP_principles
>
> I packaged it up for erlware (http://erlware.org) and you can  
> install it
> with their 'faxien' tool, or just grab the code here:
>
> http://git.krondo.com/?p=erlware/gen_socket.git;a=summary
>
> dave
>
>> Thanks!
>> Eric
>> References:
>> 1. http://www.erlang.org/pipermail/erlang-questions/2008-January/032400.html
>> 2. http://www.duomark.com/erlang/tutorials/proxy.html
>> _______________________________________________
>> erlang-questions mailing list
>> 
>> http://www.erlang.org/mailman/listinfo/erlang-questions
>




More information about the erlang-questions mailing list