Erlang Ranch Websocket Client не может обнаружить потерянное интернет-соединение - PullRequest
1 голос
/ 27 сентября 2019

Я написал очень стандартный веб-сокет-клиент, использующий Gun.Он работает, как и ожидалось, подключается, отправляет и получает сообщения и т. Д. Все очень нормально.

Однако я обнаружил, что он не обнаруживает разрыв соединения с Интернетом.Если я отсоединяю кабель Ethernet от моего компьютера, клиент Gun ничего не делает.Я не получаю никакой ошибки, сообщения "ВНИЗ" или какой-либо информации.И затем, если я снова подключу кабель Ethernet, ничего не произойдет.Gun просто останавливается и ничего не делает.

В идеале, я хочу получить какое-то сообщение от Gun, если соединение разорвется.Таким образом, я могу справиться со всем и попытаться восстановить соединение.

Чего мне не хватает?Как я могу обнаружить потерянное соединение от Gun?

Мой код клиента:

-module(test_client).
-behaviour(gen_server).

-include_lib("kernel/include/logger.hrl").


%% API.
-export([start_link/0]).

%% gen_server.
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).

-record(state, {
    uri,
    port,
    path
}).

%% API.

-spec start_link() -> {ok, pid()}.
start_link() ->
    gen_server:start_link(?MODULE, [], []).

%% gen_server.

init([]) ->

    ?LOG_INFO(#{pid=>self(), module=>?MODULE, where=>init, msg=>started}),

    URI = "127.0.0.1",
    Port = 443,
    Path = "/ws",
    Opts = #{transport => tls, protocols => [http],retry => 5,retry_timeout => 2000},

    gen_server:cast(self(), connect),

    {ok,  #state{uri=URI, port=Port, path=Path, conn_opts=Opts}}.

handle_call(_Request, _From, State) ->
    {reply, ignored, State}.

handle_cast(connect, State0) ->
    {ok, ConnPid} = gun:open(State0#state.uri, State0#state.port, State0#state.conn_opts),
    _ = monitor(process, ConnPid),
    {noreply, State0#state{conn_pid=ConnPid};

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info({gun_up, ConnPID, http}, State) ->
    ?LOG_INFO(#{pid=>self(), module=>?MODULE, where=>info, msg=>gun_up, conn_pid=>ConnPID}),
    gun:ws_upgrade(ConnPid),
    {noreply, State#state{conn_pid=ConnPID}};

handle_info({gun_upgrade, ConnPID, ConnRef, _, _}, State) ->
    ?LOG_INFO(#{pid=>self(), module=>?MODULE, where=>info, msg=>gun_upgrade, conn_pid=>ConnPID, conn_ref=>ConnRef}),
    {noreply, State#state{conn_pid=ConnPID}};

handle_info({gun_down, ConnPID, ws, closed, _, _}, State) ->
    ?LOG_INFO(#{pid=>self(), module=>?MODULE, where=>info, msg=>gun_down, conn_pid=>ConnPID}),
    gun:close(ConnPID),
    gen_server:cast(self(), retry_connect),
    {noreply, State#state{conn_pid=null}};   

handle_info({gun_ws, _ConnPID, _ConnRef, RawMsg}, State) ->
    io:format("Receive: ~p~n", [RawMsg]),
    {noreply, State};

handle_info({gun_response, ConnPID, _ConnRef, _Err, Code, _Headers}, State0) ->
    ?LOG_ERROR(#{pid=>self(), module=>?MODULE, where=>info, msg=>gun_response, code=>Code}),
    gun:close(ConnPID),
    {noreply, State0#state{conn_pid=null}};

handle_info({gun_error, ConnPID, _StreamRef, Reason}, State0) ->
    ?LOG_ERROR(#{pid=>self(), module=>?MODULE, where=>info, msg=>gun_error, code=>Reason}),
    gun:close(ConnPID),
    {noreply, State0#state{conn_pid=null}};

handle_info({gun_error, ConnPID,Reason}, State0) ->
    ?LOG_ERROR(#{pid=>self(), module=>?MODULE, where=>info, msg=>gun_error, code=>Reason}),
    gun:close(ConnPID),
    {noreply, State0#state{conn_pid=null}};

handle_info({'DOWN', Mref, process, ConnPid, Reason}, State) ->
    ?LOG_ERROR(#{pid=>self(), module=>?MODULE, where=>info, msg=>monitor_down, code=>Reason}),
    demonitor(Mref),
    gun:close(ConnPid),
    {noreply, State#state{conn_pid=null}};   

handle_info(Info, State) ->
    ?LOG_ERROR(#{pid=>self(), module=>?MODULE, where=>info, status=>unknown, msg=>Info}),
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

1 Ответ

0 голосов
/ 28 сентября 2019

Похоже, вам нужно реализовать обработчик ping/pong кадров, для получения дополнительной информации, пожалуйста, посмотрите RFC https://tools.ietf.org/html/rfc6455#section-5.5.2 и https://tools.ietf.org/html/rfc6455#section-5.5.3. Итак, когда сервер отправляет ping, клиентприложения, например: браузер должен ответить pong обратно на сторону сервера, и вы можете справиться с этим с помощью WebSocket.Но если сервер отправит ping и не получит pong в ответ на стороне клиента - это будет означать, что соединение потеряно.Надеюсь, это будет полезно.

...