什么是websocket
?
websocket
是一种网络传输协议,可在单个tcp
连接上进行全双工通信,位于OSI
模型的应用层。
特点:
TCP
链接,与HTTP
协议兼容
- 双向通信,主动推送(服务端向客户端)
- 无同源限制,协议标识符为ws(加密wss)
应用场景
- 聊天、消息、点赞
- 直播评论(弹幕)
- 游戏、协调编辑、基于位置的应用
开始你的第一个websocket应用
websocket常用前端库
- ws (实现原生协议,特点:通用、性能高,定制型强)
- socket.io (向下兼容协议,特点:适配性强,性能一般)
3分钟编写一个ws应用
- 新建
server
目录,通过npm init -y
初始化server
目录
npm install ws
安装ws库
- 新建
index.js
作为入口文件1 2 3 4 5 6
| const WebSocket = require('ws') const wss = new WebSocket.Server({port:3000})
wss.on('connection',function connection(ws){ console.log('on client is connected') })
|
- 根目录下新建一个
client
目录,并添加index.html
文件1
| var ws = new WebSocket('ws://127.0.0.1:3000')
|
- 通过
node index.js
启动服务端
- 访问客户端的
index.html
,我们可以看到服务端控制台输出有用户连接了
客户端中使用ws方法与服务端类似
常见API介绍
https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket
websocket常见状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ws.onopen = function(){ console.log('open'+ws.readyState) ws.send('hello from client') } ws.onmessage = function(msg){ console.log('message'+ws.readyState) console.log(msg) }
ws.onclose = function(){ console.log('close'+ ws.readyState) console.log('yiguanbi') }
ws.onerror = function(){ console.log('error'+ ws.readyState) }
|
实现一个ws广播
1 2 3 4 5 6 7 8 9 10
|
... wss.clients.forEach((client)=>{ if(ws!==client&&client.readyState===WebSocket.OPEN){ client.send(msg.toString()) } }) ...
|
统计进入聊天室的人数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
|
sendMsg(){ this.lists.push(this.name+":"+this.message) this.ws.send(JSON.stringify({ event:'message', message:this.message, name:this.name })) this.message='' },
onMessage(event){ if(this.isshow) return var obj = JSON.parse(event.data) if(obj.event==='enter'){ this.lists.push('欢迎'+obj.message+'加入聊天室') }else if(obj.event ==='out'){ this.lists.push(obj.name+'离开了聊天室') }else { if(obj.name!==this.name){ this.lists.push(obj.name+":"+obj.message) } } this.num = obj.num },
let num = 0
ws.on("message", function (msg) { const msgObj = JSON.parse(msg); if (msgObj.event === "enter") { ws.name = msgObj.message; num++; } wss.clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { msgObj.num = num; client.send(JSON.stringify(msgObj)); } }); });
ws.on("close", function () { if(ws.name){ num--; } let msgObj = {} wss.clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { msgObj.num = num; msgObj.name = ws.name; msgObj.event = 'out' client.send(JSON.stringify(msgObj)); } }); });
|
实现多聊天室
调整client
代码,在enter
的时候带上聊天室的id
,广播的时候只对该聊天室进行广播
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| enter(){ if(this.name.trim()==='') { alert('用户名不得为空') return } this.isShow=false this.ws.send(JSON.stringify({ event:'enter', message:this.name, roomid:this.roomid })) }
wss.clients.forEach((client) => { if (client.readyState === WebSocket.OPEN&& client.roomid ===ws.roomid) { msgObj.name = ws.name client.send(JSON.stringify(msgObj)); } });
|
websocket鉴权
- 协议本身在握手阶段不提的
- 浏览器侧:url传参、message主动消息,session/cookie
- Nodejs侧:直接使用ws传Header
心跳检测&断线重连
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| const timeInterval = 1000 setInterval(()=>{ wss.clients.forEach((ws)=>{ if(!ws.isAlive){ group[ws.roomid]-- return ws.terminate() } ws.isAlive = false ws.send(JSON.stringify({ event:'heartbeat', message:'ping' })) }) },timeInterval)
onMessage(event) { if (this.isShow) return; console.log("message" + this.ws.readyState); var obj = JSON.parse(event.data); switch (obj.event) { case "noAuth": break; case "enter": this.lists.push("欢迎" + obj.message + "加入聊天室"); break; case "out": this.lists.push(obj.name + "离开了聊天室"); break; case 'heartbeat': this.ws.send(JSON.stringify({ event:'heartbeat', message:'pong' })) break; default: if (obj.name !== this.name) { this.lists.push(obj.name + ":" + obj.message); } } this.num = obj.num; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
checkServer(){ clearTimeout(this.handle) this.handle = setTimeout(() => { this.onClose() this.initWS() }, 1000+500); }
onError() { console.log("error" + ws.readyState); setTimeout(() => { this.initWS() }, 1000); },
|
相关代码请移步github —–> websocketBase