1 -module(local_server).
2 -export([start/0]).
3
4 start() ->
5 F = fun interact/2,
6 spawn(fun() -> start(F, 0) end).
7
8 start(F, State0) ->
9 {ok, Listen} = gen_tcp:listen(8000, [{packet,raw}, {reuseaddr,true}, {active, true}]),
10 par_connect(Listen, F, State0).
11
12 par_connect(Listen, F, State0) ->
13 case gen_tcp:accept(Listen) of
14 {ok, Socket} ->
15 spawn(fun() -> par_connect(Listen, F, State0) end),
16 wait(Socket, F, State0);
17 {error, closed} ->
18 {ok, Listen2} = gen_tcp:listen(8000, [{packet,raw}, {reuseaddr,true}, {active, true}]),
19 par_connect(Listen2, F, State0)
20 end.
21
22 wait(Socket, F, State0) ->
23 receive
24 {tcp, Socket, Data} ->
25 Key = list_to_binary(lists:last(string:tokens(hd(lists:filter(fun(S) -> lists:prefix("Sec-WebSocket-Key:", S) end, string:tokens(Data, "\r\n"))), ": "))),
26 Challenge = base64:encode(crypto:sha(<< Key/binary, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" >>)),
27 Handshake =
28 ["HTTP/1.1 101 Switching Protocols\r\n",
29 "connection: Upgrade\r\n",
30 "upgrade: websocket\r\n",
31 "sec-websocket-accept: ", Challenge, "\r\n",
32 "\r\n",<<>>],
33 gen_tcp:send(Socket, Handshake),
34 send_data(Socket, "Hello, my world"),
35 S = self(),
36 Pid = spawn_link(fun() -> F(S, State0) end),
37 loop(Socket, Pid);
38 _Any ->
39 wait(Socket, F, State0)
40 end.
41
42 loop(Socket, Pid) ->
43 receive
44 {tcp, Socket, Data} ->
45 Text = websocket_data(Data),
46 case Text =/= <<>> of
47 true ->
48 Pid ! {browser, self(), ["You said: ", Text]};
49 false ->
50 ok
51 end,
52 loop(Socket, Pid);
53 {tcp_closed, Socket} ->
54 ok;
55 {send, Data} ->
56 send_data(Socket, Data),
57 loop(Socket, Pid);
58 _Any ->
59 loop(Socket, Pid)
60 end.
61
62 interact(Browser, State) ->
63 receive
64 {browser, Browser, Str} ->
65 Browser ! {send, Str},
66 interact(Browser, State)
67 after 1000 ->
68 Browser ! {send, "clock ! tick " ++ integer_to_list(State)},
69 interact(Browser, State+1)
70 end.
71
72 %% 仅处理长度为125以内的文本消息
73 websocket_data(Data) when is_list(Data) ->
74 websocket_data(list_to_binary(Data));
75 websocket_data(<< 1:1, 0:3, 1:4, 1:1, Len:7, MaskKey:32, Rest/bits >>) when Len < 126 ->
76 <<End:Len/binary, _/bits>> = Rest,
77 Text = websocket_unmask(End, MaskKey, <<>>),
78 Text;
79 websocket_data(_) ->
80 <<>>.
81
82 %% 由于Browser发过来的数据都是mask的,所以需要unmask
83 websocket_unmask(<<>>, _, Unmasked) ->
84 Unmasked;
85 websocket_unmask(<< O:32, Rest/bits >>, MaskKey, Acc) ->
86 T = O bxor MaskKey,
87 websocket_unmask(Rest, MaskKey, << Acc/binary, T:32 >>);
88 websocket_unmask(<< O:24 >>, MaskKey, Acc) ->
89 << MaskKey2:24, _:8 >> = << MaskKey:32 >>,
90 T = O bxor MaskKey2,
91 << Acc/binary, T:24 >>;
92 websocket_unmask(<< O:16 >>, MaskKey, Acc) ->
93 << MaskKey2:16, _:16 >> = << MaskKey:32 >>,
94 T = O bxor MaskKey2,
95 << Acc/binary, T:16 >>;
96 websocket_unmask(<< O:8 >>, MaskKey, Acc) ->
97 << MaskKey2:8, _:24 >> = << MaskKey:32 >>,
98 T = O bxor MaskKey2,
99 << Acc/binary, T:8 >>.
100
101 %% 发送文本给Client
102 send_data(Socket, Payload) ->
103 Len = iolist_size(Payload),
104 BinLen = payload_length_to_binary(Len),
105 gen_tcp:send(Socket, [<< 1:1, 0:3, 1:4, 0:1, BinLen/bits >>, Payload]).
106
107 payload_length_to_binary(N) ->
108 case N of
109 N when N =< 125 -> << N:7 >>;
110 N when N =< 16#ffff -> << 126:7, N:16 >>;
111 N when N =< 16#7fffffffffffffff -> << 127:7, N:64 >>
112 end.