【问题标题】:Updating state in Cowboy Websocket handler crashes or ignores silentlyCowboy Websocket 处理程序中的更新状态崩溃或静默忽略
【发布时间】:2018-05-06 14:37:56
【问题描述】:

我有一个接收 websocket 连接的 ws_handler。

此进程等待以<<"h 开头的输入登录。

然后它会保存默认的 Websocket 状态、一个 Player 进程 ID(通过对当前 PID 的引用产生)和一个发件箱消息,`>

websocket_handle({text, <<"h", Name/binary>>}, State) ->
  {ok, PID} = player:go(self(), <<"profiledata", Name/binary>>),
  erlang:start_timer(1000, self(), <<"Hello!">>),
  {reply, {text, <<"You joined, ", Name/binary>>}, {State, PID, <<"turn1">>}};

我想控制来自这个单独的播放器进程的数据流,然后让我的 websocket 处理程序检索消息并通过 Outbox 元素将它们传递给它的客户端。

所以我添加这个来手动触发消息:

websocket_handle({text, <<"myscreen">>}, S = {_, P, _}) ->
  gen_server:call(P, myscreen),
  {ok, S};

在 player.erl 中,

handle_call(myscreen, _, {WS, Profile, Cab}) ->
  gen_server:cast(WS, myscreenupdate),
  {reply, ok, {WS, Profile, Cab}};

回到 ws_handler 我希望它会被调用:

websocket_info(myscreenupdate, State = {St,P, _}) ->
  {reply, {text, <<"My screen update">>}, {St, P, <<"turn2">>}};

但是我的浏览器中的 websocket 输出连续打印turn1,而不是turn2

我在player.erl 中尝试了gen_server:call,但我遇到了超时崩溃。我认为这是因为ws_handlerwebsocket_handle{reply 元组应该是回复websocket.. 但如果这是真的,那么我希望数据会被更新:

websocket_info(myscreenupdate, State = {St,P, _}) ->
  {reply, {text, <<"My screen update">>}, {St, P, <<"turn2">>}};

所以我不确定这里发生了什么。

如何从 Player 进程更新状态,然后让我的 websocket 处理程序检索该状态并将其发送到其连接?

ws_handler.erl:

-module(ws_handler).

-export([init/2]).
-export([websocket_init/1]).
-export([websocket_handle/2]).
-export([websocket_info/2]).

init(Req, Opts) ->
    {cowboy_websocket, Req, Opts}.

websocket_init(State) ->
    {ok, State}.

websocket_handle({text, <<"h", Name/binary>>}, State) ->
  {ok, PID} = player:go(self(), <<"profiledata", Name/binary>>),
  erlang:start_timer(1000, self(), <<"Hello!">>),
  {reply, {text, <<"You joined, ", Name/binary>>}, {State, PID, <<"turn1">>}};

websocket_handle({text, <<"myscreen">>}, S = {_, P, _}) ->
  gen_server:call(P, myscreen),
  {ok, S};

websocket_handle({text, <<"auth", Auth/binary>>}, S = {_St, P, _}) ->
  case s:s(P, Auth) of
    {ok, Valid} -> {reply, {text, << "Authorized">>}, S};
    _ -> {reply, {text, <<"Error">>}, S}
  end;
websocket_handle({text, Msg}, S = {_St, P, Outbox}) ->
    {reply, {text, Outbox}, S};
websocket_handle(_Data, State) ->
    {ok, State}.

websocket_info(myscreenupdate, State = {St,P, _}) ->
  {reply, {text, <<"My screen update">>}, {St, P, <<"turn2">>}};

websocket_info({timeout, _Ref, _Ignored}, State = {_, P, Outbox}) ->
    erlang:start_timer(1000, self(), <<"This is ignored">>),
  Msg = Outbox,
    {reply, {text, Msg}, State};
websocket_info(_Info, State) ->
    {ok, State}.

播放器.erl:

-module(player).
-compile(export_all).

handle_call(myscreen, _, {WS, Profile, Cab}) ->
  gen_server:cast(WS, myscreenupdate),
  {reply, ok, {WS, Profile, Cab}};
handle_call(get_profile, _, State = {_WSPID, Profile, _}) ->
  {reply, Profile, State}.

init([WSPID, Profile]) ->
  {ok, {WSPID, Profile, null}};
init([WSPID, Profile, CabinetPID]) ->
  {ok, {WSPID, Profile, CabinetPID}}.

go(WSPID, Profile, CabinetPID) ->
  gen_server:start_link(?MODULE, [WSPID, Profile, CabinetPID], []).
go(WSPID, Profile) ->
  gen_server:start_link(?MODULE, [WSPID, Profile], []).

【问题讨论】:

标签: websocket erlang gen-server cowboy


【解决方案1】:

问题在于,cowboy 的 websocket_info/2 处理程序只会接收使用 erlang 内置消息运算符 !(或等效的 erlang:send/{2,3} 函数)发送到 websocket 进程的消息。

所以你应该这样做:

WS ! myscreenupdate

而不是

gen_server:cast(WS, myscreenupdate)

当您使用gen_server:cast 时,消息可能会被牛仔消息循环丢弃,因为它不是可识别的消息。当您使用gen_server:call 时,您会遇到死锁。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-27
    • 2018-02-09
    • 2020-09-07
    • 2016-11-29
    • 2015-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多