如何使用gen_tcp在Elixir的活动套接字上检测TCP超时?

如何使用gen_tcp在Elixir的活动套接字上检测TCP超时?

问题描述:

我有一个GenServer通过gen_tcp连接到远程TCP连接。如何使用gen_tcp在Elixir的活动套接字上检测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

+0

不知道为什么有人低估了这一点。这是许多人很早就用Erlang,Elixir,LFE等人使用'{active,true}'套接字所面临的一个基本难题 - 而且这个解决方案不仅适用于Elixir。 – zxq9

如果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}. 
+0

感谢您的回答!我也意识到,Elixir'GenServer's支持返回'timeout'值,所以{:noreply,state,timeout}'导致发送超时消息,如果在timeout内没有收到其他消息。我不确定Erlang是否存在于gen_servers中,或者Elixir是否将它添加进去,但是这为我解决了问题。如果我无法访问该超时助手,您的解决方案也可以工作。谢谢! – user2666425

+0

@ user2666425是的,Erlang gen_servers具有完全相同的超时值,您可以将其包含在来自'handle_ *'调用的返回值中。 *但是*对于all_中的任何消息,这是一个超时_,并且这与TCP消息_的超时_不同。例如,如果你使用Erlang系统消息给你的TCP处理程序发送垃圾邮件,它将不断重置该超时值并且错过了套接字没有活动的事实。这就是为什么这个特殊的用途,我添加自己的'tcp_timeout'消息作为特定于套接字流量的消息。 – zxq9

+0

这很有道理。非常感谢您提供清晰有用的答案! – user2666425