1. ホーム
  2. erlang

[解決済み] Elixirでgen_tcpを使用してアクティブなソケットのTCPタイムアウトを検出するにはどうすればよいですか?

2022-02-19 16:58:40

質問内容

を持っています。 GenServer を介してリモートの TCP コネクションに接続しています。 gen_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 ?

解決方法は?

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