一、WebSocket介绍
- WebSocket协议是基于TCP的一种新的网络协议,它实现了浏览器与服务器全双工(full-duplex)通信,允许服务器主动发送信息给客户端。
- WebSocket是一种持久性协议,http是非持久性协议。
现在很多网站都有实时推送的需求,比如聊天,客服咨询等
早期没有WebSocket时,由于http请求服务器无法主动给浏览器发送数据,所以通过ajax轮询,浏览器定时给服务器发送请求(比如1s一次),服务器把最新的数据响应给浏览器。这种模式的缺点就是浪费性能和资源。
由图可知,http协议每发送一次请求,服务器给一次响应,然后就断开,直到下一次客户端发起请求;WebSocket在客户端建立好连接后,客户端和服务端之间不用断开,可以互相主动地发送消息。
WebSocket是一种网络协议,允许客户端和服务端全双工的进行网络通信,服务端可以给客户端发送消息,客户端也可以给服务端发送消息。
二、使用node创建WebSocket服务
1、项目搭建
- mkdir server
- cd server
- npm init -y
- npm i nodejs-websocket
2、server下新建app.js
const ws = require('nodejs-websocket') // 导入nodejs-websocket
const POST = 3000
// 创建socket服务
const server = ws.createServer(conn => {
console.log('接收到了新的连接') // 当浏览器新的窗口连接到ws://localhost:3000会触发这里
// 接收到客户端的文本内容时触发
conn.on('text', data => {
conn.send('我是服务器返回的:' + data.toUpperCase()) // 发送至客户端
})
// 关闭事件,浏览器窗口关闭时触发
conn.on('close', () => {
console.log('关闭了连接')
})
// 异常事件:浏览器窗口关闭了,发送的数据格式不对...
conn.on('error', () => {
console.log('连接异常')
})
})
server.listen(POST, () => {
console.log('服务器启动成功,端口号为:' + POST)
})
3、终端中执行node app.js
此时,基于node的socket服务就搭建好了,占用的端口号为3000
三、WebSocket基本使用
在H5中,浏览器已经实现了WebSocket的API,可以直接使用
- 创建WebSocket实例
const socket = new WebSocket('ws://localhost:3000')
- WebSocket事件
| 事件 | 事件处理程序 | 触发时机 |
|---|---|---|
| open | socket.onopen | 连接建立时触发 |
| message | socket.onmessage | 客户端接收到服务端数据时触发 |
| error | socket.onerror | 通信发生错误时触发 |
| close | socket.onclose | 连接关闭时触发 |
- WebSocket方法
| 方法 | 描述 |
|---|---|
| send | 发送数据至服务端 |
| close | 客户端手动关闭连接 |
<body>
<!-- 输入内容 -->
<input type="text" placeholder="输入你的内容" />
<!-- 发送请求 -->
<button>发送请求</button>
<!-- 显示结果 -->
<div></div>
<script>
const input = document.querySelector('input')
const button = document.querySelector('button')
const div = document.querySelector('div')
const socket = new WebSocket('ws://localhost:3000') // 创建socket实例
// open事件:当和WebSocket服务连接成功时触发
socket.addEventListener('open', function () {
div.innerHTML = '连接成功了'
})
// 主动给WebSocket服务发送消息
button.addEventListener('click', function () {
const { value } = input
console.log(value)
socket.send(value)
})
// 接收WebSocket服务器的数据
socket.addEventListener('message', function (e) {
console.log(e)
div.innerHTML = e.data
})
socket.addEventListener('close', function () {
div.innerHTML = '服务器断开连接'
})
</script>
</body>
效果:
四、基于以上的知识点,做一个聊天室DEMO
1、效果
可以看到这个dome中,某个用户进来/离开,发送消息时,服务器都会实时推送给所有的用户。这正体现了WebSocket是持久性的协议,并且会主动发送消息至客户端。这一点要是用http请求就完蛋了,每次由客户端发起请求再得到相应,这期间得产生多少资源的浪费
2、app.js
const ws = require('nodejs-websocket') // 导入nodejs-websocket
const POST = 3000
let userCount = 0
const nameMap = new Map([
[1, '王总'],
[2, '1号技师'],
[3, '2号技师']
])
const TYPE_ENTER = 0
const TYPE_LEAVE = 1
const TYPE_MSG = 2
// 通过广播给所有的用户发送消息 connections是一个数组,里面是所有的用户;conn.send只是给当前用户发送消息
const broadcase = data => {
for (const item of server.connections) item.send(JSON.stringify(data))
}
// 每个连接到服务器的用户,都有个conn对象
const server = ws.createServer(conn => {
console.log('接收到了新的连接')
userCount++
conn.username = nameMap.get(userCount)
// 1、当有用户连接上服务器时,广播告诉所有的用户,谁进入了,什么时间进入的
broadcase({
type: TYPE_ENTER,
msg: `${conn.username}进入了聊天室`,
time: new Date().toLocaleTimeString()
})
conn.on('text', data => {
// 当有一个用户发送消息时,广播告诉所有的用户,谁发送了xx消息,什么时间发送的
broadcase({
type: TYPE_MSG,
msg: data,
time: new Date().toLocaleTimeString()
})
})
conn.on('close', () => {
userCount--
console.log('关闭了连接')
broadcase({
type: TYPE_LEAVE,
msg: `${conn.username}离开了聊天室`,
time: new Date().toLocaleTimeString()
})
})
conn.on('error', () => {
console.log('连接异常')
})
})
server.listen(POST, () => {
console.log('服务器启动成功,端口号为:' + POST)
})
3、user.html
<body>
<!-- 输入内容 -->
<input type="text" placeholder="输入你的内容" />
<!-- 发送请求 -->
<button id="send">发送请求</button>
<!-- 显示结果 -->
<ul></ul>
<script>
const input = document.querySelector('input')
const button = document.querySelector('#send')
const ul = document.querySelector('ul')
const colorMap = new Map([
[0, 'yellowgreen'],
[1, 'red'],
[2, 'blue']
])
const nameMap = new Map([
[1, '王总'],
[2, '1号技师'],
[3, '2号技师']
])
const socket = new WebSocket('ws://localhost:3000') // 创建socket实例
// open事件:当和WebSocket服务连接成功时触发
socket.addEventListener('open', function () {
ul.innerHTML = '已连接服务器'
})
// 主动给WebSocket服务发送消息
button.addEventListener('click', function () {
const { value } = input
socket.send(value)
input.value = ''
})
// 接收WebSocket服务器的数据
socket.addEventListener('message', function (e) {
const { type, msg, time, userCount } = JSON.parse(e.data)
const li = document.createElement('li')
li.style.color = colorMap.get(type)
li.innerText = `${msg}----${time}`
ul.appendChild(li)
})
socket.addEventListener('close', function () {
div.innerHTML = '服务器断开连接'
})
</script>
</body>
五、socket.io基本使用
在socket.io中,通过emit方法触发自定义事件,通过on方法接收自定义事件
1、创建一个文件夹,安装socket.io
yarn add socket.io
app.js
const http = require('http')
const fs = require('fs')
const app = http.createServer()
// 当访问localhost:3000时,返回index.html
app.on('request', (req, res) => {
fs.readFile(__dirname + '/index.html', function (err, data) {
if (err) {
res.writeHead(500)
return res.end('Error loading index.html')
}
res.writeHead(200)
res.end(data)
})
})
app.listen(3000, () => {
console.log('服务器启动成功')
})
// 以上代码创建了一个node服务
const io = require('socket.io')(app)
// 监听用户连接事件
io.on('connection', socket => {
console.log('有新用户连接了')
socket.on('hehe', data => {
console.log('服务器接收', data)
socket.emit('haha', data)
})
})
这里也可以使用express启动服务:
- yarn add express
- app.js
const app = require('express')()
const server = require('http').Server(app)
const io = require('socket.io')(server)
server.listen(3000, () => {
console.log('服务器启动成功')
})
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html')
})
io.on('connection', socket => {
socket.on('hehe', data => {
console.log('服务器接收', data)
socket.emit('haha', data)
})
})
3、index.html
<body>
123
<script src="/socket.io/socket.io.js"></script>
<script>
console.log(io)
const socket = io('localhost:3000')
socket.on('haha', data => {
console.log('浏览器接收', data)
})
socket.emit('hehe', { name: 'xx', age: 10 })
</script>
</body>