在 Cowboy Websocket 处理程序中更新状态会崩溃或忽略
Updating state in Cowboy Websocket handler crashes or ignores silently
我有一个接收 websocket 连接的 ws_handler。
此进程等待以 <<"h
开头的输入以登录。
然后它会保存默认的 Websocket 状态、播放器进程 ID(通过引用当前 PID 生成)和发件箱消息,`<<"turn1">>
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_handler
中 websocket_handle
的 {reply
元组应该回复 websocket.. 但如果这是真的,那么我希望数据被更新:
websocket_info(myscreenupdate, State = {St,P, _}) ->
{reply, {text, <<"My screen update">>}, {St, P, <<"turn2">>}};
所以我不确定这里发生了什么。
如何从播放器进程更新状态然后让我的 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}.
player.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], []).
问题在于 cowboy 的 websocket_info/2
处理程序将仅接收使用 erlang 内置消息运算符 !
(或者等效地 erlang:send/{2,3}
发送到 websocket 进程的消息函数)。
所以你应该这样做:
WS ! myscreenupdate
而不是
gen_server:cast(WS, myscreenupdate)
当您使用 gen_server:cast
时,消息可能会被牛仔消息循环丢弃,因为它不是可识别的消息。当你使用 gen_server:call
时,你会陷入僵局。
我有一个接收 websocket 连接的 ws_handler。
此进程等待以 <<"h
开头的输入以登录。
然后它会保存默认的 Websocket 状态、播放器进程 ID(通过引用当前 PID 生成)和发件箱消息,`<<"turn1">>
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_handler
中 websocket_handle
的 {reply
元组应该回复 websocket.. 但如果这是真的,那么我希望数据被更新:
websocket_info(myscreenupdate, State = {St,P, _}) ->
{reply, {text, <<"My screen update">>}, {St, P, <<"turn2">>}};
所以我不确定这里发生了什么。
如何从播放器进程更新状态然后让我的 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}.
player.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], []).
问题在于 cowboy 的 websocket_info/2
处理程序将仅接收使用 erlang 内置消息运算符 !
(或者等效地 erlang:send/{2,3}
发送到 websocket 进程的消息函数)。
所以你应该这样做:
WS ! myscreenupdate
而不是
gen_server:cast(WS, myscreenupdate)
当您使用 gen_server:cast
时,消息可能会被牛仔消息循环丢弃,因为它不是可识别的消息。当你使用 gen_server:call
时,你会陷入僵局。