What I am trying to solve: have an Erlang TCP server that listens on a specific port (the code should reside in some kind of external facing interface/API) and each incoming connection should be handled by a gen_server (that is even the gen_tcp:accept should be coded inside the gen_server), but I don't actually want to initially spawn a predefined number of processes that accepts an incoming connection). Is that somehow possible ?
Erlang accept incoming tcp connections dynamically
2.2k views Asked by hyperboreean AtThere are 4 answers
On
The issue with gen_tcp:accept is that it blocks, so if you call it within a gen_server, you block the server from receiving other messages. You can try to avoid this by passing a timeout but that ultimately amounts to a form of polling which is best avoided. Instead, you might try Kevin Smith's gen_nb_server instead; it uses an internal undocumented function prim_inet:async_accept and other prim_inet functions to avoid blocking.
On
You should use "prim_inet:async_accept(Listen_socket, -1)" as said by Steve. Now the incoming connection would be accepted by your handle_info callback (assuming you interface is also a gen_server) as you have used an asynchronous accept call.
On accepting the connection you can spawn another ger_server(I would recommend gen_fsm) and make that as the "controlling process" by calling "gen_tcp:controlling_process(CliSocket, Pid of spwned process)".
After this all the data from socket would be received by that process rather than by your interface code. Like that a new controlling process would be spawned for another connection.
On
You might want to check out http://github.com/oscarh/gen_tcpd and use the handle_connection function to convert the process you get to a gen_server.
Basic Procedure
You should have one static process (implemented as a
gen_serveror a custom process) that performs the following procedure:gen_tcp:accept/1gen_serverprocess)gen_tcp:controlling_process/2with the newly returned socket and that pidThe listening process should only have one responsibility, and that is spawning of workers for new connections. This process will block when calling
gen_tcp:accept/1, which is fine because the started workers will handle ongoing connections concurrently. Blocking on accept ensure the quickest response time when new connections are initiated. If the process needs to do other things in-between,gen_tcp:accept/2could be used with other actions interleaved between timeouts.Scaling
You can have multiple processes waiting with
gen_tcp:accept/1on a single listening socket, further increasing concurrency and minimizing accept latency.Another optimization would be to pre-start some socket workers to further minimize latency after accepting the new socket.
Third and final, would be to make your processes more lightweight by implementing the OTP design principles in your own custom processes using
proc_lib(more info). However, this you should only do if you benchmark and come to the conclusion that it is thegen_serverbehavior that slows you down.