Browser ! #{ cmd => fill_div, id => clock, txt => <<"16:30:52">>}
注:包含一个结构的Erlang消息是如何被转换成等价的JavaScript函数调用,然后在浏览器里执行的。扩展这个系统极其简单。你要做的就是编写一个JavaScript小函数来对应你需要处理的Erlang消息。
要完成这张图,需要添加启动和停止时钟的代码。把所有东西都放到一起后,HTML代码看起来就像这样:
(1) 给网页里所有属于 live_button 类的按钮添加点击处理函数。这些点击处理函数会在按钮被点击时向Erlang 发送消息。(2) 尝试启动一个到http://localhost:2233的 WebSocket 连接。在服务器端会有一个新分裂出的进程调用 clock1:start(Browser) 函数。所有这些都是通过调用 JavaScript 函 数connect("localhost", 2233, "clock1")实现的。 2233 这个数字没有什么特别的含义,任何大于1023 的未使用端口号都可以用。
现在是Erlang代码:
%% websockets/clock1.erl
-module(clockl).
-export([start/1, current_time/0]).
start(Browser) ->
Browser! #{cmd => fill_div, id => clock, txt => current_time()},
running(Browser).
running(Browser) ->
receive
{Browser, #{clicked => <<"stop">>}} ->
idle(Browser)
after 1000 ->
Browser! #{cmd => fill_div, id => clock, txt => current_time()},
running(Browser)
end.
idle(Browser) ->
receive
{Browser, #{clicked => <<"start">>}} ->
running(Browser)
end.
current_time() ->
{Hour, Min, Sec} = time(),
list_to_binary(io lib:format("~2.2.0w:~2.2.0w:~2.2.0w",
[Hour, Min, Sec])).
Browser ! #{cmd => fill_div, id => clock, txt => current_time()}
接下来的示例有一个显示数据的可滚动文本区域和一个输入框。当你在输入框里输入文本并按回车时就会发送一个消息给浏览器。浏览器以一个更新显示内容的消息作为响应。
它的HTML代码如下:
Interaction
%% websockets/interact1.erl
-module(interactl).
-export([start/1])
start(Browser) -> running(Browser).
running(Browser) ->
receive
{Browser, #{entry => <<"input">>, txt => Bin} }
Time = clockl:current_time(),
Browser ! #{cmd => append_div, id => scroll,
txt => list_to_binary([Time, ">", Bin, "
"])}
end,
running(Browser).
我不会展示全部代码,因为它和交互示例的代码差不多。下面是一些相关部分的代码:
%% websockets/shell1.erl
start(Browser)->
Browser! #{cmd => append_div, id => scroll,
txt => <<"Starting Erlang shell:
">>},
B0 = erl_eval:new_bindings(),
running(Browser, B0, 1).
running(Browser, B0, N)->
receive
{Browser, #{entry => <<"input">>}, txt => Bin}} ->
{Value, B1} = string2value(binary_to_list(Bin), B0),
BV = bf("~w> ~s
~p
",
[N, Bin, Value]),
Browser ! #cmd => append_div, id => scroll, txt => BV),
running(Browser, B1, N+1)
end.
%% websockets/shell1.erl
string2value(Str, Bindings0) ->
case erl_scan:string(Str, 0) of
{ok, Tokens, _} ->
case erl_parse:parse_exprs(Tokens) of
{ok, Exprs} ->
{value, Val, Bindings1} = erl_eval:exprs(Exprs, Bindings0),
{Val,Bindings1};
Other ->
io:format("cannot parse:~p Reason=~p~n",[Tokens, other]),
{parse_error, Bindings0}
end;
Other ->
io:format("cannot tokenise:~p Reason=~p~n",[str, Other])
end.
Chat
$("#join").click(function()
var val = s("#nick_input").val();
send_json({'join'val});
$("#nick_input").val("");
})
%% websockets/chat1.erl
-module(chat1).
-export([start/1]).
start(Browser) ->
running(Browser, []).
running(Browser, L) ->
receive
{Browser, #{join => Who}} ->
Browser ! #{cmd => append_div , id => scroll,
txt => list_to_binary([who, "joined the group\n"])},
L1 - [Who, "
"| L],
Browser ! #{cmd => fill_div,id => users,
txt => list_to_binary(L1)},
running(Browser, L1);
{Browser, #{entry => <<"tell">>, txt => Txt}} ->
Browser ! #{cmd => append_div, id => scroll,
txt => list_to_binary([">", Txt, "
"])},
running(Browser, L);
X ->
io:format("chat received:~p~n", [X])
end,
running(Browser, L).
Chat
%% websockets/chat2.erl
-module(chat2).
-export([start/1]).
start(Browser) ->
idle(Browser).
idle(Browser) ->
receive
{Browser, #{join =Who}} ->
irc ! {join, self(), Who},
idle(Browser);
{irc, welcome, Who} ->
Browser ! #{cmd => hide_div, id => idle},
Browser ! #{cmd => show_div, id => running},
running(Browser, Who);
X ->
io:format("chat idle received:~p~n", [X]),
idle(Browser)
end.
running(Browser, Who) ->
receive
{Browser, #{entry => <<"tell">>, txt => Txt}} ->
irc ! {broadcast, Who, Txt},
running(Browser, Who);
{Browser, #{clicked => <<"Leave">>}} ->
irc ! {leave, who},
Browser ! #{cmd => hide_div, id =>running},
Browser ! #{cmd => show_div, id =>idle},
idle(Browser);
{irc, scroll, Bin} ->
Browser ! #(cmd => append_div, id => scroll, txt => Bin},
running(Browser, who);
{irc, groups, Bin} ->
Browser ! #{cmd => fill_div, id => users, txt => Bin},
running(Browser, Who);
X ->
io:format("chat running received:~p~n", [X]),
running(Browser, Who)
end.
%% websockets/irc.erl
-module(irc).
-export([start/0]).
start() ->
register(irc, spawn(fun() -> startl() end)).
startl()->
process_flag(trap_exit, true),
loop([]).
loop(L) ->
receive
{join, Pid, Who} ->
case lists:keysearch(Who, 1, L) of
false ->
L1 = L ++ [{Who,Pid}],
Pid ! {irc, welcome, Who},
Msg = [Who, <<"joined the chat
">>],
broadcast(L1, scroll, list_to_binary(Msg)),
broadcast(L1, groups, list_users(L1)),
loop(L1);
{value,_} ->
Pid = [irc, error, <<"Name taken">>},
loop(L)
end;
{leave, Who} ->
case lists:keysearch(Who, 1, L) of
false ->
loop(L);
{value, {Who, Pid}} ->
L1 = L -- [{who, Pid}],
Msg = [Who, <<"left the chat
">>],
broadcast(L1, scroll, list_to_binary(Msg)),
broadcast(L1, groups, list_users(L1)),
loop(L1)
end;
{broadcast, Who, Txt} ->
broadcast(L, scroll, list_to_binary([">", Who, ">", Txt, "
"])),
loop(L);
X ->
io:format("irc:received:~p~n", [X]),
loop(L)
end.
broadcast(L, Tag, B) ->
[Pid ! {irc, Tag, B} || {_, Pid} <- L].
list_users(L) ->
L1 = [[Who, ""] || {Who, _} <- L],
list_to_binary(L1).