我的ejabberd版本是14.12和erlang R17B,所以这段代码似乎没用,因为R17B中的erlang:system_info(otp_release)重新打造了" 17"
ejabberd_listener.erl
SockOpts2 =
try erlang:system_info(otp_release) >= "R13B" of
true -> [{send_timeout_close, true} | SockOpts];
false -> SockOpts
catch
_:_ -> []
end,
我在listen选项中手动添加了{send_timeout_close,true},我的问题看到了解决,因为socket在发送超时的同时关闭,尝试在队列中发送后续消息会收到{error,enotconn响应。
当{gen_event,'关闭'} msg到来时,c2s进程正常终止。
也许我找到了重现这个问题的方法:
1.与xmpp客户端建立正常的c2s连接
2.用一些工具切断客户的网络,例如。笨拙(从服务器丢弃所有tcp数据包)
3.继续向c2s进程发送大数据包
首先,gen_tcp:send在sendbuffer填充之前返回ok
然后,gen_tcp:发送retruns {error,timeout}因为sendbuffer被填充了
进程调用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%。
简单地说,如果我将数据包发送到无法接收数据包且sendbuffer已满的客户端套接字,我将在sendtimeout之后收到{error,timeout}。但是,如果我在等待gen_tcp:send / 2的sendtimeout时另一个异步进程关闭套接字,我将永远不会得到响应。
所以,我用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;
但在某些情况下,c2s pid在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 ...}
...
大多数情况发生在用户网络状况不太好的情况下,接收方进程应向c2s pid发送2条消息,c2s将终止会话或等待恢复
{' $ gen_event',关闭}
的 {' DOWN',#价< 0.0.1201.250595>,过程,< 0.19617.245>,正常}
我在c2s进程中打印了消息队列,2 msg在队列中,等待处理。不幸, 由于进程在处理这些消息之前已被阻塞,因此队列不再移动,如上所述,堆叠在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.
在erlang:port_command(S,Data,OptList)之后,端口驱动程序似乎没有回复{inet_reply,S,Status}。
gen_tcp:send函数会阻止无穷大,任何人都能解释一下吗?
答案 0 :(得分:0)
这取决于您使用的Erlang版本。 gen_tcp send上的超时选项未在旧的ejabberd版本上使用,因为它当时在Erlang中不可用。此外,您必须使用最新版本的Erlang,因为Erlang本身已针对这些选项修复了一些错误。