介绍
WebSocket
是 HTML5
开始提供的一种在单个 TCP
连接上进行全双工通讯的协议。
WebSocket
使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API
中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API
中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5
定义的 WebSocket
协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
总而言之,WebSocket
让服务器可以主动向客户端发出请求,实现双向通信,并且可以在浏览器内使用。
使用
服务器
const express = require('express')
const app = express()
const expressWs = require('express-ws') // 引入 WebSocket 包
expressWs(app) // 将 WebSocket 服务混入 app,相当于为 app 添加 .ws 方法
app.get('/', function (req, res) {
res.send('Hello World!')
})
// 建立 WebSocket 服务
// ws 相当于建立 WebSocket 的实例
app.ws('/ws', ws=> {
console.log(ws)
// 使用 ws 的 send 方法向客户端发送请求
ws.send('hello')
// 使用 on 方法监听事件
// message 接收客户端的数据
ws.on('message', function (msg) {
console.log(msg)
ws.send('default response')
})
// close 事件表示客户端断开连接时执行的回调函数
ws.on('close', function (e) {
console.log('连接断开')
})
})
app.listen(3000);
复制代码
客户端
<script>
var ws = new WebSocket('ws://localhost:3000/ws');
ws.onopen = function () {
console.log('连接成功');
ws.send('hello');
};
// 接收来自服务端的数据,通过e.data获取
ws.onmessage = function (e) {
console.log('hello');
console.log(e.data);
};
</script>
复制代码
实现一个聊天室
创建项目并初始化
- 创建
onlineChat
- 执行
npm init
安装依赖并开启服务器
这里使用
express-ws
实现webSocket
通信
-
npm i express express-ws
-
开启
express
服务器const express = require('express') const app = express() const port = 3000 app.use('/', function(req,res){ console.log('hello'); }) app.listen(port, () => { console.log('server is running'); }) 复制代码
实现两个聊天页面
设计思路:
- 顶部:显示对方名称
- 内容:显示聊天内容
- 底部:输入框和发送按钮
聊天内容:对方发送的信息显示在左侧,自己发送的信息显示在右侧
我是两个页面分开写(张飞、关羽),这样实现比较简单。
代码
<div class="main">
<div class="top">二哥</div>
<div class="content" id="content">
<div class="msg left">
<img src="./guanyu1.png">
<div>我是二哥</div>
</div>
<div class="msg right">
<div>我是三弟</div>
<img src="./zhangfei1.png">
</div>
</div>
<div class="bottom">
<input id="inp" placeholder="请输入内容" />
<button class="sendBtn" onclick="sendBtn()">发送</button>
</div>
</div>
复制代码
此时两个页面已经写好了
效果展示:
我们通过express
提供的static
获取两个页面
通过
Express
内置的express.static
可以方便地托管静态文件,例如图片、CSS、JavaScript
文件等。
app.use(express.static('public'))
app.use('/guanyu', express.static('public/guanyu.html'))
app.use('/zhangfei', express.static('public/zhangfei.html'))
复制代码
实现数据传递
设计思路:用
服务器
将客户端A
发送回来的数据,保存并转发给客户端A
与客户端B
,从而实现聊天功能,客户端B
发送的数据也同理。
-
实现张飞向关羽发送数据
- 服务器接收到张飞发送的数据
- 服务器将数据发送给张飞,并存储数据
- 将存储的张飞数据发送给关羽
-
代码
websocket.js
// zhangfei to zhangfei 简写,表示接收张飞数据,转发给张飞,并存储。张飞调用。下同 router.ws('/ztz', ws => { ws.on('message', msg => { zhangfeiInfo.push(msg) ws.send(msg) }) }) // zhangfei to guanyu 简写,表示将张飞的数据,发送给关羽。关羽调用。下同 router.ws('/ztg', ws => { setInterval(() => { if (zhangfeiInfo.length > 0) { let msg = zhangfeiInfo[0] zhangfeiInfo.shift() ws.send(msg) } }, 100) }) 复制代码
zhangfei.html
const ztz = new WebSocket('ws://localhost:3000/ztz') ztz.onopen = () => {} ztz.onmessage = (res) => content.innerHTML += rightMsg(res.data) function rightMsg(value) { var rightHtml = ` <div class="msg right"> <div>${value}</div> <img src="./zhangfei1.png"> </div> `; return rightHtml; } function sendBtn(e) { let value = inp.value; ztz.send(value) } 复制代码
guanyu.html
const ztg = new WebSocket('ws://127.0.0.1:3000/ztg') ztg.onopen = () => {} ztg.onmessage = res => content.innerHTML += leftMsgFn(res.data) let inp = document.getElementById('inp') function leftMsgFn(value) { var leftHtml = ` <div class="msg left"> <img src="./zhangfei1.png"> <div>${value}</div> </div> `; return leftHtml; } 复制代码
到这里就实现了张飞发送数据给关羽
-
同理实现,关羽发送数据给张飞
-
效果展示
注意:这里获取数据是通过定时器,定时检查服务器存放的数据,如果有数据,那就通过
send
发送给客户端,所以,定时器设置的时间越短,对方接收的信息延迟就越低。不过时间设置太短容易造成服务器崩溃。
部署到服务器
到这里,项目已经完成了,但是只能在本地运行,我希望能随时随地实现两个设备聊天。
然后正好有个闲置的云服务器,所以我将项目部署到云服务器
-
进入宝塔面板
-
将项目上传到文件中
-
使用
PM2
管理器管理项目将传入的
node
文件添加到PM2
中 -
设置端口号,启动
注意:端口号需要提前打开
-
效果展示
不同地方的两台移动设备
总结
如果是http
通信,需要通过ajax
轮询,让浏览器每隔一段时间,发送一次请求,询问服务器是否有跟新数据,会有很多不必要的请求,浪费服务器资源
WebSocket
是http
的补充,它不需要通过ajax
轮询,只要服务器与客户端建立起连接,服务器就不必等待请求,之间发送数据,减少了资源消耗。