一、WebSocket

全双工通信

通信双方都可以发送和接受信息

2. WebSocket协议

一旦建立WebScoket协议的通信连接,通信可以发送JSON、XML、HTML或图片任意格式的数据

3. 功能(优点)

1. 推送功能

服务端可以主动向客户端推送数据啦

2. 减少通信量

建立的WebSocket连接,一直保持连接状态,和HTTP相比减少了每次连接的开销,而且WebSocket的首部信息很小,所以通信量也少了

3. 连接过程


为了实现WebSocket通信,在HTTP连接建立之后,需要完成一次握手的步骤
为了实现WebSocket,需要用到HTTP的 Upgrade的首部字段 ,告诉服务器通信协议发生改变,以达到握手的目的

请求头
请求头

Sec-WebSocket-Key 字段记录握手过程必不可少的键值
Sec-WebSocket-Protocol 字段记录使用的子协议

流程
流程

响应:

响应头
响应头

4. 实现

1. 前台
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<body>
<button id="a">发信息</button>
</body>
<script type="text/javascript">
let ws = new WebSocket('ws://localhost:3333')

// 触发连接
ws.onopen = function() {
console.log('连接')
}
ws.onclose = function() {
console.log('断开连接')
}
ws.onmessage = function(e) {
console.log('收到信息', e.data)
}
document.getElementById("a").onclick = function() { // 监测 id=“sendb”的 按钮 触发 onclick 就会发送数据 send //
ws.send('发信息了');
}
</script>
2. 后台
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const ws = require('nodejs-websocket')

let server = ws.createServer(conn => {
console.log('连接', conn)
conn.on('text', str => {
console.log(str)
})
conn.on('close', (code, reason) => {
console.log('连接断开')
})
conn.on('error', err => {

})
}).listen(3333)
3. 结果
信息头
信息头

点击发信息

结果
结果

一、SSE (server-sent event)

仅仅服务端往客户端推动,基于http协议

1
var source = new EventSource(url);

EventSource实例的readyState属性,表明连接的当前状态。该属性只读,可以取以下值。

0:相当于常量EventSource.CONNECTING,表示连接还未建立,或者断线正在重连。
1:相当于常量EventSource.OPEN,表示连接已经建立,可以接受数据。
2:相当于常量EventSource.CLOSED,表示连接已断,且不会重连。

1. 数据格式(发送端)

⓵ 响应头

响应头必须有

  1. Content-Type: text/event-stream MIME Type 规定为 text/event-stream
  2. Cache-Control: no-cache 不允许缓存(可不写)
  3. Connection: keep-alive 指定持久连接(长连接),默认都是持久连接(可不写)
⓶ data 数据

数据用data表示,用 \n\n结尾
比如: data: 这里是数据 \n\n
多行用 \n 分隔,\n\n 结尾
比如:
data: asdf \n
data: ghjk \n
data: asdf \n\n
这是一条数据

⓷ event 事件类型

前端默认用 message 接收事件,但是也可以用服务端自定的事件名称

event: have \n
data: 定义了一个have事件 \n\n

触发客户端 have 事件

⓸ retry 最大间隔时间

浏览器默认服务器端三秒内没有发送任何信息,则开始重连。服务器端可以用retry头信息,指定这个时间
retry: 10000\n

⓹ id

id: msg1\n
data: message\n\n

我没咋用到过

浏览器会一直跟踪最近的事件ID,如果发生了重连,浏览器会把最近接收到的事件ID放入 HTTP Header “Last-Event-ID” 中,作为一种简单的同步机制。

⓺ 客户端主动关闭
1
source.close();
⓻ 服务端关闭

我用的node做测试的,就是接受一个前端触发的close事件,来关闭

1
2
3
req.connection.addListener("close", function() {
clearInterval(interval);
}, false);

2. demo

⓵ 客户端
1
2
3
4
5
6
7
let source = new EventSource('/url');
source.onmessage = function(e) {
console.log(e, e.data);
};
setTimeout(() => {
source.close()
}, 5000);
⓶ 服务端(node)
1
2
3
4
5
6
7
8
9
10
11
12
13
app.use("/url", function(req, res) {
res.writeHead(200, {
"Content-Type": "text/event-stream",
});
setInterval(() => {
res.write("id: num1\n\n");
res.write("data:" + (new Date()) + "\n\n");
}, 1000);

req.connection.addListener("close", function() {
clearInterval(interval);
}, false);
})

每一秒触发一次,5秒断开连接

⓷ gif
结果
结果

三、comet

解释

基于HTTP长连接的,无需在浏览器安装插件的服务器推送的一种HACK技术

1. 与ajax对比

Ajax要想取得数据,必须先发送请求,在延时要是比较高的web应用中,只能增加服务器请求的频率
comet是客户端与服务器端保持一个长连接,只有客户端需要数据更新时,服务器才主动将数据推送到客户端

comet的实现主要有两种方式

  1. 基于Ajax的长轮询(long-polling)
  2. 基于Iframe及htmlfile的流(http streaming)方式

2. 基于Ajax的长轮询(long-polling)

两种区别
两种区别

服务端发起请求,服务端挂起,有更新的时候,服务端才返回数据

demo
⓵ 客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<button onclick="run()">开始</button>
</body>
<script type="text/javascript">
function run(){
// 这个超时时间不设置的话就是不会因超时而停止
// 因为有可能客户端已经超时了,服务端才返回
// 所以设不设置看需求
axios.get('/url',{timeout: 10000}).then(r=>{
console.log(r.data)
run()
}).catch(e=>{
// 这块应该有个次数判断,我简写了
run()
})
}

</script>
⓶ 服务端(node)
1
2
3
4
5
6
7
app.use("/url", function(req, res) {
// 模拟一下时间
const time=Math.ceil(Math.random()*5);
setTimeout(() => {
res.json("成功");
}, time*1000);
})
⓷ gif
结果
结果

3. 基于Iframe及htmlfile的流(http streaming)

前台定义好获取数据的函数,参数是数据,后台传过来的是函数的调用

⓵ 客户端
1
2
3
4
5
6
7
8
9
 <script type="text/javascript">
function getJSON(data){
console.log(data)
}
</script>
<body>
<!-- 一般这个iframe是隐藏的或者通过js创建的,我简化了 -->
<iframe src="/iframe" frameborder="0"></iframe>
</body>
⓶ 服务端(node)
1
2
3
4
5
6
7
8
app.use("/iframe", function(req, res) {
// 模拟一下时间
const time=Math.ceil(Math.random()*5);
setTimeout(() => {
// 这就直接调用了
res.send('<script type="text/javascript">parent.getJSON('+time+')</script>');
}, time*1000);
})
⓷ gif
结果
结果