отредактировано 25.11.2015 02:10
Моя версия ejabberd — 14.12 и erlang R17B, поэтому этот код кажется бесполезным, потому что erlang:system_info(otp_release) в R17B повторно запускает «17»
ejabberd_listener.erl
SockOpts2 =
try erlang:system_info(otp_release) >= "R13B" of
true -> [{send_timeout_close, true} | SockOpts];
false -> SockOpts
catch
_:_ -> []
end,
Я добавил {send_timeout_close, true} вручную в параметр прослушивания, моя проблема кажется решенной, поскольку сокет закрывается одновременно с тайм-аутом отправки, при попытке отправить последующие сообщения в очередь будет получен ответ {error, enotconn}. когда приходит сообщение {gen_event, 'closed'}, процесс c2s завершается нормально.
отредактировано 24.11.2015 03:40
Возможно, я нашел способ воспроизвести эту проблему:
1. построить обычное соединение c2s с клиентом xmpp
2. отключить сеть клиента с помощью некоторых инструментов, например. неуклюже (отбрасывает все TCP-пакеты с сервера)
3. Продолжайте отправлять большие пакеты процессу c2s
Сначала gen_tcp:send возвращает значение ok до заполнения буфера отправки
Затем gen_tcp:send перезапускается {ошибка, время ожидания} из-за заполнения буфера отправки
процесс вызывает ejabberd_socket:close(Socket), чтобы закрыть соединение
send_text(StateData, Text) when StateData#state.mgmt_state == active ->
catch ?INFO_MSG("Send XML on stream = ~ts", [Text]),
case catch (StateData#state.sockmod):send(StateData#state.socket, Text) of
{'EXIT', _} ->
(StateData#state.sockmod):close(StateData#state.socket),
error;
_ ->
ok
конец;
Но ejabberd_socket:close/1 кажется асинхронным вызовом, поэтому процесс c2s будет обрабатывать следующее сообщение в message_queue, продолжать вызывать gen_tcp:send/2, ожидая send_timeout.
Но в это время ejabberd_receiver вызвал gen_tcp:close(Socket), сокет закрыт, поэтому предыдущий gen_tcp:send/2 никогда не возвращается. Пробовал несколько раз этим методом, получается 100%.
Вкратце, если я отправляю пакеты в клиентский сокет, который не может получить пакет, а буфер отправки заполнен, я получу {ошибку, тайм-аут} после тайм-аута отправки. Но если другой асинхронный процесс закроет сокет, когда я ожидаю таймаута отправки с gen_tcp:send/2, я никогда не получу ответ.
Итак, я сделал это с помощью erl, и gen_tcp:send/2 не ответил (отключение сети на шаге 3, продолжение отправки пакета, асинхронное закрытие). Я хочу знать, это проблема или из-за себя?
оригинальный пост ниже
Обычно в ejabberd я перенаправляю сообщение клиентскому процессу, отправляю его в сокет tcp с помощью этой функции. И это работает хорошо большую часть времени. Модуль ejabberd_c2s.erl
send_text(StateData, Text) when StateData#state.mgmt_state == active ->
catch ?INFO_MSG("Send XML on stream = ~ts", [Text]),
case catch (StateData#state.sockmod):send(StateData#state.socket, Text) of
{'EXIT', _} ->
(StateData#state.sockmod):close(StateData#state.socket),
error;
_ ->
ok
end;
Но в некоторых случаях pid c2s блокируется на gen_tcp: отправить так
erlang:process_info(pid(0,8353,11)).
[{current_function,{prim_inet,send,3}},
{initial_call,{proc_lib,init_p,5}},
{status,waiting},
{message_queue_len,96},
{messages ...}
...
В большинстве случаев случалось, когда состояние сети пользователя было не очень хорошим, процесс получателя должен был отправить 2 сообщения pid c2s, а c2s завершал сеанс или ждал возобновления.
{'$gen_event',closed}
{'DOWN',#Ref‹0.0.1201.250595>,process,‹0.19617.245>,normal} < бр/>
Я напечатал очередь сообщений в процессе c2s, 2 сообщения находятся в очереди, ожидая обработки. К сожалению, очередь больше не перемещается, потому что процесс был заблокирован перед обработкой этих сообщений, как описано выше, сложенных в prim_inet:send/3, когда tring выполняет gen_tcp:send/2. Очередь становится очень большой через несколько дней, и ejabberd падает, когда процесс запрашивает больше памяти.
prim_inet:send/3 source :
send(S, Data, OptList) when is_port(S), is_list(OptList) ->
?DBG_FORMAT("prim_inet:send(~p, ~p)~n", [S,Data]),
try erlang:port_command(S, Data, OptList) of
false -> % Port busy and nosuspend option passed
?DBG_FORMAT("prim_inet:send() -> {error,busy}~n", []),
{error,busy};
true ->
receive
{inet_reply,S,Status} ->
?DBG_FORMAT("prim_inet:send() -> ~p~n", [Status]),
Status
end
catch
error:_Error ->
?DBG_FORMAT("prim_inet:send() -> {error,einval}~n", []),
{error,einval}
end.
Похоже, что драйвер порта не ответил {inet_reply,S,Status} после erlang:port_command(S, Data, OptList) . функция gen_tcp:send заблокирует бесконечность. Кто-нибудь может это объяснить?