作为 Cowboy 客户端,您如何使用 Gun?
How do you use Gun as a Cowboy client?
我按照 Cowboy 的 Getting Started instructions,我有 Cowboy 运行 并在端口 8080 上侦听,当我输入 [=31= 时,我得到了 Hello Erlang!
响应] 在我的浏览器中。现在,如何使用 Gun 连接到 Cowboy?
我看了Gun docs,它说要添加"Gun as an erlang.mk dependency"。所以我下载了 erlang.mk:
~/erlang_programs/my_gun$ curl -O https://erlang.mk/erlang.mk
并在 Erlang.mk User Guide 之后,我创建了一个应用程序:
~/erlang_programs/my_gun$ gmake -f erlang.mk bootstrap
然后我将 gun 添加为 Makefile 的依赖项:
PROJECT = my_gun
PROJECT_DESCRIPTION = New project
PROJECT_VERSION = 0.1.0
DEPS = gun
include erlang.mk
然后我编译:
~/erlang_programs/my_gun$ gmake
gmake[1]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
ERLC cow_cookie.erl cow_date.erl cow_hpack.erl cow_http.erl cow_http2.erl cow_http_hd.erl cow_http_te.erl cow_mimetypes.erl cow_multipart.erl cow_qs.erl cow_spdy.erl cow_sse.erl cow_uri.erl cow_ws.erl
APP cowlib
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
DEPEND ranch.d
ERLC ranch.erl ranch_acceptor.erl ranch_acceptors_sup.erl ranch_app.erl ranch_conns_sup.erl ranch_listener_sup.erl ranch_protocol.erl ranch_server.erl ranch_ssl.erl ranch_sup.erl ranch_tcp.erl ranch_transport.erl
APP ranch
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
DEPEND gun.d
ERLC gun.erl gun_app.erl gun_content_handler.erl gun_data.erl gun_http.erl gun_http2.erl gun_spdy.erl gun_sse.erl gun_sup.erl gun_ws.erl gun_ws_handler.erl
APP gun
GEN rebar.config
gmake[1]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
DEPEND my_gun.d
ERLC my_gun_app.erl my_gun_sup.erl
APP my_gun
GEN /Users/7stud/erlang_programs/my_gun/.erlang.mk/relx
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
/Users/7stud/erlang_programs/my_gun/ebin
/Users/7stud/erlang_programs/my_gun/deps
/Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang/lib
/Users/7stud/erlang_programs/my_gun/apps
===> Resolved my_gun_release-1
===> rendering builtin_hook_status hook to "/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/bin/hooks/builtin/status"
===> Including Erts from /Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang
===> release successfully created!
===> tarball /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/my_gun_release-1.tar.gz successfully created!
但是当我切换到 erlang shell 并尝试启动 gun 时,我得到一个错误:
~/erlang_programs/my_gun$ erl
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2 (abort with ^G)
1> application:ensure_all_started(gun).
{error,{gun,{"no such file or directory","gun.app"}}}
有人可以 post 一个简单的例子来说明如何使用 Gun(或任何其他支持 websocket 的 http 客户端)连接到 Cowboy 吗?
好的,我取得了一些进展。我删除了 my_gun 目录,重新创建目录,重新下载 erlang.mk,并创建了 a release,其中 command:
~/erlang_programs/my_gun$ gmake -f erlang.mk bootstrap-lib bootstrap-rel
然后我将 gun 依赖项添加到 Makefile 中(如上所述)。然后我做了:
~/erlang_programs/my_gun$ gmake run
如果代码中没有错误,则会启动一个 erlang shell。在 erlang shell 中,我输入了以下代码(请参阅下面的 tip
以避免必须在 shell 中键入所有代码):
(my_gun@127.0.0.1)1> application:ensure_all_started(gun).
{ok,[]}
(my_gun@127.0.0.1)2> {ok, ConnPid} = gun:open("localhost", 8080).
=PROGRESS REPORT==== 10-Jul-2017::05:21:58 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.366.0>},{mfa,{inet_gethost_native,init,[[]]}}]
{ok,<0.364.0>}
=PROGRESS REPORT==== 10-Jul-2017::05:21:58 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.365.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
(my_gun@127.0.0.1)3> {ok, Protocol} = gun:await_up(ConnPid).
{ok,http}
(my_gun@127.0.0.1)4> gun:ws_upgrade(ConnPid, "/websocket").
#Ref<0.0.3.244>
(my_gun@127.0.0.1)5> receive
(my_gun@127.0.0.1)5> {gun_ws_upgrade, ConnPid, ok, Headers} ->
(my_gun@127.0.0.1)5> upgrade_success(ConnPid);
(my_gun@127.0.0.1)5> {gun_response, ConnPid, _, _, Status, Headers} ->
(my_gun@127.0.0.1)5> exit({ws_upgrade_failed, Status, Headers});
(my_gun@127.0.0.1)5> {gun_error, ConnPid, StreamRef, Reason} ->
(my_gun@127.0.0.1)5> exit({ws_upgrade_failed, Reason})
(my_gun@127.0.0.1)5> after 1000 ->
(my_gun@127.0.0.1)5> exit(timeout)
(my_gun@127.0.0.1)5> end.
=CRASH REPORT==== 10-Jul-2017::05:25:17 ===
crasher:
initial call: gun:proc_lib_hack/5
pid: <0.364.0>
registered_name: []
exception exit: {{owner_gone,normal},
[{gun,loop,1,[{file,"src/gun.erl"},{line,706}]},
{gun,proc_lib_hack,5,[{file,"src/gun.erl"},{line,535}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,247}]}]}
in function gun:proc_lib_hack/5 (src/gun.erl, line 540)
ancestors: [gun_sup,<0.343.0>]
messages: []
links: [<0.344.0>]
dictionary: []
trap_exit: false
status: running
heap_size: 2586
stack_size: 27
reductions: 10857
neighbours:
=SUPERVISOR REPORT==== 10-Jul-2017::05:25:17 ===
Supervisor: {local,gun_sup}
Context: child_terminated
Reason: {{owner_gone,normal},
[{gun,loop,1,[{file,"src/gun.erl"},{line,706}]},
{gun,proc_lib_hack,5,[{file,"src/gun.erl"},{line,535}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,247}]}]}
Offender: [{pid,<0.364.0>},
{id,gun},
{mfargs,{gun,start_link,undefined}},
{restart_type,temporary},
{shutdown,5000},
{child_type,worker}]
** exception exit: {ws_upgrade_failed,404,
[{<<"content-length">>,<<"0">>},
{<<"date">>,
<<"Mon, 10 Jul 2017 11:22:38 GMT">>},
{<<"server">>,<<"Cowboy">>}]}
(my_gun@127.0.0.1)6>
现在,我收到 404 错误。 Cowboy 是 运行,当我输入 http://localhost:8080 in my browser
时,我看到一条响应消息。为什么 Gun 给我一个 404 错误?
接下来我尝试使用 Gun docs 中的说明来发出 GET 请求:
StreamRef = gun:get(ConnPid, "/").
case gun:await(ConnPid, StreamRef) of
{response, fin, Status, Headers} ->
no_data;
{response, nofin, Status, Headers} ->
{ok, Body} = gun:await_body(ConnPid, StreamRef),
io:format("~s~n", [Body])
end.
那是成功的:
=PROGRESS REPORT==== 10-Jul-2017::06:36:14 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::06:36:14 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
Hello Erlang!
ok
(my_gun@127.0.0.1)2>
响应意味着我能够使用 Gun 与 Cowboy 服务器交互——但我想使用 websockets。知道我做错了什么吗?
tip:
为了避免在 gun shell 中键入所有代码,我创建了文件 ~/erlang_programs/my_gun/src/my.erl
:
-module(my).
-compile(export_all).
get() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
StreamRef = gun:get(ConnPid, "/"),
case gun:await(ConnPid, StreamRef) of
{response, fin, _Status, _Headers} ->
no_data;
{response, nofin, _Status, _Headers} ->
{ok, Body} = gun:await_body(ConnPid, StreamRef),
io:format("~s~n", [Body])
end.
ws() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
gun:ws_upgrade(ConnPid, "/websocket"),
receive
{gun_ws_upgrade, ConnPid, ok, Headers} ->
upgrade_success(ConnPid, Headers);
{gun_response, ConnPid, _, _, Status, Headers} ->
exit({ws_upgrade_failed, Status, Headers});
{gun_error, _ConnPid, _StreamRef, Reason} ->
exit({ws_upgrade_failed, Reason})
%% More clauses here as needed.
after 1000 ->
exit(timeout)
end,
gun:shutdown(ConnPid).
upgrade_success(ConnPid, Headers) ->
io:format("Upgraded ~w. Success!~nHeaders:~n~p~n",
[ConnPid, Headers]).
然后make(或gmake)命令:
~/erlang_programs/my_gun$ gmake run
将编译 src/
目录中的所有内容并提醒您任何错误。一旦枪 shell 响应 gmake run
成功发射,您可以执行以下操作:
(my_gun@127.0.0.1)1> my:get().
=PROGRESS REPORT==== 10-Jul-2017::06:36:14 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::06:36:14 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
Hello Erlang!
ok
(my_gun@127.0.0.1)2>
回复评论:
Since you're getting a 404, I guess you don't have a websocket handler
defined in the cowboy routes.
你是对的。我只有 Cowboy 入门指南中显示的处理程序。现在,我已经将 websocket 设置代码和 websocket 处理程序添加到 cowboy。我现在有路线:
hello_erlang_app.erl:
-module(hello_erlang_app).
-behaviour(application).
-export([start/2]).
-export([stop/1]).
start(_Type, _Args) ->
Dispatch = cowboy_router:compile([
{'_', [{"/", hello_handler, []}] },
{'_', [{"/websocket", myws_handler, []}] }
]),
{ok, _} = cowboy:start_clear(my_http_listener,
[{port, 8080}],
#{env => #{dispatch => Dispatch} }
),
hello_erlang_sup:start_link().
stop(_State) ->
ok.
这是我的处理程序:
-module(myws_handler).
-compile(export_all).
init(Req, State) ->
{cowboy_websocket, Req, State}. %Perform websocket setup
websocket_handle({text, Msg}, State) ->
{
reply,
{text, <<"Server received: ", Msg/binary>>, State} %%Error in format here, too!
};
websocket_handle(_Data, State) ->
{ok, State}.
但是我在gunshell:
中执行my:ws()
时还是报404错误
-module(my).
-compile(export_all).
get() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
StreamRef = gun:get(ConnPid, "/"),
case gun:await(ConnPid, StreamRef) of
{response, fin, _Status, _Headers} ->
no_data;
{response, nofin, _Status, _Headers} ->
{ok, Body} = gun:await_body(ConnPid, StreamRef),
io:format("~s~n", [Body])
end.
ws() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
gun:ws_upgrade(ConnPid, "/websocket"),
receive
{gun_ws_upgrade, ConnPid, ok, Headers} ->
upgrade_success(ConnPid, Headers);
{gun_response, ConnPid, _, _, Status, Headers} ->
exit({ws_upgrade_failed, Status, Headers});
{gun_error, _ConnPid, _StreamRef, Reason} ->
exit({ws_upgrade_failed, Reason})
%% More clauses here as needed.
after 1000 ->
exit(timeout)
end,
gun:shutdown(ConnPid).
upgrade_success(ConnPid, Headers) ->
io:format("Upgraded ~w. Success!~nHeaders:~n~w~n",
[ConnPid, Headers]).
这是输出:
~/erlang_programs/my_gun$ gmake run
gmake[1]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
GEN rebar.config
gmake[1]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
DEPEND my_gun.d
ERLC my.erl
APP my_gun
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
/Users/7stud/erlang_programs/my_gun/ebin
/Users/7stud/erlang_programs/my_gun/deps
/Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang/lib
/Users/7stud/erlang_programs/my_gun/apps
/Users/7stud/erlang_programs/my_gun/_rel
===> Resolved my_gun_release-1
===> rendering builtin_hook_status hook to "/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/bin/hooks/builtin/status"
===> Including Erts from /Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang
===> release successfully created!
===> tarball /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/my_gun_release-1.tar.gz successfully created!
Exec: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/erts-8.2/bin/erlexec -boot /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/my_gun_release -mode embedded -boot_var ERTS_LIB_DIR /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/lib -config /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/sys.config -args_file /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/vm.args -pa -- console
Root: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
heart_beat_kill_pid = 32843
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
supervisor: {local,sasl_safe_sup}
started: [{pid,<0.353.0>},
{id,alarm_handler},
{mfargs,{alarm_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.352.0>},
{id,sasl_safe_sup},
{mfargs,
{supervisor,start_link,
[{local,sasl_safe_sup},sasl,safe]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.354.0>},
{id,release_handler},
{mfargs,{release_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
application: sasl
started_at: 'my_gun@127.0.0.1'
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
supervisor: {local,runtime_tools_sup}
started: [{pid,<0.360.0>},
{id,ttb_autostart},
{mfargs,{ttb_autostart,start_link,[]}},
{restart_type,temporary},
{shutdown,3000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
application: runtime_tools
started_at: 'my_gun@127.0.0.1'
Eshell V8.2 (abort with ^G)
(my_gun@127.0.0.1)1> my:ws().
=PROGRESS REPORT==== 10-Jul-2017::16:26:08 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:08 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
=CRASH REPORT==== 10-Jul-2017::16:26:08 ===
crasher:
initial call: gun:proc_lib_hack/5
pid: <0.365.0>
registered_name: []
exception exit: {{owner_gone,normal},
[{gun,loop,1,[{file,"src/gun.erl"},{line,706}]},
{gun,proc_lib_hack,5,[{file,"src/gun.erl"},{line,535}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,247}]}]}
in function gun:proc_lib_hack/5 (src/gun.erl, line 540)
ancestors: [gun_sup,<0.345.0>]
messages: []
links: [<0.346.0>]
dictionary: []
trap_exit: false
status: running
heap_size: 610
stack_size: 27
reductions: 1042
neighbours:
=SUPERVISOR REPORT==== 10-Jul-2017::16:26:08 ===
Supervisor: {local,gun_sup}
Context: child_terminated
Reason: {{owner_gone,normal},
[{gun,loop,1,[{file,"src/gun.erl"},{line,706}]},
{gun,proc_lib_hack,5,[{file,"src/gun.erl"},{line,535}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,247}]}]}
Offender: [{pid,<0.365.0>},
{id,gun},
{mfargs,{gun,start_link,undefined}},
{restart_type,temporary},
{shutdown,5000},
{child_type,worker}]
** exception exit: {ws_upgrade_failed,404,
[{<<"content-length">>,<<"0">>},
{<<"date">>,
<<"Mon, 10 Jul 2017 22:26:08 GMT">>},
{<<"server">>,<<"Cowboy">>}]}
in function my:ws/0 (src/my.erl, line 30)
(my_gun@127.0.0.1)2>
我保存了所有文件并重新启动了 cowboy 和 gun,因此我对代码所做的更改正在执行,但我仍然收到 404 错误。
我将我的路由格式与评论中 spawn_think 链接的示例中的路由进行了比较,我的格式是错误的。这是我现在拥有的:
-module(hello_erlang_app).
-behaviour(application).
-export([start/2]).
-export([stop/1]).
start(_Type, _Args) ->
Dispatch = cowboy_router:compile([
{'_', [
{"/", hello_handler, []},
{"/websocket", myws_handler, []}
]}
]),
{ok, _} = cowboy:start_clear(my_http_listener,
[{port, 8080}],
#{env => #{dispatch => Dispatch} }
),
hello_erlang_sup:start_link().
stop(_State) ->
ok.
并且在我的 gun 客户端中调整了 io:format() 语句中的一个控制序列之后:
-module(my).
-compile(export_all).
get() ->
...
ws() ->
...
upgrade_success(ConnPid, Headers) ->
io:format("Upgraded ~w. Success!~nHeaders:~n~p~n", %% <*** CHANGED ~w to ~p
[ConnPid, Headers]).
这是输出:
~/erlang_programs/my_gun$ gmake run
gmake[1]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
GEN rebar.config
gmake[1]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
DEPEND my_gun.d
ERLC my.erl
APP my_gun
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
/Users/7stud/erlang_programs/my_gun/ebin
/Users/7stud/erlang_programs/my_gun/deps
/Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang/lib
/Users/7stud/erlang_programs/my_gun/apps
/Users/7stud/erlang_programs/my_gun/_rel
===> Resolved my_gun_release-1
===> rendering builtin_hook_status hook to "/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/bin/hooks/builtin/status"
===> Including Erts from /Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang
===> release successfully created!
===> tarball /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/my_gun_release-1.tar.gz successfully created!
Exec: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/erts-8.2/bin/erlexec -boot /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/my_gun_release -mode embedded -boot_var ERTS_LIB_DIR /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/lib -config /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/sys.config -args_file /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/vm.args -pa -- console
Root: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
heart_beat_kill_pid = 34141
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
supervisor: {local,sasl_safe_sup}
started: [{pid,<0.353.0>},
{id,alarm_handler},
{mfargs,{alarm_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.352.0>},
{id,sasl_safe_sup},
{mfargs,
{supervisor,start_link,
[{local,sasl_safe_sup},sasl,safe]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.354.0>},
{id,release_handler},
{mfargs,{release_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
application: sasl
started_at: 'my_gun@127.0.0.1'
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
supervisor: {local,runtime_tools_sup}
started: [{pid,<0.360.0>},
{id,ttb_autostart},
{mfargs,{ttb_autostart,start_link,[]}},
{restart_type,temporary},
{shutdown,3000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
application: runtime_tools
started_at: 'my_gun@127.0.0.1'
Eshell V8.2 (abort with ^G)
(my_gun@127.0.0.1)1> my:ws().
=PROGRESS REPORT==== 10-Jul-2017::16:50:57 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:57 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
Upgraded <0.365.0>. Success!
Headers:
[{<<"connection">>,<<"Upgrade">>},
{<<"date">>,<<"Mon, 10 Jul 2017 22:50:56 GMT">>},
{<<"sec-websocket-accept">>,<<"S1w7rWXToZefi/NOEcDAEDb4yEU=">>},
{<<"server">>,<<"Cowboy">>},
{<<"upgrade">>,<<"websocket">>}]
ok
(my_gun@127.0.0.1)2>
我已经达到了论坛的文本限制,所以请参阅我的回答,了解我实际上是如何使用 websocket 发送和接收数据的。
成功!
~/erlang_programs/my_gun/src/my.erl:
-module(my).
-compile(export_all).
get() ->
...
ws() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
gun:ws_upgrade(ConnPid, "/websocket"),
receive
{gun_ws_upgrade, ConnPid, ok, Headers} ->
upgrade_success(ConnPid, Headers);
{gun_response, ConnPid, _, _, Status, Headers} ->
exit({ws_upgrade_failed, Status, Headers});
{gun_error, _ConnPid, _StreamRef, Reason} ->
exit({ws_upgrade_failed, Reason})
%% More clauses here as needed.
after 1000 ->
exit(timeout)
end,
upgrade_success(ConnPid, Headers) ->
io:format("Upgraded ~w. Success!~nHeaders:~n~p~n",
[ConnPid, Headers]),
gun:ws_send(ConnPid, {text, "It's raining!"}),
receive
{gun_ws, ConnPid, {text, Msg} } ->
io:format("~s~n", [Msg])
end.
牛仔方面:
-module(myws_handler).
-compile(export_all).
init(Req, State) ->
{cowboy_websocket, Req, State}. %Perform websocket setup
websocket_handle({text, Msg}, State) ->
{
reply,
{text, io_lib:format("Server received: ~s", [Msg]) },
State
};
websocket_handle(_Other, State) -> %Ignore
{ok, State}.
这是输出:
~/erlang_programs/my_gun$ gmake run
gmake[1]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
GEN rebar.config
gmake[1]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
DEPEND my_gun.d
ERLC my.erl
APP my_gun
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
/Users/7stud/erlang_programs/my_gun/ebin
/Users/7stud/erlang_programs/my_gun/deps
/Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang/lib
/Users/7stud/erlang_programs/my_gun/apps
/Users/7stud/erlang_programs/my_gun/_rel
===> Resolved my_gun_release-1
===> rendering builtin_hook_status hook to "/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/bin/hooks/builtin/status"
===> Including Erts from /Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang
===> release successfully created!
===> tarball /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/my_gun_release-1.tar.gz successfully created!
Exec: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/erts-8.2/bin/erlexec -boot /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/my_gun_release -mode embedded -boot_var ERTS_LIB_DIR /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/lib -config /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/sys.config -args_file /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/vm.args -pa -- console
Root: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
heart_beat_kill_pid = 38883
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
supervisor: {local,sasl_safe_sup}
started: [{pid,<0.353.0>},
{id,alarm_handler},
{mfargs,{alarm_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.352.0>},
{id,sasl_safe_sup},
{mfargs,
{supervisor,start_link,
[{local,sasl_safe_sup},sasl,safe]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.354.0>},
{id,release_handler},
{mfargs,{release_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
application: sasl
started_at: 'my_gun@127.0.0.1'
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
supervisor: {local,runtime_tools_sup}
started: [{pid,<0.360.0>},
{id,ttb_autostart},
{mfargs,{ttb_autostart,start_link,[]}},
{restart_type,temporary},
{shutdown,3000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
application: runtime_tools
started_at: 'my_gun@127.0.0.1'
Eshell V8.2 (abort with ^G)
(my_gun@127.0.0.1)1> my:ws().
=PROGRESS REPORT==== 10-Jul-2017::18:04:41 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:41 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
Upgraded <0.365.0>. Success!
Headers:
[{<<"connection">>,<<"Upgrade">>},
{<<"date">>,<<"Tue, 11 Jul 2017 00:04:40 GMT">>},
{<<"sec-websocket-accept">>,<<"pYv8QeeJfzQgaS/x8flZHyrIexk=">>},
{<<"server">>,<<"Cowboy">>},
{<<"upgrade">>,<<"websocket">>}]
Server received: It's raining!
ok
(my_gun@127.0.0.1)2>
来自评论讨论issues/suggestions的摘要
对于 运行 你的 Erlang 应用程序(在本例中包括 gun 作为依赖项),你需要有 shell 可发现的 beam 和 .app 文件的路径.使用 erlang.mk 时,最简单的方法是 运行 使用 make run
发布
在 Cowboy 方面,要正确处理 Websocket,您需要在 cowboy 路由中定义一个处理程序。请参阅 Cowboy Websocket example
中的示例
有关在 Gun 中处理 Websocket 的更多详细信息,另请参阅 Gun user guide
我按照 Cowboy 的 Getting Started instructions,我有 Cowboy 运行 并在端口 8080 上侦听,当我输入 [=31= 时,我得到了 Hello Erlang!
响应] 在我的浏览器中。现在,如何使用 Gun 连接到 Cowboy?
我看了Gun docs,它说要添加"Gun as an erlang.mk dependency"。所以我下载了 erlang.mk:
~/erlang_programs/my_gun$ curl -O https://erlang.mk/erlang.mk
并在 Erlang.mk User Guide 之后,我创建了一个应用程序:
~/erlang_programs/my_gun$ gmake -f erlang.mk bootstrap
然后我将 gun 添加为 Makefile 的依赖项:
PROJECT = my_gun
PROJECT_DESCRIPTION = New project
PROJECT_VERSION = 0.1.0
DEPS = gun
include erlang.mk
然后我编译:
~/erlang_programs/my_gun$ gmake
gmake[1]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
ERLC cow_cookie.erl cow_date.erl cow_hpack.erl cow_http.erl cow_http2.erl cow_http_hd.erl cow_http_te.erl cow_mimetypes.erl cow_multipart.erl cow_qs.erl cow_spdy.erl cow_sse.erl cow_uri.erl cow_ws.erl
APP cowlib
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
DEPEND ranch.d
ERLC ranch.erl ranch_acceptor.erl ranch_acceptors_sup.erl ranch_app.erl ranch_conns_sup.erl ranch_listener_sup.erl ranch_protocol.erl ranch_server.erl ranch_ssl.erl ranch_sup.erl ranch_tcp.erl ranch_transport.erl
APP ranch
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
DEPEND gun.d
ERLC gun.erl gun_app.erl gun_content_handler.erl gun_data.erl gun_http.erl gun_http2.erl gun_spdy.erl gun_sse.erl gun_sup.erl gun_ws.erl gun_ws_handler.erl
APP gun
GEN rebar.config
gmake[1]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
DEPEND my_gun.d
ERLC my_gun_app.erl my_gun_sup.erl
APP my_gun
GEN /Users/7stud/erlang_programs/my_gun/.erlang.mk/relx
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
/Users/7stud/erlang_programs/my_gun/ebin
/Users/7stud/erlang_programs/my_gun/deps
/Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang/lib
/Users/7stud/erlang_programs/my_gun/apps
===> Resolved my_gun_release-1
===> rendering builtin_hook_status hook to "/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/bin/hooks/builtin/status"
===> Including Erts from /Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang
===> release successfully created!
===> tarball /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/my_gun_release-1.tar.gz successfully created!
但是当我切换到 erlang shell 并尝试启动 gun 时,我得到一个错误:
~/erlang_programs/my_gun$ erl
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2 (abort with ^G)
1> application:ensure_all_started(gun).
{error,{gun,{"no such file or directory","gun.app"}}}
有人可以 post 一个简单的例子来说明如何使用 Gun(或任何其他支持 websocket 的 http 客户端)连接到 Cowboy 吗?
好的,我取得了一些进展。我删除了 my_gun 目录,重新创建目录,重新下载 erlang.mk,并创建了 a release,其中 command:
~/erlang_programs/my_gun$ gmake -f erlang.mk bootstrap-lib bootstrap-rel
然后我将 gun 依赖项添加到 Makefile 中(如上所述)。然后我做了:
~/erlang_programs/my_gun$ gmake run
如果代码中没有错误,则会启动一个 erlang shell。在 erlang shell 中,我输入了以下代码(请参阅下面的 tip
以避免必须在 shell 中键入所有代码):
(my_gun@127.0.0.1)1> application:ensure_all_started(gun).
{ok,[]}
(my_gun@127.0.0.1)2> {ok, ConnPid} = gun:open("localhost", 8080).
=PROGRESS REPORT==== 10-Jul-2017::05:21:58 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.366.0>},{mfa,{inet_gethost_native,init,[[]]}}]
{ok,<0.364.0>}
=PROGRESS REPORT==== 10-Jul-2017::05:21:58 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.365.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
(my_gun@127.0.0.1)3> {ok, Protocol} = gun:await_up(ConnPid).
{ok,http}
(my_gun@127.0.0.1)4> gun:ws_upgrade(ConnPid, "/websocket").
#Ref<0.0.3.244>
(my_gun@127.0.0.1)5> receive
(my_gun@127.0.0.1)5> {gun_ws_upgrade, ConnPid, ok, Headers} ->
(my_gun@127.0.0.1)5> upgrade_success(ConnPid);
(my_gun@127.0.0.1)5> {gun_response, ConnPid, _, _, Status, Headers} ->
(my_gun@127.0.0.1)5> exit({ws_upgrade_failed, Status, Headers});
(my_gun@127.0.0.1)5> {gun_error, ConnPid, StreamRef, Reason} ->
(my_gun@127.0.0.1)5> exit({ws_upgrade_failed, Reason})
(my_gun@127.0.0.1)5> after 1000 ->
(my_gun@127.0.0.1)5> exit(timeout)
(my_gun@127.0.0.1)5> end.
=CRASH REPORT==== 10-Jul-2017::05:25:17 ===
crasher:
initial call: gun:proc_lib_hack/5
pid: <0.364.0>
registered_name: []
exception exit: {{owner_gone,normal},
[{gun,loop,1,[{file,"src/gun.erl"},{line,706}]},
{gun,proc_lib_hack,5,[{file,"src/gun.erl"},{line,535}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,247}]}]}
in function gun:proc_lib_hack/5 (src/gun.erl, line 540)
ancestors: [gun_sup,<0.343.0>]
messages: []
links: [<0.344.0>]
dictionary: []
trap_exit: false
status: running
heap_size: 2586
stack_size: 27
reductions: 10857
neighbours:
=SUPERVISOR REPORT==== 10-Jul-2017::05:25:17 ===
Supervisor: {local,gun_sup}
Context: child_terminated
Reason: {{owner_gone,normal},
[{gun,loop,1,[{file,"src/gun.erl"},{line,706}]},
{gun,proc_lib_hack,5,[{file,"src/gun.erl"},{line,535}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,247}]}]}
Offender: [{pid,<0.364.0>},
{id,gun},
{mfargs,{gun,start_link,undefined}},
{restart_type,temporary},
{shutdown,5000},
{child_type,worker}]
** exception exit: {ws_upgrade_failed,404,
[{<<"content-length">>,<<"0">>},
{<<"date">>,
<<"Mon, 10 Jul 2017 11:22:38 GMT">>},
{<<"server">>,<<"Cowboy">>}]}
(my_gun@127.0.0.1)6>
现在,我收到 404 错误。 Cowboy 是 运行,当我输入 http://localhost:8080 in my browser
时,我看到一条响应消息。为什么 Gun 给我一个 404 错误?
接下来我尝试使用 Gun docs 中的说明来发出 GET 请求:
StreamRef = gun:get(ConnPid, "/").
case gun:await(ConnPid, StreamRef) of
{response, fin, Status, Headers} ->
no_data;
{response, nofin, Status, Headers} ->
{ok, Body} = gun:await_body(ConnPid, StreamRef),
io:format("~s~n", [Body])
end.
那是成功的:
=PROGRESS REPORT==== 10-Jul-2017::06:36:14 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::06:36:14 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
Hello Erlang!
ok
(my_gun@127.0.0.1)2>
响应意味着我能够使用 Gun 与 Cowboy 服务器交互——但我想使用 websockets。知道我做错了什么吗?
tip:
为了避免在 gun shell 中键入所有代码,我创建了文件 ~/erlang_programs/my_gun/src/my.erl
:
-module(my).
-compile(export_all).
get() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
StreamRef = gun:get(ConnPid, "/"),
case gun:await(ConnPid, StreamRef) of
{response, fin, _Status, _Headers} ->
no_data;
{response, nofin, _Status, _Headers} ->
{ok, Body} = gun:await_body(ConnPid, StreamRef),
io:format("~s~n", [Body])
end.
ws() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
gun:ws_upgrade(ConnPid, "/websocket"),
receive
{gun_ws_upgrade, ConnPid, ok, Headers} ->
upgrade_success(ConnPid, Headers);
{gun_response, ConnPid, _, _, Status, Headers} ->
exit({ws_upgrade_failed, Status, Headers});
{gun_error, _ConnPid, _StreamRef, Reason} ->
exit({ws_upgrade_failed, Reason})
%% More clauses here as needed.
after 1000 ->
exit(timeout)
end,
gun:shutdown(ConnPid).
upgrade_success(ConnPid, Headers) ->
io:format("Upgraded ~w. Success!~nHeaders:~n~p~n",
[ConnPid, Headers]).
然后make(或gmake)命令:
~/erlang_programs/my_gun$ gmake run
将编译 src/
目录中的所有内容并提醒您任何错误。一旦枪 shell 响应 gmake run
成功发射,您可以执行以下操作:
(my_gun@127.0.0.1)1> my:get().
=PROGRESS REPORT==== 10-Jul-2017::06:36:14 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::06:36:14 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
Hello Erlang!
ok
(my_gun@127.0.0.1)2>
回复评论:
Since you're getting a 404, I guess you don't have a websocket handler defined in the cowboy routes.
你是对的。我只有 Cowboy 入门指南中显示的处理程序。现在,我已经将 websocket 设置代码和 websocket 处理程序添加到 cowboy。我现在有路线:
hello_erlang_app.erl:
-module(hello_erlang_app).
-behaviour(application).
-export([start/2]).
-export([stop/1]).
start(_Type, _Args) ->
Dispatch = cowboy_router:compile([
{'_', [{"/", hello_handler, []}] },
{'_', [{"/websocket", myws_handler, []}] }
]),
{ok, _} = cowboy:start_clear(my_http_listener,
[{port, 8080}],
#{env => #{dispatch => Dispatch} }
),
hello_erlang_sup:start_link().
stop(_State) ->
ok.
这是我的处理程序:
-module(myws_handler).
-compile(export_all).
init(Req, State) ->
{cowboy_websocket, Req, State}. %Perform websocket setup
websocket_handle({text, Msg}, State) ->
{
reply,
{text, <<"Server received: ", Msg/binary>>, State} %%Error in format here, too!
};
websocket_handle(_Data, State) ->
{ok, State}.
但是我在gunshell:
中执行my:ws()
时还是报404错误
-module(my).
-compile(export_all).
get() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
StreamRef = gun:get(ConnPid, "/"),
case gun:await(ConnPid, StreamRef) of
{response, fin, _Status, _Headers} ->
no_data;
{response, nofin, _Status, _Headers} ->
{ok, Body} = gun:await_body(ConnPid, StreamRef),
io:format("~s~n", [Body])
end.
ws() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
gun:ws_upgrade(ConnPid, "/websocket"),
receive
{gun_ws_upgrade, ConnPid, ok, Headers} ->
upgrade_success(ConnPid, Headers);
{gun_response, ConnPid, _, _, Status, Headers} ->
exit({ws_upgrade_failed, Status, Headers});
{gun_error, _ConnPid, _StreamRef, Reason} ->
exit({ws_upgrade_failed, Reason})
%% More clauses here as needed.
after 1000 ->
exit(timeout)
end,
gun:shutdown(ConnPid).
upgrade_success(ConnPid, Headers) ->
io:format("Upgraded ~w. Success!~nHeaders:~n~w~n",
[ConnPid, Headers]).
这是输出:
~/erlang_programs/my_gun$ gmake run
gmake[1]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
GEN rebar.config
gmake[1]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
DEPEND my_gun.d
ERLC my.erl
APP my_gun
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
/Users/7stud/erlang_programs/my_gun/ebin
/Users/7stud/erlang_programs/my_gun/deps
/Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang/lib
/Users/7stud/erlang_programs/my_gun/apps
/Users/7stud/erlang_programs/my_gun/_rel
===> Resolved my_gun_release-1
===> rendering builtin_hook_status hook to "/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/bin/hooks/builtin/status"
===> Including Erts from /Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang
===> release successfully created!
===> tarball /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/my_gun_release-1.tar.gz successfully created!
Exec: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/erts-8.2/bin/erlexec -boot /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/my_gun_release -mode embedded -boot_var ERTS_LIB_DIR /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/lib -config /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/sys.config -args_file /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/vm.args -pa -- console
Root: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
heart_beat_kill_pid = 32843
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
supervisor: {local,sasl_safe_sup}
started: [{pid,<0.353.0>},
{id,alarm_handler},
{mfargs,{alarm_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.352.0>},
{id,sasl_safe_sup},
{mfargs,
{supervisor,start_link,
[{local,sasl_safe_sup},sasl,safe]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.354.0>},
{id,release_handler},
{mfargs,{release_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
application: sasl
started_at: 'my_gun@127.0.0.1'
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
supervisor: {local,runtime_tools_sup}
started: [{pid,<0.360.0>},
{id,ttb_autostart},
{mfargs,{ttb_autostart,start_link,[]}},
{restart_type,temporary},
{shutdown,3000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:05 ===
application: runtime_tools
started_at: 'my_gun@127.0.0.1'
Eshell V8.2 (abort with ^G)
(my_gun@127.0.0.1)1> my:ws().
=PROGRESS REPORT==== 10-Jul-2017::16:26:08 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::16:26:08 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
=CRASH REPORT==== 10-Jul-2017::16:26:08 ===
crasher:
initial call: gun:proc_lib_hack/5
pid: <0.365.0>
registered_name: []
exception exit: {{owner_gone,normal},
[{gun,loop,1,[{file,"src/gun.erl"},{line,706}]},
{gun,proc_lib_hack,5,[{file,"src/gun.erl"},{line,535}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,247}]}]}
in function gun:proc_lib_hack/5 (src/gun.erl, line 540)
ancestors: [gun_sup,<0.345.0>]
messages: []
links: [<0.346.0>]
dictionary: []
trap_exit: false
status: running
heap_size: 610
stack_size: 27
reductions: 1042
neighbours:
=SUPERVISOR REPORT==== 10-Jul-2017::16:26:08 ===
Supervisor: {local,gun_sup}
Context: child_terminated
Reason: {{owner_gone,normal},
[{gun,loop,1,[{file,"src/gun.erl"},{line,706}]},
{gun,proc_lib_hack,5,[{file,"src/gun.erl"},{line,535}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,247}]}]}
Offender: [{pid,<0.365.0>},
{id,gun},
{mfargs,{gun,start_link,undefined}},
{restart_type,temporary},
{shutdown,5000},
{child_type,worker}]
** exception exit: {ws_upgrade_failed,404,
[{<<"content-length">>,<<"0">>},
{<<"date">>,
<<"Mon, 10 Jul 2017 22:26:08 GMT">>},
{<<"server">>,<<"Cowboy">>}]}
in function my:ws/0 (src/my.erl, line 30)
(my_gun@127.0.0.1)2>
我保存了所有文件并重新启动了 cowboy 和 gun,因此我对代码所做的更改正在执行,但我仍然收到 404 错误。
我将我的路由格式与评论中 spawn_think 链接的示例中的路由进行了比较,我的格式是错误的。这是我现在拥有的:
-module(hello_erlang_app).
-behaviour(application).
-export([start/2]).
-export([stop/1]).
start(_Type, _Args) ->
Dispatch = cowboy_router:compile([
{'_', [
{"/", hello_handler, []},
{"/websocket", myws_handler, []}
]}
]),
{ok, _} = cowboy:start_clear(my_http_listener,
[{port, 8080}],
#{env => #{dispatch => Dispatch} }
),
hello_erlang_sup:start_link().
stop(_State) ->
ok.
并且在我的 gun 客户端中调整了 io:format() 语句中的一个控制序列之后:
-module(my).
-compile(export_all).
get() ->
...
ws() ->
...
upgrade_success(ConnPid, Headers) ->
io:format("Upgraded ~w. Success!~nHeaders:~n~p~n", %% <*** CHANGED ~w to ~p
[ConnPid, Headers]).
这是输出:
~/erlang_programs/my_gun$ gmake run
gmake[1]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
GEN rebar.config
gmake[1]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
DEPEND my_gun.d
ERLC my.erl
APP my_gun
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
/Users/7stud/erlang_programs/my_gun/ebin
/Users/7stud/erlang_programs/my_gun/deps
/Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang/lib
/Users/7stud/erlang_programs/my_gun/apps
/Users/7stud/erlang_programs/my_gun/_rel
===> Resolved my_gun_release-1
===> rendering builtin_hook_status hook to "/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/bin/hooks/builtin/status"
===> Including Erts from /Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang
===> release successfully created!
===> tarball /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/my_gun_release-1.tar.gz successfully created!
Exec: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/erts-8.2/bin/erlexec -boot /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/my_gun_release -mode embedded -boot_var ERTS_LIB_DIR /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/lib -config /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/sys.config -args_file /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/vm.args -pa -- console
Root: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
heart_beat_kill_pid = 34141
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
supervisor: {local,sasl_safe_sup}
started: [{pid,<0.353.0>},
{id,alarm_handler},
{mfargs,{alarm_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.352.0>},
{id,sasl_safe_sup},
{mfargs,
{supervisor,start_link,
[{local,sasl_safe_sup},sasl,safe]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.354.0>},
{id,release_handler},
{mfargs,{release_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
application: sasl
started_at: 'my_gun@127.0.0.1'
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
supervisor: {local,runtime_tools_sup}
started: [{pid,<0.360.0>},
{id,ttb_autostart},
{mfargs,{ttb_autostart,start_link,[]}},
{restart_type,temporary},
{shutdown,3000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:53 ===
application: runtime_tools
started_at: 'my_gun@127.0.0.1'
Eshell V8.2 (abort with ^G)
(my_gun@127.0.0.1)1> my:ws().
=PROGRESS REPORT==== 10-Jul-2017::16:50:57 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::16:50:57 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
Upgraded <0.365.0>. Success!
Headers:
[{<<"connection">>,<<"Upgrade">>},
{<<"date">>,<<"Mon, 10 Jul 2017 22:50:56 GMT">>},
{<<"sec-websocket-accept">>,<<"S1w7rWXToZefi/NOEcDAEDb4yEU=">>},
{<<"server">>,<<"Cowboy">>},
{<<"upgrade">>,<<"websocket">>}]
ok
(my_gun@127.0.0.1)2>
我已经达到了论坛的文本限制,所以请参阅我的回答,了解我实际上是如何使用 websocket 发送和接收数据的。
成功!
~/erlang_programs/my_gun/src/my.erl:
-module(my).
-compile(export_all).
get() ->
...
ws() ->
{ok, _} = application:ensure_all_started(gun),
{ok, ConnPid} = gun:open("localhost", 8080),
{ok, _Protocol} = gun:await_up(ConnPid),
gun:ws_upgrade(ConnPid, "/websocket"),
receive
{gun_ws_upgrade, ConnPid, ok, Headers} ->
upgrade_success(ConnPid, Headers);
{gun_response, ConnPid, _, _, Status, Headers} ->
exit({ws_upgrade_failed, Status, Headers});
{gun_error, _ConnPid, _StreamRef, Reason} ->
exit({ws_upgrade_failed, Reason})
%% More clauses here as needed.
after 1000 ->
exit(timeout)
end,
upgrade_success(ConnPid, Headers) ->
io:format("Upgraded ~w. Success!~nHeaders:~n~p~n",
[ConnPid, Headers]),
gun:ws_send(ConnPid, {text, "It's raining!"}),
receive
{gun_ws, ConnPid, {text, Msg} } ->
io:format("~s~n", [Msg])
end.
牛仔方面:
-module(myws_handler).
-compile(export_all).
init(Req, State) ->
{cowboy_websocket, Req, State}. %Perform websocket setup
websocket_handle({text, Msg}, State) ->
{
reply,
{text, io_lib:format("Server received: ~s", [Msg]) },
State
};
websocket_handle(_Other, State) -> %Ignore
{ok, State}.
这是输出:
~/erlang_programs/my_gun$ gmake run
gmake[1]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/cowlib'
gmake[2]: Entering directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
gmake[2]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/ranch'
GEN rebar.config
gmake[1]: Leaving directory '/Users/7stud/erlang_programs/my_gun/deps/gun'
DEPEND my_gun.d
ERLC my.erl
APP my_gun
===> Starting relx build process ...
===> Resolving OTP Applications from directories:
/Users/7stud/erlang_programs/my_gun/ebin
/Users/7stud/erlang_programs/my_gun/deps
/Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang/lib
/Users/7stud/erlang_programs/my_gun/apps
/Users/7stud/erlang_programs/my_gun/_rel
===> Resolved my_gun_release-1
===> rendering builtin_hook_status hook to "/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/bin/hooks/builtin/status"
===> Including Erts from /Users/7stud/.evm/erlang_versions/otp_src_19.2/lib/erlang
===> release successfully created!
===> tarball /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/my_gun_release-1.tar.gz successfully created!
Exec: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/erts-8.2/bin/erlexec -boot /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/my_gun_release -mode embedded -boot_var ERTS_LIB_DIR /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/lib -config /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/sys.config -args_file /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release/releases/1/vm.args -pa -- console
Root: /Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
/Users/7stud/erlang_programs/my_gun/_rel/my_gun_release
heart_beat_kill_pid = 38883
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
supervisor: {local,sasl_safe_sup}
started: [{pid,<0.353.0>},
{id,alarm_handler},
{mfargs,{alarm_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.352.0>},
{id,sasl_safe_sup},
{mfargs,
{supervisor,start_link,
[{local,sasl_safe_sup},sasl,safe]}},
{restart_type,permanent},
{shutdown,infinity},
{child_type,supervisor}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
supervisor: {local,sasl_sup}
started: [{pid,<0.354.0>},
{id,release_handler},
{mfargs,{release_handler,start_link,[]}},
{restart_type,permanent},
{shutdown,2000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
application: sasl
started_at: 'my_gun@127.0.0.1'
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
supervisor: {local,runtime_tools_sup}
started: [{pid,<0.360.0>},
{id,ttb_autostart},
{mfargs,{ttb_autostart,start_link,[]}},
{restart_type,temporary},
{shutdown,3000},
{child_type,worker}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:38 ===
application: runtime_tools
started_at: 'my_gun@127.0.0.1'
Eshell V8.2 (abort with ^G)
(my_gun@127.0.0.1)1> my:ws().
=PROGRESS REPORT==== 10-Jul-2017::18:04:41 ===
supervisor: {local,inet_gethost_native_sup}
started: [{pid,<0.367.0>},{mfa,{inet_gethost_native,init,[[]]}}]
=PROGRESS REPORT==== 10-Jul-2017::18:04:41 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.366.0>},
{id,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
Upgraded <0.365.0>. Success!
Headers:
[{<<"connection">>,<<"Upgrade">>},
{<<"date">>,<<"Tue, 11 Jul 2017 00:04:40 GMT">>},
{<<"sec-websocket-accept">>,<<"pYv8QeeJfzQgaS/x8flZHyrIexk=">>},
{<<"server">>,<<"Cowboy">>},
{<<"upgrade">>,<<"websocket">>}]
Server received: It's raining!
ok
(my_gun@127.0.0.1)2>
来自评论讨论issues/suggestions的摘要
对于 运行 你的 Erlang 应用程序(在本例中包括 gun 作为依赖项),你需要有 shell 可发现的 beam 和 .app 文件的路径.使用 erlang.mk 时,最简单的方法是 运行 使用
make run
发布
在 Cowboy 方面,要正确处理 Websocket,您需要在 cowboy 路由中定义一个处理程序。请参阅 Cowboy Websocket example
中的示例
有关在 Gun 中处理 Websocket 的更多详细信息,另请参阅 Gun user guide