webim(Instant Messaging,im即时通讯)
websocket是双向服务
nodejs有两个常用的库: ws(原生websocket协议实现的) Socket.io(对websocket进行的封装,并且兼容不支持的socktio的浏览器)
websocket可以主动向客户端发送消息: 如外卖众包平台的最新订单,安卓的消息推送好像也是
一:ws
npm i ws
// 客户端
const WebSocket = require('ws')
const wss = new WebSocket('ws://127.0.0.1:8000')
wss.on('open', function () { // 接受open事件
wss.send('hellow', 0) // 1.发给服务端
wss.on('message',function(msg){
console.log(msg) // 2.接受服务端
})
})
// 服务端
const WebSocket = require('ws')
const wss = new WebSocket.Server({ port: 8000 })
wss.on('connection', function (ws) {
ws.on('message', function (msg) { // 监听message事件
console.log(msg) // 1.接受客户端
})
ws.send('message from server') // 2.接受客户端
})
二:socket.io
三: websocket聊天应用
//client/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 用户输入昵称后才能进入聊天室 -->
<div v-if="flag">
<input type="text" value="请输入昵称" v-model="name">
<button @click="into">进入聊天室</button>
</div>
<div v-else>
<div>在线人数:{{num}}</div>
<div class="list" v-for="(item,index) in list" :key="index">{{item.message}}</div>
<input type="text" v-model="ipt">
<button @click="sumbit">发送</button>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
// 1.发送消息
// 客户端 取input值 -> websocket -> 发送到服务器 -> 转发给其它的所有客户端
var app = new Vue({
el: '#app',
data: function () {
return {
list: [{ message: 'message' }],
ipt: 'hello',
wsHandle: '',
flag: true,
name: '',
num: 0
}
},
methods: {
onOpen: function () {
console.log('client is connected')
},
// 收到消息
onMessage: function (evt) {
// 把数据推送到list中
var msg = JSON.parse(evt.data)
if (msg.num) { this.num = msg.num }
if (msg.event === 'login') {
this.list.push({
message: '欢迎' + msg.name + '加入聊天室'
})
} else if (msg.event === 'logout') {
this.list.push({
message: msg.name + '退出了聊天室'
})
}
else {
if (msg.name !== this.name) { // 不等于当前用户去推送
this.list.push({
message: msg.name + ':' + msg.message
})
}
}
},
// 输入昵称后的聊天书界面
sumbit: function () {
// 取inputvalue
// 通过webSocket发送数据
console.log(this.ipt)
this.wsHandle.send(JSON.stringify({
message: this.ipt,
name: this.name,
event: 'message'
}))
this.list.push({
message: this.name + ':' + this.ipt
})
this.ipt = ''
},
// 输入昵称登录
into: function () {
if (this.name.trim() === '') {
return alert('用户昵称不能为空')
}
console.log(this.name)
this.wsHandle.send(JSON.stringify({
name: this.name,
event: 'login'
}))
this.flag = false
}
},
mounted() {
var _this = this
this.wsHandle = new WebSocket('ws://localhost:8000')
this.wsHandle.onopen = this.onopen
// 服务端发送回来的其它消息
this.wsHandle.onmessage = this.onMessage
}
})
</script>
</body>
</html>
服务端
// /server/index.js
const WebSocket = require('ws')
const wss = new WebSocket.Server({ port: 8000 })
wss.on('connection', function (ws) {
console.log('a new client is connected')
ws.on('message', function (msg) { // 监听message事件
// 将受到的msg广播到其它客户端
let msgObj = JSON.parse(msg)
msgObj.num = wss.clients.size // 连接数量
if (msgObj.name) {
ws.name = msgObj.name
}
wss.clients.forEach(function each (client) { // 广播代码
// 广播给非自己的客户端 client !== ws &&
// 这里是广播所有用户,包括自己
if (client.readyState === WebSocket.OPEN) {
// WebSocket.OPEN readyState属性返回实例对象的当前状态,共有四种 OPEN:值为1,表示连接成功
client.send(JSON.stringify(msgObj))
}
})
})
ws.on('close', function () {
if (typeof ws.name !== 'undefined') { // ws.name存在才广播
console.log('onee client is closed:' + ws)
wss.clients.forEach(function each (client) { // 广播代码
// 广播给非自己的客户端 client !== ws &&
// 这里是广播所有用户,包括自己
if (client.readyState === WebSocket.OPEN) {
// WebSocket.OPEN readyState属性返回实例对象的当前状态,共有四种 OPEN:值为1,表示连接成功
client.send(JSON.stringify({
name: ws.name,
event: 'logout',
num: wss.clients.size
}))
}
})
}
})
// ws.send('message from server') // 2.接受客户端
})