【问题标题】:How can I detect a TCP timeout on an active socket in Elixir with gen_tcp?如何使用 gen_tcp 在 Elixir 中的活动套接字上检测 TCP 超时?
【发布时间】:2016-07-24 10:32:39
【问题描述】:

我有一个GenServer,它通过gen_tcp 连接到远程TCP 连接。

opts = [:binary, active: true, packet: :line] {:ok, socket} = :gen_tcp.connect('remote-url', 8000, opts}

我正在处理消息:

def handle_info({:tcp, socket, msg}, stage) do IO.inspect msg {:noreply, state} end

效果很好。但是,TCP 服务器很容易超时。如果我使用gen_tcp.recv,我可以指定超时。但是,我使用active: true 来接收带有handle_info 的消息,而不必循环调用recv。所以,GenServer 很高兴地等待下一条消息,即使服务器已经超时。

如何让GenServer 在 X 秒后没有收到来自 TCP 连接的消息时触发函数?我被recv 卡住了吗?

【问题讨论】:

  • 不知道为什么有人反对这个。这是许多人在早期使用 {active, true} 套接字使用 Erlang、Elixir、LFE 等时遇到的一个基本难题——解决方案仅适用于 Elixir。

标签: erlang elixir gen-tcp


【解决方案1】:

如果 TCP 连接本身已经超时,那么您应该会收到一个关闭的套接字消息{tcp_closed, Socket}。在handle_info 中匹配就可以了。至于在连接上建立您自己的超时,我通常使用erlang:send_after/3 向自己发送一条消息——有效地添加我可以在handle_info 或任何receive 服务中接收的超时消息语义可能存在循环。

erlang:send_after(?IDLE_TIMEOUT, self(), {tcp_timeout, Socket})

这必须与每次收到流量时取消计时器配对。这可能看起来像:

handle_info({tcp, Socket, Bin}, State = #s{timer = T, socket = Socket}) ->
    _ = erlang:cancel_timer(T),
    {Messages, NewState} = interpret(Bin, State),
    ok = handle(Messages),
    NewT = erlang:send_after(?IDLE_TIMEOUT, self(), {tcp_timeout, Socket})
    {noreply, NewState#s{timer = NewT}};
handle_info({tcp_closed, Socket}, State = #s{timer = T, socket = Socket}) ->
    _ = erlang:cancel_timer(T),
    NewState = handle_close(State),
    {noreply, NewState};
handle_info({tcp_timeout, Socket}, State = #s{socket = Socket}) ->
    NewState = handle_timeout(State),
    {noreply, NewState};
handle_info(Unexpected, State) ->
    ok = log(warning, "Received unexpected message: ~tp", [Unexpected]),
    {noreply, State}.

【讨论】:

  • 感谢您的回答!我还意识到 Elixir GenServers 支持返回 timeout 值,因此 {:noreply, state, timeout}` 如果在 timeout 内没有收到其他消息,则会导致发送超时消息。我不确定这是否存在于 Erlang gen_servers 中,或者 Elixir 是否正在添加它,但这为我解决了问题。如果我无法访问该超时助手,您的解决方案也将起作用。谢谢!
  • @user2666425 是的,Erlang gen_servers 具有完全相同的超时值,您可以将其包含在 handle_* 调用的返回值中。 但是这是一个超时对于任何消息,这与超时对于 TCP 消息不同。例如,如果您使用 Erlang 系统消息向 TCP 处理程序发送垃圾邮件,它将不断重置该超时值并忽略套接字没有活动的事实。这就是为什么对于这种特殊用途,我将自己的 tcp_timeout 消息添加为特定于套接字流量的消息。
  • 这很有意义。非常感谢您提供清晰而有帮助的答案!
猜你喜欢
  • 2014-05-16
  • 2016-01-30
  • 1970-01-01
  • 2016-07-30
  • 2019-07-11
  • 2011-09-18
  • 1970-01-01
  • 2014-01-28
相关资源
最近更新 更多