在Cowboy Websocket处理程序中更新状态会以静默方式崩溃或忽略

时间:2017-11-22 18:38:51

标签: websocket erlang gen-server cowboy

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

此过程等待以<<"h开始登录的输入。

然后保存默认的Websocket状态,一个Player进程ID(通过对当前PID的引用产生),以及一个发件箱消息,`&lt;&lt;&lt;&lt;&lt;&lt;&#&gt;&lt;&lt;&lt;&lt;&lt;&#&gt;&gt;&#34;&gt;&gt;&gt;

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

我在gen_server:call中尝试了player.erl,我发现了超时崩溃。我认为这是因为{replywebsocket_handle的{​​{1}}元组应该回复websocket ..但如果这是真的,那么我期待数据到更新:

ws_handler

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

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

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

player.erl:

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

1 个答案:

答案 0 :(得分:1)

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

所以你应该这样做:

WS ! myscreenupdate

而不是

gen_server:cast(WS, myscreenupdate)

当您使用gen_server:cast时,牛仔消息循环可能会丢弃该消息,因为它不是可识别的消息。当你使用gen_server:call时,你会陷入僵局。