共计 4287 个字符,预计需要花费 11 分钟才能阅读完成。
一 目录
不折腾的前端,和咸鱼有什么区别
目录 |
---|
一 目录 |
二 前言 |
三 WebSocket 和 HTTP |
3.1(短)轮询(Polling) |
3.2 长轮询 |
3.3 WebSocket |
3.4 两者比对 |
四 Socket.io |
4.1 服务端代码 |
4.2 客户端代码 |
4.3 小结 |
五 参考文献 |
二 前言
返回目录
WebSocket
是 HTML5
新增的一种全双工通信协议,客户端和服务器基于 TCP 握手连贯胜利后,两者之间就能够建设持久性的连贯,实现双向数据传输。
三 WebSocket 和 HTTP
返回目录
咱们晓得 HTTP
协定是一种单向的网络协议,在建设连贯后,它容许客户端向服务器发送申请资源后,服务器才会返回相应的数据,而服务器不能被动推送数据给客户端。
过后为什么这样设计呢?假如一些不良广告商被动将一些信息强行推送给客户端,这不得不说是一个劫难。
所以当初咱们要做股票的实时行情,或者获取火车票的残余票数等,就须要客户端和服务器之间重复地进行 HTTP
通信,客户端一直地发送 GET
申请,去获取以后的实时数据。
上面介绍几种常见的形式。
3.1(短)轮询(Polling)
返回目录
短轮询模式下,客户端每隔一段时间向服务器发送 HTTP
申请。
服务器收到申请后,将最新的数据发回给客户端。
这种状况下的弊病是非常明显的:某个时间段服务器没有更新内容,然而客户端每隔一段时间发送申请来询问,而这段时间内的申请是有效的。
这就导致了网络带宽的节约。
3.2 长轮询
返回目录
长轮询模式下,客户端向服务器发出请求,服务器并不一定会立刻回应客户端,而是查看数据是否有更新。
如果数据更新了的话,那就立刻发送数据给客户端;如果没有更新,那就放弃这个申请,期待有新的数据到来,才将数据返回给客户端。
如果服务器长时间没有更新,那么一段时间后,申请变会超时,客户端收到音讯后,会立刻发送一个新的申请给服务器。
当然这种形式也有弊病:当服务器向客户端发送数据后,必须期待下一次申请能力将新的数据收回,这样客户端接管新数据就有一个最短时间距离。
如果服务器更新频率较快,那么就会呈现问题。
3.3 WebSocket
返回目录
基于后面的状况,为了彻底解决服务器被动向客户端发送数据的问题。
W3C
在 HTML5
中提供了一种让客户端与服务器之间进行全双工通信的网络技术 WebSocket
。
WebSocket
基于 TCP
协定,是一种全新的、独立的协定,与 HTTP
协定兼容却不会融入 HTTP
协定,仅仅作为 HTML5
的一部分。
3.4 两者比对
返回目录
基于下面,小伙伴们大略理解了 WebSocket
的原因了,这里再总结比照一下 HTTP
和 WebSocket
。
- 相同点
- 都须要建设
TCP
连贯 - 都属于七层协定中的应用层协定
- 不同点
HTTP
是单向数据流,客户端向服务器发送申请,服务器响应并返回数据;WebSocket
连贯后能够实现客户端和服务器双向数据传递,除非某一端断开连接。HTTP
的url
应用http//
或者https//
结尾,而WebSocket
的url
应用ws//
结尾
四 Socket.io
返回目录
咱们先来看 WebSocket
的一个应用形式:
const ws = new WebSocket("ws//:xxx.xx", [protocol]) | |
ws.onopen = () => {ws.send('hello') | |
console.log('send') | |
} | |
ws.onmessage = (ev) =>{console.log(ev.data) | |
ws.close()} | |
ws.onclose = (ev) =>{console.log('close') | |
} | |
ws.onerror = (ev) =>{console.log('error') | |
} |
WebSocket
实例化后,前端能够通过下面介绍的办法进行对应的操作,看起来还是蛮简略的。
然而,如果想齐全搭建一个 WebSocket
服务端比拟麻烦,又浪费时间。
所以:Socket.io
基于 WebSocket
,加上轮询机制以及其余的实时通信方面的内容,实现的一个库,它在服务端实现了实时机制的响应代码。
也就是说,WebSocket
仅仅是 Socket.io
实现通信的一个子集。
因而,WebSocket
客户端连贯不上 Socket.io
服务端,Socket.io
客户端也连不上 WebSocket
服务端。
上面咱们解说下如何实现一个简略的聊天。
4.1 服务端代码
返回目录
package.json
{ | |
"devDependencies": { | |
"express": "^4.15.2", | |
"socket.io": "^2.3.0" | |
} | |
} |
index.js
let express = require('express'); | |
let app = express(); | |
let server = require('http').createServer(app); | |
let io = require('socket.io')(server); | |
let path = require('path'); | |
app.use('/', (req, res, next) => {res.status(200).sendFile(path.resolve(__dirname, 'index.html')); | |
}); | |
// 开启 socket.io | |
io.on('connection', (client) => { | |
// 如果有新客户端进来,显示 ID | |
console.log(` 客户端 ID:${client.id}`); | |
// 监听客户端的输出信息 | |
client.on('channel', (data) => {console.log(` 客户端 ${client.id} 发送信息 ${data}`); | |
io.emit('broadcast', data); | |
}); | |
// 判断客户端是否敞开 | |
client.on('disconnect', () => {console.log(` 客户端敞开:${client.id}`); | |
}); | |
}); | |
server.listen(3000, () => {console.log('服务监听 3000 端口'); | |
}); |
如上,咱们间接通过 npm i
装置依赖包后,间接通过 node index.js
能够开启服务。
当然,如果小伙伴们想手动装包,执行上面命令即可:
npm i express socket.io express -D
4.2 客户端代码
返回目录
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<title>Socket.io</title> | |
<script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.slim.js"></script> | |
</head> | |
<body> | |
<input type="text" id="input"> | |
<button id="btn">send</button> | |
<div id="content-wrap"></div> | |
<script> | |
window.onload = function () { | |
let inputValue = null; | |
// 连贯 socket.io | |
let socket = io('http://localhost:3000'); | |
// 将创立的信息以增加 p 标签的模式展现成列表 | |
socket.on('broadcast', data => {let content = document.createElement('p'); | |
content.innerHTML = data; | |
document.querySelector('#content-wrap').appendChild(content); | |
}) | |
// 设置输入框的内容 | |
let inputChangeHandle = (ev) => {inputValue = ev.target.value;} | |
// 获取输入框并监听输出 | |
let inputDom = document.querySelector("#input"); | |
inputDom.addEventListener('input', inputChangeHandle, false); | |
// 当用户点击发送信息的时候,进行数据交互 | |
let sendHandle = () => {socket.emit('channel', inputValue); | |
} | |
let btnDom = document.querySelector("#btn"); | |
btnDom.addEventListener('click', sendHandle, false); | |
// 打页面卸载的时候,告诉服务器敞开 | |
window.onunload = () => {btnDom.removeEventListener('click', sendHandle, false); | |
inputDom.removeEventListener('input', inputChangeHandle, false); | |
} | |
}; | |
</script> | |
</body> | |
</html> |
4.3 小结
返回目录
Socket.io
不仅反对 WebSocket
,还反对许多种轮询机制以及其余实时通信形式,并封装了通用的接口。
这些形式蕴含 Adobe Flash Socket
、Ajax
长轮询、Ajax multipart streaming
、长久 Iframe
、JSONP
轮询等。
换句话说,当 Socket.io
检测到以后环境不反对 WebSocket
时,可能主动地抉择最佳的形式来实现网络的实时通信。
这样,咱们就对 WebSocket
有肯定的理解,面试的时候就不慌了。
五 参考文献
返回目录
- [x] websocket 与 Socket.IO 介绍【浏览倡议:10min】
- [x] WebSocket 与 Socket.IO【浏览倡议:10min】
- [x] Websocket 和 Socket.io 的区别及利用【浏览倡议:20min】
jsliang 的文档库由 梁峻荣 采纳 常识共享 署名 - 非商业性应用 - 雷同形式共享 4.0 国内 许可协定 进行许可。<br/> 基于 https://github.com/LiangJunrong/document-library 上的作品创作。<br/> 本许可协定受权之外的应用权限能够从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处取得。