
  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.
