Erlang cowboy websocket 服务器
Erlang cowboy websocket 服务器
原文见于:
http://marcelog.github.io/articles/erlang_websocket_server_cowboy_tutorial.html
本文不是原文的简单翻译,是参考原文,根据我的理解和实践写出来的。本文的源码见于:
https://github.com/marcelog/erws
1 引言
Erlang可以用来实现一个websocket服务器。cowboy这样框架可以完成这个任务,使我们不必关注websocket协议的细节。在吃正餐之前,我们先来点围碟:
WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。 WebSocket通信协议于2011年被IETF定为标准 RFC 6455,WebSocketAPI被W3C定为标准。在WebSocket API中,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。 现在,很多网站为了实现即时通讯(real-time),所用的技术都是轮询(polling)。轮询是在特定的的时间间隔(time interval)(如每1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给客服端的浏览器。这种传统的HTTP request d的模式带来很明显的缺点 – 浏览器需要不断的向服务器发出请求(request),然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽。 而最比较新的技术去做轮询的效果是长轮询(long polling),这是很古老的技术。长轮询的优势在于,它完全颠覆了典型的客户端发送请求的做法。长轮询的核心在于客户端和服务器间的相互联系,打破了客户端尽快发送请求的传统惯例。客户端期望服务器能够长时间留住请求,保持潜在的TCP连接的开放性。只要服务器拥有希望同客户端共享的信息,它就会将数据传送给客户端并终止连接。这种做法的效率相对较高,免除了所有HTTP请求的麻烦。客户端收到更新数据后马上向服务器发送下个请求,依然期望服务器留住该请求,直到有需要传回的数据为止。 基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性。长连接、长轮询一般应用与WebIM、ChatRoom和一些需要及时交互的网站应用中。其真实案例有:WebQQ、Hi网页版、Facebook IM等。 而在 WebSocket API,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket 协议中,为我们实现即使服务带来了两大好处: Header,互相沟通的Header是很小的-大概只有 2 Bytes Server Push,服务器可以主动传送数据给客户端。 因此WebSocket是一种更新更优秀的技术。
2 为什么用cowboy
cowboy是终极悍将。到目前为止(2012-05),可用来写websocket服务的erlang工具有:
Socket.io-Erlang
这是最有吸引力的方法,因为可以用在客户端。Socket.io非常受欢迎,特别在NodeJS里,因为其中一个作者(Frederic Trottier-Hebert)也是LearnYouSomeErlang的作者。可是自从socket.io 0.6.x之后,作者就不再添加新功能。所以对这个产品非常不利。
MochiWeb
非常流行的HTTP框架,但是本身不支持websocket。
Yaws
这个是全功能的 HTTP server,足够健壮性、可扩展性 。适用于静态和动态页面,也支持websockets。但是对我的需求来说,有点杀鸡用牛刀。当然,如果没有其他方案好用,我肯定会选择这个。(译者注:Yaws也是我下一个要学习使用的产品)
Misultin
原作者表示不再更新,已经转向mochiweb和cowboy。
Cowboy
非常有意思的东西。HTTP是这个产品框架的一部分。这个东西更像一个连接池,我们可以设置处理TCP或SSL连接。最大的缺点是缺乏文档,出了问题有时候需要看源代码。
3 开始编程
原文使用了rebar来构建项目。Rebar是一款Erlang的构建工具,使用它可以方便的编译、测试erlang程序、内联驱动和打包Erlang发行版本。
【译者:我继续使用上一篇文章的make工具(Erlang cowboy入门参考),因此,本文与原文略有出入】
这次,我们创建一个erws(Erlang WebSocket),主要的模块就是erws_handler.erl,所有必要的文件都会在下面展示。
当程序运行,cowboy会监听8080端口,分发HTTP请求到erws_handler。
erws_handler实现了2个处理器:cowboy_http_handler和cowboy_websocket_handler。第一个处理器接受普通的HTTP GET请求,并启动websocket完成握手,这个过程通过发送必要的头信息使HTTP连接升级(upgrade),如协议所述。对我们来讲,这个过程非常简单,我们只需要对erws_handler:init/1(参考代码)返回一个元组。所有脏活都是由cowboy的模块cowboy_http_websocket来完成。
一旦这个过程完成,通过调用cowboy_http_websocket_handler暴露出来的api,我们就可以把收到的请求当作消息处理,和普通的gen_server非常类似。
创建项目
$ mkdir erws && cd erws $ wget --no-check-certificate https://raw.githubusercontent.com/ninenines/erlang.mk/master/erlang.mk $ make -f erlang.mk bootstrap bootstrap-rel $ make
erws_app.src
注意:一定要加入"crypto"模块,否则websocket连接无法建立。crypto在websocket连接握手时被cowboy用到。
erws_app.src 资源文件用来生成程序的描述,全文如下:
{application, erws, [
{description, "Erlang WebSocket Demo By cheungmine"},
{vsn, "1"},
{modules, []},
{registered, []},
{applications, [
kernel,
stdlib,
crypto,
cowboy,
compiler,
syntax_tools
]},
{mod, {erws_app, []}},
{env, []}
]}.
Makefile
内容如下:
PROJECT = erws DEPS = cowboy include erlang.mk
erws_app.erl
这个代码设置cowboy监听并接受连接。然后分发请求到指定模块。代码全文如下:
-module(erws_app).
-behaviour(application).
-export([start/2]).
-export([stop/1]).
start(_Type, _Args) ->
Dispatch = cowboy_router:compile([
{'_', [
{"/", cowboy_static, {priv_file, erws, "index.html"}},
{"/websocket", erws_handler, []}
]}
]),
{ok, _} = cowboy:start_http(http, 100, [{port, 8080}],
[{env, [{dispatch, Dispatch}]}]),
erws_sup:start_link().
stop(_State) ->
ok.
erws_sup.erl
督程代码。注意最后一行:
-module(erws_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
Procs = [],
{ok, {{one_for_one, 5, 10}, Procs}}.
erws_handler.erl
生成主模块代码:
$ make new t=cowboy_http n=erws_handler
全文如下:
-module(erws_handler).
-behaviour(cowboy_http_handler).
-behaviour(cowboy_websocket_handler).
-export([init/3, handle/2, terminate/3]).
-export([
websocket_init/3, websocket_handle/3,
websocket_info/3, websocket_terminate/3
]).
init({tcp, http}, _Req, _Opts) ->
{upgrade, protocol, cowboy_websocket}.
handle(_, State) ->
{ok, Req2} = cowboy_http_req:reply(404, [{'Content-Type', <<"text/html">>}]),
{ok, Req2, State}.
websocket_init(_TransportName, Req, _Opts) ->
{ok, Req, undefined_state}.
websocket_handle({text, Msg}, Req, State) ->
{reply, {text, << "responding to ", Msg/binary >>}, Req, State, hibernate };
websocket_handle(_Any, Req, State) ->
{reply, {text, << "whut?">>}, Req, State, hibernate }.
websocket_info({timeout, _Ref, Msg}, Req, State) ->
{reply, {text, Msg}, Req, State};
websocket_info(_Info, Req, State) ->
lager:debug("websocket info"),
{ok, Req, State, hibernate}.
websocket_terminate(_Reason, _Req, _State) ->
ok.
terminate(_Reason, _Req, _State) ->
ok.
index.html
这是WebSocket的客户端。使用支持WebSocket的浏览器访问链接:http://your-webserver-ip:8080/
如何开发这样的WebSocket页面,参考:http://dev.w3.org/html5/websockets/
erws/priv/index.html的全部内容:
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
var ws = new Object;
function send() {
ws.send("hi, cowboy websocket by cheungmine!");
console.log('sent');
}
function open() {
if (!("WebSocket" in window)) {
alert("WebSocket NOT supported by your Browser!");
return;
}
console.log('open');
ws = new WebSocket("ws://"+window.location.host+"/websocket");
ws.onopen = function() {
console.log('connected');
};
ws.onmessage = function (evt) {
var received_msg = evt.data;
console.log("Received: " + received_msg);
var txt = document.createTextNode("Got from server: " + received_msg);
document.getElementById('messages').appendChild(txt);
};
ws.onclose = function() {
// websocket is closed.
console.log('close');
};
}
</script>
</head>
<body>
<div id="sse">
<a href="javascript:open()">Press Me to Open WebSocket</a><br/>
<a href="javascript:send()">Press Me to Send hi Message</a>
</div>
<div id="messages">
</div>
</body>
</html>
4 结论
以上就是全部代码,你可以获取全部源代码:
https://github.com/marcelog/erws
译者注:本文没有采用rebar,你可以参考:http://blog.csdn.net/ubuntu64fan/article/details/40542549
编译和运行:
$ make $ ./_rel/erws_release/bin/erws_release console
显示如下:
(erws@127.0.0.1)1>
输入q().可以退出服务。
打开FireFox浏览器,地址栏输入:http://192.168.82.181:8080/,显示如下:
此时按Send hi Message,页面没有任何反应。先点Open WebSocket,再点Send hi,结果如下:
完全正确
尽管我也不怎么喜欢代码中依赖别人提供的框架,但是cowboy确实干的不错,我认为cowboy是构造复杂应用不错的选择。不仅仅是支持websocket,同时还有HTTP和HTTPS。
最重要的是,cowboy让你关注业务而不是技术实现细节,比如构建连接池,websocket协议等。仅仅几行代码,我们就可以给沉闷乏味的web程序带来魔力。
Erlang cowboy websocket 服务器的更多相关文章
- Cowboy http服务器 websocket
一.基础介绍 cowboy是一个小巧.快速.模块化的http服务器,采用Erlang开发.其中良好的clean module使得我们可以扩展到多种网络协议之中,cowboy自带的有tcp和ssl,而也 ...
- Erlang cowboy 入门参考之现代Web的发展历史
Erlang cowboy 入门参考之现代Web发展史 原文: http://ninenines.eu/docs/en/cowboy/1.0/guide/modern_web/ 让我回顾一下web技术 ...
- Erlang cowboy http request生命周期
Erlang cowboy http request生命周期 翻译自: http://ninenines.eu/docs/en/cowboy/1.0/guide/http_req_life/ requ ...
- Erlang cowboy 入门参考
Erlang cowboy 入门参考 cheungmine,2014-10-28 本文翻译自: http://ninenines.eu/docs/en/cowboy/HEAD/guide/gettin ...
- HTML5学习总结-08 WebSocket 服务器推送
一 WebSocket 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展 ...
- python实现websocket服务器,可以在web实时显示远程服务器日志
一.开始的话 使用python简单的实现websocket服务器,可以在浏览器上实时显示远程服务器的日志信息. 之前做了一个web版的发布系统,但没实现在线看日志,每次发布版本后,都需要登录到服务器上 ...
- Erlang cowboy 处理不规范的client
Erlang cowboy 处理不规范的client Cowboy 1.0 參考 本章: Dealing with broken clients 存在很多HTTP协议的实现版本号. 很多广泛使用的cl ...
- 根据Unix哲学来编写你的HTML5 Websocket服务器来实现全双工通信
websocketd代表WebSocket的守护进程 websocketd处理的是浏览器和服务器之间的WebSocket连接,它会启动你所指定的服务器端应用来对WebSockets进行处理,然后在浏览 ...
- 实现一个websocket服务器-理论篇
本文是Writing WebSocket servers的中文文档,翻译自MDNWriting WebSocket servers.篇幅略长,个人能力有限难免有所错误,抛砖引玉共同进步. websoc ...
随机推荐
- Java异常处理-----抛出处理
抛出处理 定义一个功能,进行除法运算例如(div(int x,int y))如果除数为0,进行处理. 功能内部不想处理,或者处理不了.就抛出使用throw new Exception("除数 ...
- 如何使用Matlab产生对称矩阵
有时候做实验需要使用对称矩阵,这里介绍如何使用Matlab产生随机的对称矩阵. 用例子说明一下:我要产生4X4的随机矩阵,要求是对称矩阵. 产生对称矩阵 A = rand(4); B = tril(A ...
- C语言如何在两个文件中访问同一个全局变量
方法一: 不使用头文件. 1.c 中 int var; 2.c 中 extern int var; 方法二: 使用头文件. 1.c 中 int var; 不必添加#include "1.h& ...
- ActiveMQ入门示例
1.ActiveMQ下载地址 http://activemq.apache.org/download.html 2.ActiveMQ安装,下载解压之后如下目录
- Hadoop:hadoop fs、hadoop dfs与hdfs dfs命令的区别
http://blog.csdn.net/pipisorry/article/details/51340838 'Hadoop DFS'和'Hadoop FS'的区别 While exploring ...
- (一一三)使用系统自带框架操作SQLite3数据库
系统自带的框架是基于C语言的,使用比较繁琐. 下面是使用步骤: 首先导入libsqlite3.0.dylib. ①在Document目录下打开数据库,如果没有则创建. NSString *sqlite ...
- Cocos2D iOS之旅:如何写一个敲地鼠游戏(六):放置地鼠
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...
- Java项目源码为什么要做代码混淆(解释的很好)
代码混淆,是将计算机程序的代码转换成一种功能上等价,但是难于阅读和理解的形式的行为.代码混淆可以用于程序源代码,也可以用于程序编译而成的中间代码.执行代码混淆的程序被称作代码混淆器.目前已经存在许多种 ...
- JSP实现界面的自动跳转的几种方式
下面来谈一谈在jsp中实现的几种界面自动跳转的方法. 使用JavaScript脚本 <html> <script language=javascript> function o ...
- pig的一些实例(我常用的语法)
在pig中, dump和store会分别完成两个MR,不会一起进行 1:加载名用正则表达式: LOAD'/user/wizad/data/wizad/raw/2014-0{6,7-0,7-1,7-2, ...