由于port命令无法返回

时间:2015-10-26 05:18:07

标签: sockets erlang ejabberd gen-tcp

于2015-11-25 02:10编辑

我的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进程正常终止。

编辑于2015-11-24 03:40



也许我找到了重现这个问题的方法:
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切断网络,继续发送数据包,异步关闭)。 我想知道这是一个问题还是因为我自己的原因? enter image description here




以下原帖


一般在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函数会阻止无穷大,任何人都能解释一下吗?

1 个答案:

答案 0 :(得分:0)

这取决于您使用的Erlang版本。 gen_tcp send上的超时选项未在旧的ejabberd版本上使用,因为它当时在Erlang中不可用。此外,您必须使用最新版本的Erlang,因为Erlang本身已针对这些选项修复了一些错误。