概述
WebSockets是一种在浏览器和服务器之间建立持久连接的协议,它允许服务器主动推送数据给客户端,并且在客户端和服务器之间实现双向通信。
-
建立连接:客户端通过在JavaScript代码中使用WebSocket对象来建立WebSockets连接。例如:
var socket = new WebSocket('ws://example.com/socket');
-
协议:WebSockets使用标准的WebSocket协议来进行通信。相对于HTTP协议,WebSocket协议提供了更高效率和更低的延迟。ws和wss协议正对应了http和https。
-
服务器支持:为了使用WebSockets,服务器必须支持WebSocket协议。
-
事件:WebSocket对象提供了一些事件,用于处理连接的不同阶段和接收到的数据。常见的事件有
onopen
(连接建立)、onmessage
(接收到消息)、onclose
(连接关闭)等。 -
数据传输:WebSockets可以传输文本和二进制数据。文本数据是以UTF-8编码发送和接收的,而二进制数据使用ArrayBuffer对象。
-
安全性:与HTTP协议通过TLS/SSL协议进行加密一样,WebSockets也可以通过wss协议来提供安全的通信。wss://表示通过TLS/SSL加密的WebSockets连接。
-
跨域策略:WebSockets默认不受同源策略的限制,可以跨域使用,但仍然会受到服务器的安全策略限制。
可以在浏览器和服务器之间建立稳定的连接,实际应用如聊天应用、实时数据监控等。
基本使用
使用WebSockets进行连接和通信:
首先,在客户端使用WebSocket对象来建立连接:
var socket = new WebSocket('ws://example.com/socket');
// 监听连接建立事件
socket.onopen = function() {
console.log('WebSocket连接已建立');
// 向服务器发送消息
socket.send('Hello Server!');
};
// 监听接收到消息事件
socket.onmessage = function(event) {
var message = event.data;
console.log('接收到服务器消息:' + message);
};
// 监听连接关闭事件
socket.onclose = function() {
console.log('WebSocket连接已关闭');
};
在上面的代码中,我们创建了一个WebSocket对象并指定连接的URL。然后我们监听了三个事件:onopen
表示连接建立时触发的事件,onmessage
表示接收到消息时触发的事件,onclose
表示连接关闭时触发的事件。
在连接建立后,我们向服务器发送了一条消息Hello Server!
。当服务器向客户端发送消息时,onmessage
事件将被触发,并且我们可以在回调函数中处理接收到的消息。
接下来,让我们看一下服务器端的示例代码(以Node.js和Express框架为例):
const express = require('express');
const WebSocket = require('ws');
const app = express();
// 创建WebSocket服务器
const server = require('http').createServer(app);
const wss = new WebSocket.Server({ server });
wss.on('connection', function(socket) {
console.log('WebSocket连接已建立');
// 监听客户端发送的消息
socket.on('message', function(message) {
console.log('接收到客户端消息:' + message);
// 向客户端发送消息
socket.send('Hello Client!');
});
// 监听连接关闭事件
socket.on('close', function() {
console.log('WebSocket连接已关闭');
});
});
server.listen(3000, function() {
console.log('服务器已启动,监听端口3000');
});
在服务器端的代码中,我们使用了ws库创建了一个WebSocket服务器,并监听了connection
事件。当有客户端连接到服务器时,connection
事件将被触发,并在该回调函数中处理连接过程。
在服务器接收到客户端的消息时,我们输出消息到控制台,并向客户端发送一条回复消息。当连接关闭时,close
事件将被触发。
最后,我们在Express应用中创建了一个HTTP服务器,将WebSocket服务器绑定到服务器上,然后启动服务器监听端口3000。
通信小实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
const ws = new WebSocket('ws://localhost:3000')
ws.addEventListener('open',()=> {
console.log('已连接服务器')
ws.send('我是前端的数据')
})
ws.addEventListener('message', ({data})=> {
console.log(data)
})
</script>
</body>
</html>
const WebSocket = require('ws')
const socket = new WebSocket.Server({port: 3000});
socket.on('connection',ws=> {
console.log('我进来了')
ws.on('message',(data)=> {
console.log('前端数据:' + data + "。后端返回数据:"+ '我是后端的数据')
})
ws.on('close',()=> {
console.log('溜了溜了')
})
})
Socket.io
Socket.io 具有跨平台,实时性,WebSocket 不可连接时自动降级到其他传播机制(如 http 长轮询)等优点。
官方文档:(服务器初始化 | Socket.IO)
npm install socket.io
安装。
聊天室案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
html,
body,
.room {
height: 100%;
width: 100%;
}
.room {
display: flex;
}
.left {
width: 300px;
border-right: 0.5px solid #2b2d30;
background: #333;
}
.right {
background: #1c1c1c;
flex: 1;
display: flex;
flex-direction: column;
}
.header {
background: rgb(60, 63, 65);
color: white;
padding: 10px;
box-sizing: border-box;
font-size: 20px;
}
.main {
flex: 1;
padding: 10px;
box-sizing: border-box;
font-size: 20px;
overflow: auto;
}
.main-chat {
color: #bcbec4;
}
.footer {
min-height: 200px;
border-top: 1px solid #4e5157;
}
.footer .ipt {
width: 100%;
height: 100%;
color: #bcbec4;
outline: none;
font-size: 20px;
padding: 10px;
box-sizing: border-box;
}
.groupList {
height: 100%;
overflow: auto;
}
.groupList-items {
height: 50px;
width: 100%;
background: rgb(67, 69, 74);
border-bottom: 1px solid #2b2d30;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
</style>
</head>
<div class="room">
<div class="left">
<div class="groupList">
</div>
</div>
<div class="right">
<header class="header">聊天室</header>
<main class="main">
</main>
<footer class="footer">
<div class="ipt" contenteditable></div>
</footer>
</div>
</div>
<body>
<script type="module">
import {io} from "https://cdn.socket.io/4.7.4/socket.io.esm.min.js";
let name = prompt('请输入你的名字')
let room = prompt('请输入你要进入的房间')
const sendMessage = (message) => {
const div = document.createElement('div');
div.className = 'main-chat';
div.innerText = `${message.user}:${message.text}`;
main.appendChild(div)
}
const group = document.querySelector('.groupList'); // 左侧列表的父级
const main = document.querySelector('.main'); // 消息内容的父级
const ipt = document.querySelector('.footer .ipt')
// 希望自己也能收到信息
const addChart = (msg)=> {
let item = document.createElement('div')
item.className = 'main-chat'
item.innerHTML = `${msg.name}: ${msg.message}`
main.appendChild(item)
}
const socket = io('ws://localhost:3000') // ws 的地址
socket.on('connect', () => {
// 监听回车事件
document.addEventListener('keydown',e=> {
if (e.key === 'Enter') {
const message = ipt.innerText
socket.emit('message', {name, message, room})
addChart({name, message})
ipt.innerText = ''
}
})
// 加入房间
socket.emit('join', {name, room})
//监听左侧列表
socket.on('groupMap', (data) => {
group.innerHTML = ``
Object.keys(data).forEach(key=> {
const item = document.createElement('div')
item.className = 'groupList-items'
item.innerHTML = `房间号:${key} 房间人数:${data[key].length}`
group.appendChild(item)
})
})
//监听发送的消息
socket.on('message', (data) => {
addChart(data)
})
})
</script>
</body>
</html>
import {createServer} from "http";
import {Server} from "socket.io";
const server = createServer();
const io = new Server(server, {
cors: true // 允许跨域
});
/**
* [{name, room, id}, {name, room, id}]
* @type {{}}
*/
const groupMap = {}
io.on("connection", (socket) => {
// 前端传过来 name 和 room
socket.on('join', ({name, room}) => {
socket.join(room) // 创建一个房间
if (groupMap[room]) {
groupMap[room].push({name, room, id: socket.id}) // 已有就 push
} else {
groupMap[room] = [{name, room, id: socket.id}] // 没有就创建
}
socket.emit('groupMap', groupMap)
// 所有人都可以看到
socket.broadcast.emit('groupMap', groupMap)
// 管理员发送信息
socket.broadcast.to(room).emit('message', {
name: '管理员',
message: `欢迎${name}进入聊天室`
})
})
// 向前端发消息
socket.on('message', ({name, message, room}) => {
// 虽然给别人发送了消息,但是自己收不到
socket.broadcast.to(room).emit('message', {name, message})
})
});
server.listen(3000);