erlang实现websocket简单示例

来源:互联网 发布:数据恢复精灵吧 编辑:程序博客网 时间:2024/05/24 05:04

http://www.cnblogs.com/suex/p/3669953.html

erlang实现websocket简单示例

 

本示例仅支持文本消息

基于websocket版本13
 
由于joe armstrong的例子:
 
http://armstrongonsoftware.blogspot.com/2009/12/comet-is-dead-long-live-websockets.html

已经过时,不符合现在的websocket标准,于是改写了一下

 
 
还参考了cowboy的代码:
 
https://github.com/extend/cowboy
 
websocket标准参考:
http://blog.csdn.net/fenglibing/article/details/6852497
 
 

后端代码:
复制代码
  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 %% 发送文本给Client102 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 of109         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.
复制代码

前端使用js发送websocket请求

复制代码
  1 <html>  2   <head>  3     <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">  4     <title>Websocket client</title>  5     <script src="jquery.min.js"></script>  6     <script type="text/javascript">  7         8       var websocket;  9       $(document).ready(init); 10        11       function init() { 12           if(!("WebSocket" in window)){   13               $('#status').append('<p><span style="color: red;">websockets are not supported </span></p>'); 14               $("#navigation").hide();   15           } else { 16               $('#status').append('<p><span style="color: green;">websockets are supported </span></p>'); 17               connect(); 18           }; 19               $("#connected").hide();      20               $("#content").hide();      21       }; 22  23       function connect() 24       { 25           showScreen('<span style="color: red;">CONNECTING </span>'); 26           wsHost = $("#server").val() 27           websocket = new WebSocket(wsHost); 28           showScreen('<b>Connecting to: ' +  wsHost + '</b>');  29           websocket.onopen = function(evt) { onOpen(evt) };  30           websocket.onclose = function(evt) { onClose(evt) };  31           websocket.onmessage = function(evt) { onMessage(evt) };  32           websocket.onerror = function(evt) { onError(evt) };  33       };   34        35       function disconnect() { 36           websocket.close(); 37       };  38  39       function toggle_connection(){ 40           if(websocket.readyState == websocket.OPEN){ 41               disconnect(); 42           } else { 43               connect(); 44           }; 45       }; 46  47       function sendTxt() { 48           if(websocket.readyState == websocket.OPEN){ 49               txt = $("#send_txt").val(); 50               websocket.send(txt); 51               showScreen('sending: ' + txt);  52           } else { 53                showScreen('websocket is not connected');  54           }; 55       }; 56  57       function onOpen(evt) {  58           showScreen('<span style="color: green;">CONNECTED </span>');  59           $("#connected").fadeIn('slow'); 60           $("#content").fadeIn('slow'); 61       };   62  63       function onClose(evt) {  64           showScreen('<span style="color: red;">DISCONNECTED </span>'); 65       };   66  67       function onMessage(evt) {  68           showScreen('<span style="color: blue;">RESPONSE: ' + evt.data+ '</span>');  69       };   70  71       function onError(evt) { 72           showScreen('<span style="color: red;">ERROR: ' + evt.data+ '</span>'); 73       }; 74  75       function showScreen(txt) {  76           $('#output').prepend('<p>' + txt + '</p>'); 77       }; 78  79       function clearScreen()  80       {  81           $('#output').html(""); 82       }; 83     </script> 84   </head> 85  86   <body> 87     <div id="header"> 88       <h1>Websocket client</h1> 89       <div id="status"></div> 90     </div> 91  92  93     <div id="navigation"> 94  95       <p id="connecting"> 96     <input type='text' id="server" value="ws://localhost:8000/websocket"></input> 97     <button type="button" onclick="toggle_connection()">connection</button> 98       </p> 99       <div id="connected">                100     <p>101       <input type='text' id="send_txt" value=></input>102       <button type="button" onclick="sendTxt();">send</button>103     </p>104       </div>105 106       <div id="content">                        107     <button id="clear" onclick="clearScreen()" >Clear text</button>108     <div id="output"></div>109       </div>110 111     </div>112   </body>113 </html> 
复制代码

 

附件(内含上文代码及jquery和erlang编译后的beam文件)下载地址:

http://files.cnblogs.com/suex/websocket_demo.zip 

在解压后的文件夹中启动erlang shell, 执行local_server:start()即可启动服务端,

此时打开index.html即可看到文首的截图效果

0 0
原创粉丝点击