about websocket 76 in erlang

zhangbo bo.zhang86@REDACTED
Fri Mar 11 07:40:14 CET 2011


Hi,
	I've been writing a simple websocket(draft 76) server, but I caught a odd question. When open the html, the erlang console print "data Received:{tcp_closed,#Port<0.2108>}". But if I use jetty instead of erlang, it's ok. Please help. Thank you.


Here is the code:

Html:
<html> 
    <head> 
        <title>WebSoket Demo</title> 
        <script type="text/javascript"> 
            if (!window.WebSocket) { 
                alert("WebSocket not supported by this browser!"); 
            } 
             
            function display() { 
                var valueLabel = document.getElementById("valueLabel"); 
                valueLabel.innerHTML = ""; 
                var ws = new WebSocket("ws://127.0.0.1:8080");
		ws.onmessage = function(evt) { 
			alert("msg:" + evt.data);
                    valueLabel.innerHTML = evt.data; 
                }; 

                ws.onclose = function() { 
                }; 

                ws.onopen = function() { 
			alert("open");
                    ws.send("Hello, Server!"); 
                }; 
            } 
        </script> 
    </head> 
    <body onload="display();"> 
        <div id="valueLabel"></div> 
    </body> 
</html>


erlang:
-module(websocketd).
-compile(export_all).
-author({jha, abhinav}).


start() ->
    {ok, L} = gen_tcp:listen(8000, [binary, {active, true}, {reuseaddr, true}, {packet, 0}]),
    erlang:process_flag(trap_exit, true),
    Pid = spawn_link(websocketd, acceptor, [L]),
    receive
        {'EXIT', Pid, Why} -> 
            io:format("Process ~p exited with ~p. ~n", [Pid, Why]);
        Any -> 
            io:format("~p~n", [Any])
    end.

% Accepts multiple connections and handles them separately.
acceptor(L)->
    {ok, S} = gen_tcp:accept(L),
    spawn(fun()->acceptor(L) end),
    handle(S).

% Not really required, just a wrapper over handle/2 in case we want to do something later.
handle(S)->
    handle(S, []).

handle(S, _Data)->
    Pid = self(),
    Child = spawn(fun() -> handshake_and_talk(Pid) end),
    loop(S, Child).

loop(S, Child)->
    receive
        {tcp, S, Bin} -> 
            Child ! {self(), Bin}, 
            loop(S, Child);
        {Child, X} ->
            gen_tcp:send(S, X),
            loop(S, Child);
        _Any ->
            loop(S, Child)
    end.
            

% Handshake with the client and begin talking.
handshake_and_talk(Ppid)->
    receive
        {Ppid, X} ->
            % Body = the checksum comprised after processing Key1, Key2 and the Client's request body.
            Body = process_client_handshake(binary_to_list(X)),

            % Send the Handshake stuff to the browser.
            Ppid ! {self(), "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"},
            Ppid ! {self(), "Upgrade: WebSocket\r\n"},
            Ppid ! {self(), "Connection: Upgrade\r\n"},
            Ppid ! {self(), "Sec-WebSocket-Origin: http://127.0.0.1:8080\r\n"},
            Ppid ! {self(), "Sec-WebSocket-Location: ws://127.0.0.1:8000/\r\n"},
            Ppid ! {self(), "Sec-WebSocket-Protocol: chat"},
            Ppid ! {self(), "\r\n\r\n"},

            % Send the body. 
            Ppid ! {self(), Body},

            % Now call the talk method to do the actual talking with the browser.
            talk(Ppid);

        Any -> io:format("[Child] Random stuff received:~p~n. ~p", [Any, Ppid]) 
    end.


% Function to actuall talk to the browser.
% Dummy implementation -  Implement as reuired.
talk(Browser) ->
    receive
    after 1000 ->
            % This is the main communicator function to the Browser.
            % Whatever you write instead of "Hahah" will get sent to the browser.
            % the 0 and 255 is the framing ( required) - don't change that. 
            Browser ! {self(), [0]},
            Browser ! {self(), "Hahah"},
            Browser ! {self(), [255]},
            talk(Browser)
    end.

% Process client's handshake to retrieve information.
process_client_handshake(X)->
    [Body|Head] = lists:reverse(string:tokens(X, "\r\n")),
    {Key1, Key2} = extract_keys(lists:reverse(Head)),
    {Skey1, Skey2} = process_keys(Key1, Key2),
    Bin_body = list_to_binary(Body),
    Key = <<Skey1:32/big-unsigned-integer, Skey2:32/big-unsigned-integer, Bin_body/binary>>,
    erlang:md5(Key).

% Extract keys from the client's handshake.
extract_keys([H|T])->
    Key1 = extract_key("Sec-WebSocket-Key1: ", [H|T]),
    Key2 = extract_key("Sec-WebSocket-Key2: ", [H|T]),
    {Key1, Key2}.

extract_key(X, [H|T])->
    case string:str(H, X) of
        0 -> extract_key(X, T); 
        _Pos -> string:substr(H, string:len(X) + 1)
    end.

% Process the keys as mentioned in the handshake 76 draft of the ietf.
process_keys(Key1, Key2)->
    {Digits1, []} = string:to_integer(digits(Key1)),
    {Digits2, []} = string:to_integer(digits(Key2)),
    Spaces1 = spaces(Key1),
    Spaces2 = spaces(Key2),
    {Digits1 div Spaces1, Digits2 div Spaces2}.

% Concatenate digits 0-9 of a string 
digits(X)-> [A || A<-X, A =< 57, A >= 48].

% Count number of spaces in a string.
spaces(X)-> string:len([ A || A<-X, A =:= 32]).





Bob






More information about the erlang-questions mailing list