¡Excelente que hayas empezado a aprender Erlang/OTP!
Los siguientes recursos son muy útiles:
- El OTP Design Principles. Lea esto cuidadosamente, si ya no lo hizo. Tenga en cuenta la idea errónea de que OTP está orientado a objetos (OO): ¡no lo es! Olvídate de todo sobre "herencia". No es posible simplemente construir sistemas completos "extendiendo" módulos estándar.
- El Messaging System:
Estas funciones deben ser utilizados para implementar el uso de mensajes del sistema para un proceso de
- El Special Processes. Un proceso especial es un proceso compatible con OTP que puede integrarse bien con los supervisores.
Este es un código que tengo en mi proyecto. También soy aprendiz de Erlang, así que no confíes demasiado en el código, por favor.
-module(gen_tcpserver).
%% Public API
-export([start_link/2]).
%% start_link reference
-export([init/2]).
%% System internal API
-export([system_continue/3, system_terminate/4, system_code_change/4]).
-define(ACCEPT_TIMEOUT, 250).
-record(server_state, {socket=undefined,
args,
func}).
%% ListenArgs are given to gen_tcp:listen
%% AcceptFun(Socket) -> ok, blocks the TCP accept loop
start_link(ListenArgs, AcceptFun) ->
State = #server_state{args=ListenArgs,func=AcceptFun},
proc_lib:start_link(?MODULE, init, [self(), State]).
init(Parent, State) ->
{Port, Options} = State#server_state.args,
{ok, ListenSocket} = gen_tcp:listen(Port, Options),
NewState = State#server_state{socket=ListenSocket},
Debug = sys:debug_options([]),
proc_lib:init_ack(Parent, {ok, self()}),
loop(Parent, Debug, NewState).
loop(Parent, Debug, State) ->
case gen_tcp:accept(State#server_state.socket, ?ACCEPT_TIMEOUT) of
{ok, Socket} when Debug =:= [] -> ok = (State#server_state.func)(Socket);
{ok, Socket} ->
sys:handle_debug(Debug, fun print_event/3, undefined, {accepted, Socket}),
ok = (State#server_state.func)(Socket);
{error, timeout} -> ok;
{error, closed} when Debug =:= [] ->
sys:handle_debug(Debug, fun print_event/3, undefined, {closed}),
exit(normal);
{error, closed} -> exit(normal)
end,
flush(Parent, Debug, State).
flush(Parent, Debug, State) ->
receive
{system, From, Msg} ->
sys:handle_system_msg(Msg, From, Parent, ?MODULE, Debug, State)
after 0 ->
loop(Parent, Debug, State)
end.
print_event(Device, Event, _Extra) ->
io:format(Device, "*DBG* TCP event = ~p~n", [Event]).
system_continue(Parent, Debug, State) ->
loop(Parent, Debug, State).
system_terminate(Reason, _Parent, _Debug, State) ->
gen_tcp:close(State#server_state.socket),
exit(Reason).
system_code_change(State, _Module, _OldVsn, _Extra) ->
{ok, State}.
Tenga en cuenta que este es un proceso OTP compatible (que puede ser manejado por un supervisor). Debe usar AcceptFun
para generar (= más rápido) un nuevo hijo trabajador. Aún no lo he probado a fondo.
1> {ok, A} = gen_tcpserver:start_link({8080,[]},fun(Socket)->gen_tcp:close(Socket) end).
{ok,<0.93.0>}
2> sys:trace(A, true).
ok
*DBG* TCP event = {accepted,#Port<0.2102>}
*DBG* TCP event = {accepted,#Port<0.2103>}
3>
(Después 2>
's ok
me señaló mi navegador Google Chrome con el puerto 8080: una gran prueba para TCP)
No, usa el prim_inet no documentado (y potencialmente inestable): async_accept/2. Tal vez no hay una "forma OTP" para hacer esto:/ – rpkelly
En este caso, simplemente usaría gen_tcp: accept/1 y gen_tcp: controlling_process/2 (como sugiere la documentación: "Asigna un nuevo proceso de control de Pid a Socket El proceso de control es el proceso que recibe mensajes del socket.Si es llamado por cualquier otro proceso que no sea el proceso de control actual, se devuelve {error, eperm}. "). Este es un ejemplo de cómo usar esto: http://20bits.com/articles/erlang-a-generalized-tcp-server/ (observe el siguiente párrafo:" El problema con la implementación de un servidor de red usando gen_server es que la llamada a gen_tcp: aceptar ... ". Espero que esto ayude. – Alin