Не могу создать gen_server обработчик в ranch

215 views
Skip to first unread message

nt

unread,
Jan 11, 2013, 9:05:31 PM1/11/13
to erlang-...@googlegroups.com


Привет, разбираюсь с ranch, пробую создать свой обработчик, реализующий gen_server. Что-то явно делаю не так - не происходит подтверждение подключения клиента к сокету.

В примере обработчик выглядит так:

start_link(ListenerPid, Socket, Transport, Opts) ->
Pid = spawn_link(?MODULE, init, [ListenerPid, Socket, Transport, Opts]),
{ok, Pid}.

init(ListenerPid, Socket, Transport, _Opts = []) ->
ok = ranch:accept_ack(ListenerPid),
io:format("ack~n"),
loop(Socket, Transport).

loop(Socket, Transport) ->
case Transport:recv(Socket, 0, infinity) of
{ok, Data} ->
Transport:send(Socket, Data),
loop(Socket, Transport);
_ ->
ok = Transport:close(Socket)
end.


Я создаю аналогичный модуль:

-record(state, {socket, transport}).

-define(SERVER, ?MODULE).

start_link(ListenerPid, Socket, Transport, Opts) ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [ListenerPid, Socket, Transport, Opts], []).

init([ListenerPid, Socket, Transport, _Opts = []]) ->
io:format("initializing handler...~n"),
ok = ranch:accept_ack(ListenerPid), %% процесс находится в бесконечном ожидании
io:format("ack~n"),
{ok, #state{socket = Socket, transport = Transport}, 0}.

handle_info(timeout, #state{socket = Socket, transport = Transport} = State) ->
    io:format("timeout~n"),
case Transport:recv(Socket, 0, infinity) of
{ok, Data} ->
Transport:send(Socket, Data),
   {noreply, State};
_ ->
ok = Transport:close(Socket),
   {stop, normal, State}
end.

(лишнее убрал)


Функция ranch:accept_ack выглядит вот так:

accept_ack(ListenerPid) ->
receive {shoot, ListenerPid} -> ok end.

Собственно, не могу понять, почему процесс ranch не получает это сообщение и не дает мне работать (это из-за разделения на контейнер и модуль колбэка gen_server?)
Как я понимаю, вот место, где отправляется это сообщение https://github.com/extend/ranch/blob/master/src/ranch_acceptor.erl#L58

И сюда же еще один вопрос - почему в стандартном примере не использован gen_server, почему в cowboy не использован gen_server? https://github.com/extend/cowboy/blob/master/src/cowboy_protocol.erl

nt

unread,
Jan 11, 2013, 9:07:35 PM1/11/13
to erlang-...@googlegroups.com
gen_server хотел, чтобы получать данные об отключившихся клиентах из terminate, это правильно? :)

суббота, 12 января 2013 г., 6:05:31 UTC+4 пользователь nt написал:

Max Lapshin

unread,
Jan 11, 2013, 9:27:28 PM1/11/13
to erlang-...@googlegroups.com
Конечно у тебя не будет работать.


start_link(ListenerPid, Socket, Transport, Opts) ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [ListenerPid, Socket, Transport, Opts], []).

Эта функция не вернет {ok,Pid} до тех пор, пока не закончится функция:


init([ListenerPid, Socket, Transport, _Opts = []]) ->
io:format("initializing handler...~n"),
ok = ranch:accept_ack(ListenerPid), %% процесс находится в бесконечном ожидании
io:format("ack~n"),
{ok, #state{socket = Socket, transport = Transport}, 0}.

а функция init/1 ждет завершения функции

  ok = ranch:accept_ack(ListenerPid), %% процесс находится в бесконечном ожидании

которая не закончится, пока start_link/4 не вернет {ok,Pid}

Это называется дедлок. Проще всего лечится так:


start_link(ListenerPid, Socket, Transport, Opts) ->
proc_lib:start_link(?MODULE, init, [[ListenerPid, Socket, Transport, Opts]]).

init([ListenerPid, Socket, Transport, _Opts = []]) ->
    register(?MODULE, self()),
    proc_lib:init_ack({ok, self()}),
    ranch:accept_ack(ListenerPid),
    gen_server:enter_loop(?MODULE, #state{socket = Socket, transport = Transport}, []).

Message has been deleted

nt

unread,
Jan 12, 2013, 4:52:24 AM1/12/13
to erlang-...@googlegroups.com
Точно, спасибо :))

суббота, 12 января 2013 г., 6:27:28 UTC+4 пользователь Max Lapshin написал:
Reply all
Reply to author
Forward
0 new messages