# 一、简介
# 1、快速认知
图解(HTTP 与 websocket):
学习文档:
# 2、应用场景
- 弹幕
- 协同编辑
- 外卖员位置
- 数据实时更新(体育赛况、股票报价等等)
# 二、全双工通信
# 1、TCP 协议
Websocket 并不是全新的协议(2008 年就诞生了),它是利用了 HTTP 协议来建立连接的(或者说是建立在 TCP 协议之上的)。
TCP 协议本身就实现了全双工通信
# 2、安全通信
安全的 WebSocket 连接机制和 HTTPS 类似,底层通信走的仍然是安全的 SSL/TLS 协议。
# 三、实时通信
# 1、HTTP 协议
HTTP 协议做不到服务器主动向客户端推送信息,通信只能由客户端发起(单向请求)
提示
HTTP 协议是无状态的,每个请求都是独立的,客户端向服务器发送请求后,服务器返回响应后就会关闭连接,这种方式称为“请求-响应模式”
实际上 HTTP 协议是建立在 TCP 协议上的,但是 HTTP 协议的请求-应答机制限制了全双工通信。
但是,我们任然可以通过"轮询"方式实现实时通信。
轮询
- 前端
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>实时通信</title>
</head>
<body>
<h1>实时通信</h1>
<form>
<input id="messageInput" />
<button id="sendButton">发送</button>
</form>
<ul id="messages"></ul>
<script>
const messageList = document.getElementById('messages')
const messageInput = document.getElementById('messageInput')
const sendButton = document.getElementById('sendButton')
// 发送消息到服务器
function sendMessage(message) {
fetch('http://localhost:3000/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ message })
})
}
// 获取最新的消息
function getMessages() {
fetch('http://localhost:3000/messages')
.then((res) => res.json())
.then((messages) => {
messageList.innerHTML = ''
messages.forEach((message) => {
const li = document.createElement('li')
li.textContent = message
messageList.appendChild(li)
})
// 获取完最新的消息后再次启动定时器
setTimeout(getMessages, 1000)
})
}
// 发送消息按钮点击事件
sendButton.addEventListener('click', (event) => {
event.preventDefault()
const message = messageInput.value.trim()
console.log(message)
if (message) {
sendMessage(message)
messageInput.value = ''
}
})
// 启动定时器
setTimeout(getMessages, 1000)
</script>
</body>
</html>
- 后端
const express = require('express')
const app = express()
const cors = require('cors')
app.use(express.urlencoded({ extended: false })) // JSON数据处理👀
app.use(express.json())
app.use(cors())
// 定义一个数组,用于保存所有的消息
let messages = []
// 定义一个路由,用于获取所有的消息
app.get('/messages', (req, res) => {
res.send(messages)
})
// 定义一个路由,用于添加一条新的消息
app.post('/messages', (req, res) => {
const message = req.body.message
if (message) {
messages.push(message)
console.log(messages)
}
res.sendStatus(200)
})
// 监听端口
app.listen(3000, () => {
console.log('App listening on port 3000!')
})
# 2、WebSocket 协议
websocket 协议可以在客户端和服务器之间创建一个持久连接,使得双方能够随时向对方发送数据。
特性
- 可以发送文本,也可以发送二进制数据
- 没有同源限制
- 协议标识符是 ws(如果加密,则为 wss),服务器网址就是 URL
Node.js 本身以支持的协议包是包含 TCP 协议和 HTTP 协议的,因此要支持 WebSocket 协议的话,只需要添加并配置一下模块即可。
websocket 是"长连接服务器端推技术"的一种。
# ① ws 库
使用 ws 是基于原生的 WebSocket API (opens new window) 实现的,不会被同源策略所限制
ws 库
- 前端
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>实时通信</title>
</head>
<body>
<h1>实时通信</h1>
<form>
<input id="messageInput" />
<button id="sendButton">发送</button>
</form>
<ul id="messages"></ul>
<script>
const messageList = document.getElementById('messages')
const messageInput = document.getElementById('messageInput')
const sendButton = document.getElementById('sendButton')
let socket = null
// 连接 WebSocket 服务器
function connect() {
socket = new WebSocket('ws://localhost:3000')
// 监听 WebSocket 连接
socket.onopen = () => {
console.log('WebSocket 连接已建立')
}
// 监听 WebSocket 消息
socket.onmessage = (event) => {
const message = event.data
const li = document.createElement('li')
li.textContent = message
messageList.appendChild(li)
}
// 监听 WebSocket 关闭事件
socket.onclose = (event) => {
console.log('WebSocket 连接已关闭')
setTimeout(connect, 3000)
}
}
// 发送消息到 WebSocket 服务器
function sendMessage(message) {
socket.send(message)
}
// 发送消息按钮点击事件
sendButton.addEventListener('click', (event) => {
event.preventDefault()
const message = messageInput.value.trim()
if (message) {
sendMessage(message)
messageInput.value = ''
}
})
// 连接 WebSocket 服务器
connect()
</script>
</body>
</html>
- 后端
const express = require('express')
const http = require('http')
const WebSocket = require('ws')
// 创建 HTTP 服务器并将其传递给 WebSocket 服务器
const server = http.createServer(express())
const wss = new WebSocket.Server({ server })
// 储存所有客户端的连接
const clients = new Set()
// 监听 WebSocket 连接
wss.on('connection', (ws) => {
// 将新的连接加入到客户端集合中
clients.add(ws)
// 监听 WebSocket 消息
ws.on('message', (message) => {
// 将消息广播给所有客户端
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message, { binary: false }) // 不要使用二进制👀
}
})
})
// 监听 WebSocket 关闭事件
ws.on('close', () => {
// 将断开连接的客户端从集合中移除
clients.delete(ws)
})
})
// 启动服务器(注意这里所有server监听👀)
server.listen(3000, () => {
console.log('服务器已启动')
})
# ② socket.io 库
引入 socket.io 的client-dist (opens new window)文件
socket.io
- 前端
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>实时通信</title>
<script src="./socket.io.min.js"></script>
</head>
<body>
<h1>实时通信</h1>
<form>
<input id="messageInput" />
<button id="sendButton">发送</button>
</form>
<ul id="messages"></ul>
<script>
const messageList = document.getElementById('messages')
const messageInput = document.getElementById('messageInput')
const sendButton = document.getElementById('sendButton')
// socket = new WebSocket('ws://localhost:3000')
const socket = io('http://localhost:3000', { transport: ['websocket'] }) // 指定传输方式
// 监听 WebSocket 连接 ✖
// socket.onopen = () => {
// console.log('WebSocket 连接已建立')
// }
// 监听 WebSocket 消息
// socket.onmessage = event => {
// const message = event.data
// const li = document.createElement('li')
// li.textContent = message
// messageList.appendChild(li)
// }
// 👀
socket.on('message', (message) => {
const li = document.createElement('li')
li.textContent = message
messageList.appendChild(li)
})
// 监听 WebSocket 关闭事件 ✖
// socket.onclose = event => {
// console.log('WebSocket 连接已关闭')
// setTimeout(connect, 3000)
// }
// 发送消息到 WebSocket 服务器
function sendMessage(message) {
// socket.send(message)
socket.emit('message', message) // 👀
}
// 发送消息按钮点击事件
sendButton.addEventListener('click', (event) => {
event.preventDefault()
const message = messageInput.value.trim()
if (message) {
// sendMessage(message)
socket.emit('message', message) // 👀
messageInput.value = ''
}
})
</script>
</body>
</html>
- 后端
const express = require('express')
const http = require('http')
const socketIO = require('socket.io')
// // 创建 HTTP 服务器并将其传递给 WebSocket 服务器
const server = http.createServer(express())
const io = socketIO(server, {
cors: {
origin: 'http://localhost:8081' // 跨域问题(使用cors包行不通)
}
})
// 监听 WebSocket 连接
io.on('connection', (socket) => {
// 自动处理(add)👀
// 监听 WebSocket 消息
socket.on('message', (msg) => {
// 广播消息给所有连接的客户端
io.emit('message', msg) // 简化🤔
})
// 监听断开连接事件
socket.on('disconnect', () => {
// 自动处理(delete)👀
})
})
// 启动服务器
server.listen(3000, () => {
console.log('服务器已启动')
})
# 3、websocket 库
WebSocket 是一种基于 TCP 协议进行全双工通信的协议,通常用于实时的数据交换。在 Node.js 中,有许多流行的 npm 包可以用于 WebSocket 通信(服务器端推技术)。以下是一些常用的 npm 包:
- ws (opens new window):只支持原生的 WebSocket 协议,并且 API 接口比较简单,只提供了基本的 WebSocket 功能
- socket.io (opens new window):支持多种传输协议(WebSocket、HTTP 长轮询、HTTP 短轮询等),还提供了自动重连等功能
# 四、聊天室
实际上在线聊天室还需要实现很多其他功能,比如用户身份验证、聊天消息的持久化存储、实时通知等。
略
← MongoDB(koa) 【聊天室】 →