因为 HTTP 协议有一个缺陷:通信只能先由客户端发起,然后服务器再作出响应,并不能由服务器主动向客户端推送消息。HTTP是半双工协议
WebSocket 协议最大的特点是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息。WebSocket全双工通讯的网络技术
客户端和服务器之间会一直进行连接,每隔一段时间客户就会主动发送请求给服务器端询问一次。这种方式连接数会很多,而且每次发送请求都会有Http的Header头,会很耗流量。
//服务端
let express = require('express');
let app = express();
app.use(express.static('www'));
app.get('/', (req,res) => {
res.send(Date.now());
});
app.listen(3000);
<!--客户端-->
<div id="root"></div>
<script>
setInterval(function () {
let xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:3000/', true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
document.querySelector('#root').innerHTML = xhr.responseText;
}
}
xhr.send();
}, 1000);
长轮询是对轮询的改进版,客户端发送HTTP给服务器之后,看有没有新消息,如果没有新消息,就一直等待,当有新消息的时候,才会返回给客户端。在某种程度上减小了网络带宽问题。
//服务器
let express = require('express');
let app = express();
app.use(express.static('www'));
app.get('/', (req, res) => {
let timer = setInterval(() => {
let seconds = new Date().getSeconds()
if (seconds == 50) {
res.send(Date.now())
clearInterval(timer)
}
}, 1000)
})
app.listen(3000);
<!--客户端-->
<div id="root"></div>
<script>
(function send() {
let xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:3000', true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
document.querySelector('#root').innerHTML = xhr.responseText;
send();
}
}
xhr.send();
})();
</script>
通过在HTML页面里嵌入一个隐藏的iframe,然后将这个iframe的src属性设为对一个长连接的请求,服务器端就能源源不断地往客户推送数据。
//服务器
const express = require('express');
const app = express();
app.use(express.static('www'));
app.get('/', (req, res) => {
setInterval( () => {
res.write(`
<script type="text/javascript">
parent.document.getElementById('root').innerHTML = "${Date.now()}";
</script>
`);
}, 1000);
});
app.listen(3000);
<!--客户端-->
<div id="root"></div>
<iframe src="/" style=" display:none" />
EventSource 是服务器推送的一个网络事件接口。一个EventSource实例会对HTTP服务开启一个持久化的连接,以text/event-stream 格式发送事件, 会一直保持开启直到被要求关闭。
与 WebSockets,不同的是,服务端推送是单向的。
//服务器
/*服务器返回一定要按照必须的格式
//头信息
res.setHeader("content-type","text/event-stream")
//响应信息
res.send("event:类型\ndata:数据\n\n")*/
//----------------------
app.get('/ajax', (req, res) => {
res.setHeader("content-type", "text/event-stream")
setInterval(() => {
let dt = Date.now()
if (new Date().getSeconds() % 2 == 0) {
res.write("event:message\ndata:" + dt + "\n\n")
}
}, 1000);
})
//客户端
const es = new EventSource(url);
//接受服务器发送过来的数据
es.onmessage = ev=> ev.data
HTML5开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议,它基于TCP传输协议,并复用HTTP的握手通道。
websocket 优势在于,支持双向通信,实时性更强。较少的控制开销。连接创建后,客户端、服务端进行数据交换时,协议控制的数据包头部较小。
安装:npm i -S ws
# 服务器
const express = require('express')
const http = require('http')
const Ws = require('ws').Server
const app = express()
const server = http.createServer(app)
server.listen(3000)
app.use(express.static('www'))
let wsServer = new Ws({ server })
wsServer.on('connection', socket => {
console.log('连接成功');
// 监听客户端发过来的消息
socket.on('message', msg => {
console.log('客户端发送过来的消息:' + msg);
// 服务器给客户端发送消息
socket.send('服务器说:你好客户端')
})
});
<!--客户端-->
<script>
let ws = new WebSocket('ws://127.0.0.1:3000')
ws.onopen = () => {
ws.send('hello server')
};
ws.onmessage = ev => {
console.log(ev.data)
}
</script>
Socket.IO是一个WebSocket库,包括了客户端的js和服务器端的nodejs,它会自动根据浏览器从WebSocket、AJAX长轮询、Iframe流等等各种方式中选择最佳的方式来实现网络实时应用,非常方便和人性化,而且支持的浏览器最低达IE5.5。
安装:npm i -S socket.io
//服务端
const express = require('express')
const http = require('http')
const app = express()
const server = http.createServer(app)
const io = require('socket.io')(server)
server.listen(3000)
app.use(express.static('www'))
// 监听客户端的连接事件
io.on('connection', socket => {
socket.on('message', msg => {
console.log('接受到客户端发过来的消息:' + msg);
socket.send('服务器说:' + msg)
})
})
<!--客户端-->
<script src="/socket/socket.io.js"></script>
<script>
let socket = io.connect('http://localhost:3000/')
socket.on('connect', () => {
console.log('连接成功')
socket.emit('message','hello')
});
socket.on('message', msg => {
console.log(msg)
});
// 断开连接
socket.on('disconnect', () => {
console.log('断开连接')
});
</script>
// 全局广播
io.emit('message','全局广播');
// 向除了自己外的所有人广播
socket.broadcast.emit('message', msg);