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

时间:2016-07-21 03:02:54

标签: erlang elixir gen-tcp

我有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也会愉快地等待下一条消息。

如果在X秒后没有收到来自TCP连接的消息,我怎么能让GenServer触发一个函数?我是否坚持使用recv

1 个答案:

答案 0 :(得分:4)

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