【小白学爬虫】SSE(Server-Sent Events)

1. 

Server-Sent Events 服务器发送事件,一种新API,部署在EventSource对象上。目前,除了IE,其他主流浏览器都支持。

传统的网页:浏览器向服务器“查询”数据

很多场合,最有效的方式是服务器向浏览器“发送”数据。

比如,每当收到新的电子邮件,服务器就向浏览器发送一个“通知”,这要比浏览器按时向服务器查询(polling)更有效率。

2. 

浏览器向服务器发送一个HTTP请求,然后服务器不断单向地向浏览器推送“信息”(message)。这种信息在格式上很简单,就是“信息”加上前缀“data: ”,然后以“\n\n”结尾。

$ curl http://example.com/dates

data: 1394572346452

data: 1394572347457

data: 1394572348463

^C

3.  SSE与WebSocket

功能相似,均用于建立浏览器与服务器之间的通信渠道。

区别:

a.  WebSocket是全双工通道,可以双向通信,功能更强;

    SSE是单向通道,只能服务器向浏览器端发送。

b.    WebSocket是一个新的协议,需要服务器端支持;

    SSE部署在HTTP协议之上的,现有的服务器软件都支持。

c.  SSE是一个轻量级协议,相对简单;

    WebSocket是一种较重的协议,相对复杂。

d.  SSE默认支持断线重连,WebSocket则需要额外部署。

e.   SSE支持自定义发送的数据类型。


4. 客户端代码部署

a. 首先,检测浏览器是否支持SSE。

if(!!window.EventSource){// ...}

b. 然后,部署SSE大概如下。

varsource=newEventSource('/dates');source.onmessage=function(e){console.log(e.data);};// 或者source.addEventListener('message',function(e){})

c. 建立连接

首先,浏览器向服务器发起连接,生成一个EventSource的实例对象。

varsource=newEventSource(url);

参数url就是服务器网址,必须与当前网页的网址在同一个网域(domain),而且协议和端口都必须相同。

下面是一个建立连接的实例。

if(!!window.EventSource){varsource=newEventSource('http://127.0.0.1/sses/');}

新生成的EventSource实例对象,有一个readyState属性,表明连接所处的状态。

source.readyState

它可以取以下值:

0,相当于常量EventSource.CONNECTING,表示连接还未建立,或者连接断线。

1,相当于常量EventSource.OPEN,表示连接已经建立,可以接受数据。

2,相当于常量EventSource.CLOSED,表示连接已断,且不会重连。

open事件

连接一旦建立,就会触发open事件,可以定义相应的回调函数。

source.onopen=function(event){// handle open event};// 或者source.addEventListener("open",function(event){// handle open event},false);

message事件

收到数据就会触发message事件。

source.onmessage=function(event){vardata=event.data;varorigin=event.origin;varlastEventId=event.lastEventId;// handle message};// 或者source.addEventListener("message",function(event){vardata=event.data;varorigin=event.origin;varlastEventId=event.lastEventId;// handle message},false);

参数对象event有如下属性:

data:服务器端传回的数据(文本格式)。

origin: 服务器端URL的域名部分,即协议、域名和端口。

lastEventId:数据的编号,由服务器端发送。如果没有编号,这个属性为空。

error事件

如果发生通信错误(比如连接中断),就会触发error事件。

source.onerror=function(event){// handle error event};// 或者source.addEventListener("error",function(event){// handle error event},false);

自定义事件

服务器可以与浏览器约定自定义事件。这种情况下,发送回来的数据不会触发message事件。

source.addEventListener("foo",function(event){vardata=event.data;varorigin=event.origin;varlastEventId=event.lastEventId;// handle message},false);

上面代码表示,浏览器对foo事件进行监听。

close方法

close方法用于关闭连接。

source.close();

5. 数据格式

服务器端发送的数据的HTTP头信息如下:

Content-Type: text/event-stream

Cache-Control: no-cache

Connection: keep-alive

后面的行都是如下格式:

field: value\n

field可以取四个值:“data”, “event”, “id”, or “retry”,也就是说有四类头信息。每次HTTP通信可以包含这四类头信息中的一类或多类。\n代表换行符。

以冒号开头的行,表示注释。通常,服务器每隔一段时间就会向浏览器发送一个注释,保持连接不中断。

: This is a comment

下面是一些例子。

: this is a test stream\n\n

data: some text\n\n

data: another message\n

data: with two lines \n\n

data:数据栏

数据内容用data表示,可以占用一行或多行。如果数据只有一行,则像下面这样,以“\n\n”结尾。

data:  message\n\n

如果数据有多行,则最后一行用“\n\n”结尾,前面行都用“\n”结尾。

data: begin message\n

data: continue message\n\n

总之,最后一行的data,结尾要用两个换行符号,表示数据结束。

以发送JSON格式的数据为例。

data: {\n

data: "foo": "bar",\n

data: "baz", 555\n

data: }\n\n

id:数据标识符

数据标识符用id表示,相当于每一条数据的编号。

id: msg1\n

data: message\n\n

浏览器用lastEventId属性读取这个值。一旦连接断线,浏览器会发送一个HTTP头,里面包含一个特殊的“Last-Event-ID”头信息,将这个值发送回来,用来帮助服务器端重建连接。因此,这个头信息可以被视为一种同步机制。

event栏:自定义信息类型

event头信息表示自定义的数据类型,或者说数据的名字。

event: foo\n

data: a foo event\n\n

data: an unnamed event\n\n

event: bar\n

data: a bar event\n\n

上面的代码创造了三条信息。第一条是foo,触发浏览器端的foo事件;第二条未取名,表示默认类型,触发浏览器端的message事件;第三条是bar,触发浏览器端的bar事件。

retry:最大间隔时间

浏览器默认的是,如果服务器端三秒内没有发送任何信息,则开始重连。服务器端可以用retry头信息,指定通信的最大间隔时间。

retry: 10000\n

服务器代码

服务器端发送事件,要求服务器与浏览器保持连接。对于不同的服务器软件来说,所消耗的资源是不一样的。Apache服务器,每个连接就是一个线程,如果要维持大量连接,势必要消耗大量资源。Node.js则是所有连接都使用同一个线程,因此消耗的资源会小得多,但是这要求每个连接不能包含很耗时的操作,比如磁盘的IO读写。

下面是Node.js的服务器发送事件的代码实例

varhttp=require("http");http.createServer(function(req,res){varfileName="."+req.url;if(fileName==="./stream"){res.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","Connection":"keep-alive"});res.write("retry: 10000\n");res.write("event: connecttime\n");res.write("data: "+(newDate())+"\n\n");res.write("data: "+(newDate())+"\n\n");interval=setInterval(function(){res.write("data: "+(newDate())+"\n\n");},1000);req.connection.addListener("close",function(){clearInterval(interval);},false);}}).listen(80,"127.0.0.1");

PHP代码实例。


参考链接

Colin Ihrig,Implementing Push Technology Using Server-Sent Events

Colin Ihrig,The Server Side of Server-Sent Events

Eric Bidelman,Stream Updates with Server-Sent Events

MDN,Using server-sent events

Segment.io,Server-Sent Events: The simplest realtime browser spec

http://javascript.ruanyifeng.com/htmlapi/eventsource.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,647评论 18 139
  • https://nodejs.org/api/documentation.html 工具模块 Assert 测试 ...
    KeKeMars阅读 6,320评论 0 6
  • 我发现,我在情绪产生较大波动时,晚上会做一连串的梦,好多次了,都是这样。我的心情并不是每天都那么好,那么平静。比如...
    马少军阅读 368评论 0 5
  • 题外话,文中加粗字体为我自己重点理解感受,不违反原作者要表达的意思,少量删改,原文转自UI中国。 作为设计师,你不...
    林窗鲸落阅读 628评论 0 2
  • 在我的记忆仓库里,储藏着许许多多的名言,它们像一盏盏指路灯,或多或少地给我以帮助。其中最令我难忘的三句话不是名人名...
    梦随丹阳阅读 478评论 0 0