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