一、基础介绍

cowboy是一个小巧、快速、模块化的http服务器,采用Erlang开发。其中良好的clean module使得我们可以扩展到多种网络协议之中,cowboy自带的有tcp和ssl,而也有一些人提供了smtp等的扩展。

cowboy的特点:

1.代码少。

2.速度快。

3.模块化程度高,transport和protocol都可轻易替换。

4.采用二进制语法实现http服务,更快更小。

5.极易嵌入其它应用。

6.有dispatcher,可以嵌入FastCGI PHP 或者是 Ruby.

7.没有进程字典,代码干净。

https://github.com/marcelog/erws

1 引言

Erlang可以用来实现一个websocket服务器。cowboy这样框架可以完成这个任务,使我们不必关注websocket协议的细节。在吃正餐之前,我们先来点围碟:

  1. WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。 WebSocket通信协议于2011年被IETF定为标准 RFC 6455,WebSocketAPI被W3C定为标准。在WebSocket API中,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
  2. 现在,很多网站为了实现即时通讯(real-time),所用的技术都是轮询(polling)。轮询是在特定的的时间间隔(time interval)(如每1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给客服端的浏览器。这种传统的HTTP request d的模式带来很明显的缺点 – 浏览器需要不断的向服务器发出请求(request),然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽。
  3. 而最比较新的技术去做轮询的效果是长轮询(long polling),这是很古老的技术。长轮询的优势在于,它完全颠覆了典型的客户端发送请求的做法。长轮询的核心在于客户端和服务器间的相互联系,打破了客户端尽快发送请求的传统惯例。客户端期望服务器能够长时间留住请求,保持潜在的TCP连接的开放性。只要服务器拥有希望同客户端共享的信息,它就会将数据传送给客户端并终止连接。这种做法的效率相对较高,免除了所有HTTP请求的麻烦。客户端收到更新数据后马上向服务器发送下个请求,依然期望服务器留住该请求,直到有需要传回的数据为止。
  4. 基于HTTP的长连接,是一种通过长轮询方式实现"服务器推"的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性。长连接、长轮询一般应用与WebIM、ChatRoom和一些需要及时交互的网站应用中。其真实案例有:WebQQ、Hi网页版、Facebook IM等。
  5. 而在 WebSocket API,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket 协议中,为我们实现即使服务带来了两大好处:
  6. Header,互相沟通的Header是很小的-大概只有 2 Bytes
  7. Server Push,服务器可以主动传送数据给客户端。
  8. 因此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非常类似。

二、自己整理开源代码

三、创建项目

  1. $ mkdir erws && cd erws
  2. $ wget --no-check-certificate https://raw.githubusercontent.com/ninenines/erlang.mk/master/erlang.mk
  3. $ make -f erlang.mk bootstrap bootstrap-rel
  4. $ make

erws_app.src

注意:一定要加入"crypto"模块,否则websocket连接无法建立。crypto在websocket连接握手时被cowboy用到。

erws_app.src 资源文件用来生成程序的描述,全文如下:

  1. {application, erws, [
  2. {description, "Erlang WebSocket Demo By cheungmine"},
  3. {vsn, "1"},
  4. {modules, []},
  5. {registered, []},
  6. {applications, [
  7. kernel,
  8. stdlib,
  9. crypto,
  10. cowboy,
  11. compiler,
  12. syntax_tools
  13. ]},
  14. {mod, {erws_app, []}},
  15. {env, []}
  16. ]}.

Makefile

内容如下:

  1. PROJECT = erws
  2. DEPS = cowboy
  3. include erlang.mk

erws_app.erl

这个代码设置cowboy监听并接受连接。然后分发请求到指定模块。代码全文如下:

  1. -module(erws_app).
  2. -behaviour(application).
  3. -export([start/2]).
  4. -export([stop/1]).
  5. start(_Type, _Args) ->
  6. Dispatch = cowboy_router:compile([
  7. {'_', [
  8. {"/", cowboy_static, {priv_file, erws, "index.html"}},
  9. {"/websocket", erws_handler, []}
  10. ]}
  11. ]),
  12. {ok, _} = cowboy:start_http(http, 100, [{port, 8080}],
  13. [{env, [{dispatch, Dispatch}]}]),
  14. erws_sup:start_link().
  15. stop(_State) ->
  16. ok.

erws_sup.erl

督程代码。注意最后一行:

  1. -module(erws_sup).
  2. -behaviour(supervisor).
  3. -export([start_link/0]).
  4. -export([init/1]).
  5. start_link() ->
  6. supervisor:start_link({local, ?MODULE}, ?MODULE, []).
  7. init([]) ->
  8. Procs = [],
  9. {ok, {{one_for_one, 5, 10}, Procs}}.

erws_handler.erl

生成主模块代码:

  1. $ make new t=cowboy_http n=erws_handler

全文如下:

  1. -module(erws_handler).
  2. -behaviour(cowboy_http_handler).
  3. -behaviour(cowboy_websocket_handler).
  4. -export([init/3, handle/2, terminate/3]).
  5. -export([
  6. websocket_init/3, websocket_handle/3,
  7. websocket_info/3, websocket_terminate/3
  8. ]).
  9. init({tcp, http}, _Req, _Opts) ->
  10. {upgrade, protocol, cowboy_websocket}.
  11. handle(_, State) ->
  12. {ok, Req2} = cowboy_http_req:reply(404, [{'Content-Type', <<"text/html">>}]),
  13. {ok, Req2, State}.
  14. websocket_init(_TransportName, Req, _Opts) ->
  15. {ok, Req, undefined_state}.
  16. websocket_handle({text, Msg}, Req, State) ->
  17. {reply, {text, << "responding to ", Msg/binary >>}, Req, State, hibernate };
  18. websocket_handle(_Any, Req, State) ->
  19. {reply, {text, << "whut?">>}, Req, State, hibernate }.
  20. websocket_info({timeout, _Ref, Msg}, Req, State) ->
  21. {reply, {text, Msg}, Req, State};
  22. websocket_info(_Info, Req, State) ->
  23. lager:debug("websocket info"),
  24. {ok, Req, State, hibernate}.
  25. websocket_terminate(_Reason, _Req, _State) ->
  26. ok.
  27. terminate(_Reason, _Req, _State) ->
  28. ok.

index.html

这是WebSocket的客户端。使用支持WebSocket的浏览器访问链接:http://your-webserver-ip:8080/

如何开发这样的WebSocket页面,参考:http://dev.w3.org/html5/websockets/

erws/priv/index.html的全部内容:

  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <script type="text/javascript">
  5. var ws = new Object;
  6. function send() {
  7. ws.send("hi, cowboy websocket by cheungmine!");
  8. console.log('sent');
  9. }
  10. function open() {
  11. if (!("WebSocket" in window)) {
  12. alert("WebSocket NOT supported by your Browser!");
  13. return;
  14. }
  15. console.log('open');
  16. ws = new WebSocket("ws://"+window.location.host+"/websocket");
  17. ws.onopen = function() {
  18. console.log('connected');
  19. };
  20. ws.onmessage = function (evt) {
  21. var received_msg = evt.data;
  22. console.log("Received: " + received_msg);
  23. var txt = document.createTextNode("Got from server: " + received_msg);
  24. document.getElementById('messages').appendChild(txt);
  25. };
  26. ws.onclose = function() {
  27. // websocket is closed.
  28. console.log('close');
  29. };
  30. }
  31. </script>
  32. </head>
  33. <body>
  34. <div id="sse">
  35. <a href="javascript:open()">Press Me to Open WebSocket</a><br/>
  36. <a href="javascript:send()">Press Me to Send hi Message</a>
  37. </div>
  38. <div id="messages">
  39. </div>
  40. </body>
  41. </html>

4 结论

以上就是全部代码,你可以获取全部源代码:

https://github.com/marcelog/erws

译者注:本文没有采用rebar,你可以参考:http://blog.csdn.net/ubuntu64fan/article/details/40542549

编译和运行:

  1. $ make
  2. $ ./_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程序带来魔力。

本文不是原文的简单翻译,是参考原文,根据我的理解和实践写出来的。参考内容于:http://marcelog.github.io/articles/erlang_websocket_server_cowboy_tutorial.html及github 的cowboy源码

Cowboy http服务器 websocket的更多相关文章

  1. C#实现WebSocket协议客户端和服务器websocket sharp组件实例解析

    看到这篇文章的题目,估计很多人都会问,这个组件是不是有些显的无聊了,说到web通信,很多人都会想到ASP.NET SignalR,或者Nodejs等等,实现web的网络实时通讯.有关于web实时通信的 ...

  2. 【Netty】(7)---搭建websocket服务器

    [Netty](7)---搭建websocket服务器 说明:本篇博客是基于学习某网有关视频教学. 目的:创建一个websocket服务器,获取客户端传来的数据,同时向客户端发送数据 一.服务端 1. ...

  3. Swoole 中使用 WebSocket 异步服务器、WebSocket 协程服务器

    WebSocket 异步风格服务器 WebSocket\Server 继承自 Http\Server,所以 Http\Server 提供的所有 API 和配置项都可以使用. # ws_server.p ...

  4. 一步一步学WebSocket (一) 初识WebSocket

    众所周知,Http协议是无状态的,并且是基于Request/Response的方式与服务器进行交互,也就是我们常说的单工模式.但是随着互联网的发展,浏览器与服务端进行双向通信需求的增加,长轮询向服务器 ...

  5. 初识WebSocket

    众所周知,Http协议是无状态的,并且是基于Request/Response的方式与服务器进行交互,也就是我们常说的单工模式.但是随着互联 网的发展,浏览器与服务端进行双向通信需求的增加,长轮询向服务 ...

  6. C#工作总结(一):Fleck的WebSocket使用

    一.引子(Foreword) 最近公司里面要做窗体和网页交互的功能.网上找了一下资料,这里做一个简单的扩充和整理,部分内容可能是摘自其他博客,这里会注明出处和原文地址供大家和自己日后查阅. 二.基础知 ...

  7. Springboot 使用 webSocket

    介绍 WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议.在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进 ...

  8. Fleck WebSocket使用

    Fleck WebSocket使用 作为笔记存储. 最近公司有这方面的使用需求.在网上查了一些资料后.得到了想要的结果.以下记录摘抄至网上资料. 1.首先,服务端.项目NuGet直接引用Fleck类库 ...

  9. 封装好的socket,拿去用

    年终有空咯,分享一下自己封装的socket类库. 由于公司写的socket代码非常醉人,我不能忍,所以自己封装了一下方便大家使用,现在有空也分享给园友用用看,现在还存在一定的问题,等下我列出来,希望大 ...

随机推荐

  1. C#Razor模板引擎简单使用

    引用 install-package RazorEngine 使用 public class TestDemo { private string name; public int Age { get ...

  2. 【用户权限】MongoDB用户权限

    一.数据库用户角色: read:允许用户读取指定数据库readWrite:允许用户读写指定数据库 二.数据库管理角色:dbAdmin.dbOwner.userAdmin: dbAdmin:允许用户在指 ...

  3. Office DCOM 组件交互权限设置

    这里以 WORD 为例: 运行 mmc -32 打开控制台. 点击 文件 -> 添加/删除管理单元 ,在 可用的管理单元 中选择 组件服务 ,然后点击 添加 , 确定 . 展开 组件服务节 点直 ...

  4. error LNK1169 找到一个或多个多重定义的符号的解决方法

    问题描述如下: 有 三个源文件,A.h.B.cpp.C.cpp. A.h是头文件,其中声明了三个变量a1.a2. a3. B.cpp是A.h中所声明的类的实现源代码,C.cpp是主程序文件.B.cpp ...

  5. npm ERR! File exists: /XXX/xxx npm ERR! Move it away, and try again.

    今天抽空将我的静态服务 ks-server 之前留下的 bug(在node低版本情况下报错)维护了一下. 当我重新 npm link 时,如下错误: npm WARN ks-server@1.0.2 ...

  6. .bat批处理启动redis

    背景: 最近,公司的项目开发,需要用到Redis,然而每天都需要到d盘下面的去启动redis很烦, 我是我就想写一个.bat启动文件放在桌面上,这样每天只要在桌面上点以下redis的bat文件就可以启 ...

  7. day7_子类的拷贝构造与拷贝赋值

  8. Java并发编程的挑战

    并发编程的目的是为了让程序运行得更快,但是,并不是线程启动的越多,就能让程序最大限度地并发执行.并发编程时,会面临非常多的挑战,比如上下文切换的问题,死锁的问题,以及受限于各种硬件和软件的资源限制问题 ...

  9. vue中前端处理token过期的方法与axios请求拦截处理

    在处理token过期的这个问题上困扰了我很久,现在终于解决的了,所以分享出来给大家,希望能够对大家有所帮助. 首先,当然是路由进行拦截,路由拦截当然是在beforeEach中了: router.bef ...

  10. LayaAir引擎开发HTML5最简单教程(面向JS开发者)

    LayaAir引擎开发HTML5最简单教程(面向JS开发者) 一.总结 一句话总结:开发游戏还是得用游戏引擎来开发,其实很简单啦 切记:开发游戏还是得用游戏引擎来开发,其实很简单,引擎很多东西都帮你做 ...