Socket.io 中文教程
1. 核心概念
Socket.io 是一个功能强大的实时通信库,由 Guillermo Rauch 开发和维护。它提供了一个简单而灵活的 API,用于在客户端和服务器之间建立实时双向通信。
1.1 什么是 Socket.io?
Socket.io 是一个实时通信库,提供了:
- 基于 WebSocket 的实时双向通信
- 自动降级到轮询(Polling)等其他传输方式
- 房间(Rooms)和命名空间(Namespaces)支持
- 事件系统
- 错误处理和重连机制
- 与各种前端框架的集成
1.2 为什么选择 Socket.io?
- 实时双向通信:提供了真正的实时双向通信能力
- 可靠性:自动处理连接断开和重连
- 兼容性:支持所有现代浏览器,自动降级到其他传输方式
- 扩展性:支持房间和命名空间,便于组织和管理连接
- 易用性:简洁的 API 设计,易于上手
2. 安装与配置
2.1 基本安装
在 Node.js 项目中安装 Socket.io:
npm install socket.io2.2 基本配置
创建一个简单的 Socket.io 服务器:
import { createServer } from 'node:http';
import { Server } from 'socket.io';
const httpServer = createServer();
const io = new Server(httpServer, {
// 配置选项
cors: {
origin: '*',
methods: ['GET', 'POST']
}
});
// 监听连接事件
io.on('connection', (socket) => {
console.log('新用户连接:', socket.id);
// 监听消息事件
socket.on('message', (data) => {
console.log('收到消息:', data);
// 广播消息给所有客户端
io.emit('message', data);
});
// 监听断开连接事件
socket.on('disconnect', () => {
console.log('用户断开连接:', socket.id);
});
});
// 启动服务器
httpServer.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});2.3 客户端安装
在前端项目中安装 Socket.io 客户端:
npm install socket.io-client2.4 客户端配置
创建一个简单的 Socket.io 客户端:
import { io } from 'socket.io-client';
// 连接到服务器
const socket = io('http://localhost:3000', {
// 配置选项
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000
});
// 监听连接事件
socket.on('connect', () => {
console.log('连接成功:', socket.id);
});
// 监听消息事件
socket.on('message', (data) => {
console.log('收到消息:', data);
// 处理消息
});
// 监听断开连接事件
socket.on('disconnect', () => {
console.log('断开连接');
});
// 发送消息
function sendMessage(message) {
socket.emit('message', message);
}3. 基本使用
3.1 事件系统
Socket.io 使用事件系统进行通信,服务器和客户端都可以触发和监听事件:
3.1.1 服务器端事件
// 监听连接事件
io.on('connection', (socket) => {
// 监听自定义事件
socket.on('chat message', (msg) => {
console.log('收到聊天消息:', msg);
// 广播消息给所有客户端
io.emit('chat message', msg);
});
// 监听用户加入事件
socket.on('user join', (username) => {
console.log('用户加入:', username);
// 广播用户加入消息
io.emit('user joined', username);
});
});3.1.2 客户端事件
// 连接到服务器
const socket = io('http://localhost:3000');
// 发送聊天消息
socket.emit('chat message', 'Hello everyone!');
// 发送用户加入事件
socket.emit('user join', 'John Doe');
// 监听聊天消息事件
socket.on('chat message', (msg) => {
console.log('收到聊天消息:', msg);
// 显示消息
});
// 监听用户加入事件
socket.on('user joined', (username) => {
console.log('用户加入:', username);
// 显示用户加入通知
});3.2 房间(Rooms)
Socket.io 的房间功能允许你将客户端分组,以便向特定组的客户端发送消息:
3.2.1 服务器端房间操作
io.on('connection', (socket) => {
// 加入房间
socket.on('join room', (roomId) => {
socket.join(roomId);
console.log(`用户 ${socket.id} 加入房间 ${roomId}`);
// 向房间内的所有客户端发送消息(除了当前客户端)
socket.to(roomId).emit('user joined', `用户 ${socket.id} 加入了房间`);
});
// 离开房间
socket.on('leave room', (roomId) => {
socket.leave(roomId);
console.log(`用户 ${socket.id} 离开房间 ${roomId}`);
// 向房间内的所有客户端发送消息
socket.to(roomId).emit('user left', `用户 ${socket.id} 离开了房间`);
});
// 向特定房间发送消息
socket.on('send to room', ({ roomId, message }) => {
io.to(roomId).emit('room message', message);
});
});3.2.2 客户端房间操作
// 连接到服务器
const socket = io('http://localhost:3000');
// 加入房间
socket.emit('join room', 'room1');
// 离开房间
socket.emit('leave room', 'room1');
// 向房间发送消息
socket.emit('send to room', {
roomId: 'room1',
message: 'Hello room1!'
});
// 监听房间消息
socket.on('room message', (message) => {
console.log('收到房间消息:', message);
// 显示消息
});
// 监听用户加入/离开事件
socket.on('user joined', (notification) => {
console.log(notification);
// 显示通知
});
socket.on('user left', (notification) => {
console.log(notification);
// 显示通知
});3.3 命名空间(Namespaces)
Socket.io 的命名空间功能允许你创建多个独立的通信通道:
3.3.1 服务器端命名空间
// 创建默认命名空间
const io = new Server(httpServer);
// 创建自定义命名空间
const chatNamespace = io.of('/chat');
const gameNamespace = io.of('/game');
// 默认命名空间的连接处理
io.on('connection', (socket) => {
console.log('用户连接到默认命名空间:', socket.id);
});
// 聊天命名空间的连接处理
chatNamespace.on('connection', (socket) => {
console.log('用户连接到聊天命名空间:', socket.id);
// 聊天命名空间的事件处理
socket.on('chat message', (msg) => {
chatNamespace.emit('chat message', msg);
});
});
// 游戏命名空间的连接处理
gameNamespace.on('connection', (socket) => {
console.log('用户连接到游戏命名空间:', socket.id);
// 游戏命名空间的事件处理
socket.on('game move', (move) => {
gameNamespace.emit('game move', move);
});
});3.3.2 客户端命名空间
// 连接到默认命名空间
const defaultSocket = io('http://localhost:3000');
// 连接到聊天命名空间
const chatSocket = io('http://localhost:3000/chat');
// 连接到游戏命名空间
const gameSocket = io('http://localhost:3000/game');
// 使用聊天命名空间
tchatSocket.emit('chat message', 'Hello chat!');
chatSocket.on('chat message', (msg) => {
console.log('收到聊天消息:', msg);
});
// 使用游戏命名空间
gameSocket.emit('game move', { player: 'John', move: 'left' });
gameSocket.on('game move', (move) => {
console.log('收到游戏移动:', move);
});4. 高级特性
4.1 中间件
Socket.io 支持中间件,用于处理连接和消息:
4.1.1 服务器端中间件
import { Server } from 'socket.io';
const io = new Server(httpServer);
// 连接中间件
io.use((socket, next) => {
const token = socket.handshake.auth.token;
// 验证 token
if (!token) {
return next(new Error('未提供认证令牌'));
}
// 验证 token 有效性
// 这里只是示例,实际应用中需要实现真实的验证逻辑
if (token !== 'valid-token') {
return next(new Error('无效的认证令牌'));
}
// 将用户信息附加到 socket 对象
socket.user = { id: '1', name: 'John Doe' };
next();
});
// 消息中间件
io.on('connection', (socket) => {
console.log('用户连接:', socket.user.name);
// 可以在这里处理消息
});4.2 错误处理
Socket.io 提供了错误处理机制:
4.2.1 服务器端错误处理
io.on('connection', (socket) => {
// 监听错误事件
socket.on('error', (error) => {
console.error('Socket 错误:', error);
});
// 处理可能出错的操作
socket.on('risky operation', (data, callback) => {
try {
// 执行操作
const result = performRiskyOperation(data);
callback({ success: true, result });
} catch (error) {
console.error('操作错误:', error);
callback({ success: false, error: error.message });
}
});
});
function performRiskyOperation(data) {
// 模拟可能出错的操作
if (!data) {
throw new Error('数据不能为空');
}
return '操作成功';
}4.2.2 客户端错误处理
const socket = io('http://localhost:3000');
// 监听连接错误
socket.on('connect_error', (error) => {
console.error('连接错误:', error);
});
// 监听错误事件
socket.on('error', (error) => {
console.error('Socket 错误:', error);
});
// 发送可能出错的操作
socket.emit('risky operation', { data: 'test' }, (response) => {
if (response.success) {
console.log('操作成功:', response.result);
} else {
console.error('操作失败:', response.error);
}
});4.3 重连机制
Socket.io 内置了重连机制:
4.3.1 客户端重连配置
const socket = io('http://localhost:3000', {
// 重连配置
reconnection: true, // 启用重连
reconnectionAttempts: 5, // 重连尝试次数
reconnectionDelay: 1000, // 重连延迟(毫秒)
reconnectionDelayMax: 5000, // 最大重连延迟(毫秒)
timeout: 20000 // 连接超时(毫秒)
});
// 监听重连尝试事件
socket.on('reconnect_attempt', (attemptNumber) => {
console.log(`重连尝试 ${attemptNumber}`);
});
// 监听重连成功事件
socket.on('reconnect', (attemptNumber) => {
console.log(`重连成功,尝试次数: ${attemptNumber}`);
});
// 监听重连失败事件
socket.on('reconnect_failed', () => {
console.log('重连失败');
});4.4 状态管理
在 Socket.io 应用中管理状态:
4.4.1 服务器端状态管理
import { Server } from 'socket.io';
const io = new Server(httpServer);
// 存储在线用户
const onlineUsers = new Map();
io.on('connection', (socket) => {
// 添加用户到在线列表
onlineUsers.set(socket.id, {
id: socket.id,
name: socket.handshake.auth.username || `用户${socket.id.slice(-4)}`
});
// 广播在线用户列表
io.emit('online users', Array.from(onlineUsers.values()));
// 监听断开连接事件
socket.on('disconnect', () => {
// 从在线列表中移除用户
onlineUsers.delete(socket.id);
// 广播在线用户列表
io.emit('online users', Array.from(onlineUsers.values()));
});
});4.4.2 客户端状态管理
const socket = io('http://localhost:3000', {
auth: {
username: 'John Doe'
}
});
// 存储在线用户
let onlineUsers = [];
// 监听在线用户列表事件
socket.on('online users', (users) => {
onlineUsers = users;
console.log('在线用户:', onlineUsers);
// 更新 UI
updateOnlineUsersList(users);
});
function updateOnlineUsersList(users) {
// 更新 UI 显示在线用户列表
console.log('更新在线用户列表:', users);
}5. 最佳实践
5.1 项目结构
推荐的 Socket.io 项目结构:
├── src/
│ ├── server/ # 服务器端代码
│ │ ├── index.js # 服务器入口
│ │ ├── socket.js # Socket.io 配置
│ │ ├── middleware/ # 中间件
│ │ ├── handlers/ # 事件处理器
│ │ ├── rooms/ # 房间管理
│ │ └── utils/ # 工具函数
│ ├── client/ # 客户端代码
│ │ ├── index.js # 客户端入口
│ │ ├── socket.js # Socket.io 客户端配置
│ │ ├── components/ # 组件
│ │ └── utils/ # 工具函数
│ └── shared/ # 共享代码
│ └── events.js # 事件名称
├── package.json
└── .env5.2 性能优化
- 使用房间:将客户端分组,只向相关客户端发送消息
- 限制消息大小:避免发送过大的消息
- 批量发送消息:将多个小消息合并为一个大消息发送
- 使用二进制数据:对于大型数据,使用二进制格式
- 监控连接数:监控并限制同时连接的客户端数量
5.3 安全性
- 认证和授权:实现用户认证和权限控制
- 输入验证:验证所有客户端输入
- 速率限制:防止消息轰炸
- 加密通信:在生产环境中使用 HTTPS
- CORS 配置:正确配置 CORS 选项
6. 实用案例
6.1 构建实时聊天应用
6.1.1 服务器端实现
import { createServer } from 'node:http';
import { Server } from 'socket.io';
const httpServer = createServer();
const io = new Server(httpServer, {
cors: {
origin: '*',
methods: ['GET', 'POST']
}
});
// 存储在线用户
const onlineUsers = new Map();
io.on('connection', (socket) => {
// 添加用户到在线列表
const username = socket.handshake.auth.username || `用户${socket.id.slice(-4)}`;
onlineUsers.set(socket.id, {
id: socket.id,
name: username
});
console.log(`${username} 连接了`);
// 广播用户加入消息
io.emit('user joined', {
id: socket.id,
name: username
});
// 广播在线用户列表
io.emit('online users', Array.from(onlineUsers.values()));
// 监听聊天消息事件
socket.on('chat message', (message) => {
console.log('收到消息:', message);
// 广播消息
io.emit('chat message', {
id: socket.id,
name: username,
message: message,
timestamp: new Date().toISOString()
});
});
// 监听断开连接事件
socket.on('disconnect', () => {
console.log(`${username} 断开了连接`);
// 从在线列表中移除用户
onlineUsers.delete(socket.id);
// 广播用户离开消息
io.emit('user left', {
id: socket.id,
name: username
});
// 广播在线用户列表
io.emit('online users', Array.from(onlineUsers.values()));
});
});
httpServer.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});6.1.2 客户端实现
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>实时聊天应用</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
background-color: #f4f4f4;
}
.container {
max-width: 800px;
margin: 20px auto;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.header {
background-color: #333;
color: #fff;
padding: 15px;
text-align: center;
}
.chat-container {
display: flex;
height: 500px;
}
.users-list {
width: 200px;
background-color: #f9f9f9;
border-right: 1px solid #ddd;
padding: 15px;
overflow-y: auto;
}
.users-list h3 {
margin-bottom: 10px;
color: #333;
}
.users-list ul {
list-style: none;
}
.users-list li {
padding: 8px;
margin-bottom: 5px;
background-color: #e8f4f8;
border-radius: 4px;
}
.messages {
flex: 1;
padding: 15px;
overflow-y: auto;
}
.message {
margin-bottom: 15px;
padding: 10px;
border-radius: 8px;
max-width: 70%;
}
.message.own {
background-color: #e3f2fd;
align-self: flex-end;
margin-left: auto;
}
.message.other {
background-color: #f1f1f1;
align-self: flex-start;
}
.message-info {
font-size: 12px;
color: #666;
margin-bottom: 5px;
}
.message-text {
font-size: 14px;
}
.input-area {
display: flex;
padding: 15px;
border-top: 1px solid #ddd;
}
.input-area input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
margin-right: 10px;
}
.input-area button {
padding: 10px 20px;
background-color: #333;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
.input-area button:hover {
background-color: #555;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>实时聊天应用</h1>
</div>
<div class="chat-container">
<div class="users-list">
<h3>在线用户</h3>
<ul id="users-list"></ul>
</div>
<div class="messages" id="messages"></div>
</div>
<div class="input-area">
<input type="text" id="message-input" placeholder="输入消息...">
<button id="send-button">发送</button>
</div>
</div>
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
<script>
// 连接到服务器
const socket = io('http://localhost:3000', {
auth: {
username: prompt('请输入您的用户名:')
}
});
const messagesContainer = document.getElementById('messages');
const usersList = document.getElementById('users-list');
const messageInput = document.getElementById('message-input');
const sendButton = document.getElementById('send-button');
// 发送消息
function sendMessage() {
const message = messageInput.value.trim();
if (message) {
socket.emit('chat message', message);
messageInput.value = '';
}
}
// 监听发送按钮点击事件
sendButton.addEventListener('click', sendMessage);
// 监听回车键事件
messageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendMessage();
}
});
// 显示消息
function displayMessage(message) {
const messageElement = document.createElement('div');
messageElement.className = `message ${message.id === socket.id ? 'own' : 'other'}`;
const messageInfo = document.createElement('div');
messageInfo.className = 'message-info';
messageInfo.textContent = `${message.name} · ${new Date(message.timestamp).toLocaleTimeString()}`;
const messageText = document.createElement('div');
messageText.className = 'message-text';
messageText.textContent = message.message;
messageElement.appendChild(messageInfo);
messageElement.appendChild(messageText);
messagesContainer.appendChild(messageElement);
// 滚动到底部
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
// 更新在线用户列表
function updateUsersList(users) {
usersList.innerHTML = '';
users.forEach(user => {
const userElement = document.createElement('li');
userElement.textContent = user.name;
usersList.appendChild(userElement);
});
}
// 显示系统消息
function displaySystemMessage(message) {
const messageElement = document.createElement('div');
messageElement.style.cssText = `
text-align: center;
margin: 10px 0;
color: #666;
font-size: 12px;
font-style: italic;
`;
messageElement.textContent = message;
messagesContainer.appendChild(messageElement);
// 滚动到底部
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
// 监听聊天消息事件
socket.on('chat message', displayMessage);
// 监听用户加入事件
socket.on('user joined', (user) => {
displaySystemMessage(`${user.name} 加入了聊天`);
});
// 监听用户离开事件
socket.on('user left', (user) => {
displaySystemMessage(`${user.name} 离开了聊天`);
});
// 监听在线用户列表事件
socket.on('online users', updateUsersList);
// 监听连接错误事件
socket.on('connect_error', (error) => {
console.error('连接错误:', error);
displaySystemMessage('连接服务器失败,请刷新页面重试');
});
</script>
</body>
</html>6.2 构建多人在线游戏
6.2.1 服务器端实现
import { createServer } from 'node:http';
import { Server } from 'socket.io';
const httpServer = createServer();
const io = new Server(httpServer, {
cors: {
origin: '*',
methods: ['GET', 'POST']
}
});
// 游戏状态
const gameState = {
players: new Map(),
objects: []
};
io.on('connection', (socket) => {
console.log('玩家连接:', socket.id);
// 创建玩家
const player = {
id: socket.id,
x: Math.random() * 400 + 50,
y: Math.random() * 400 + 50,
color: `hsl(${Math.random() * 360}, 70%, 50%)`,
score: 0
};
// 添加玩家到游戏状态
gameState.players.set(socket.id, player);
// 发送初始游戏状态给新玩家
socket.emit('game state', {
players: Array.from(gameState.players.values()),
objects: gameState.objects
});
// 广播新玩家加入
socket.broadcast.emit('player joined', player);
// 监听玩家移动事件
socket.on('player move', (move) => {
const player = gameState.players.get(socket.id);
if (player) {
// 更新玩家位置
if (move.x !== undefined) player.x = move.x;
if (move.y !== undefined) player.y = move.y;
// 广播玩家移动
socket.broadcast.emit('player moved', {
id: socket.id,
x: player.x,
y: player.y
});
}
});
// 监听玩家得分事件
socket.on('player score', () => {
const player = gameState.players.get(socket.id);
if (player) {
// 增加玩家分数
player.score += 1;
// 广播玩家得分
io.emit('player scored', {
id: socket.id,
score: player.score
});
}
});
// 监听断开连接事件
socket.on('disconnect', () => {
console.log('玩家断开连接:', socket.id);
// 从游戏状态中移除玩家
gameState.players.delete(socket.id);
// 广播玩家离开
socket.broadcast.emit('player left', socket.id);
});
});
httpServer.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});6.2.2 客户端实现
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多人在线游戏</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
}
.game-container {
max-width: 800px;
margin: 20px auto;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.game-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
background-color: #333;
color: #fff;
}
.score {
font-size: 18px;
font-weight: bold;
}
.game-area {
position: relative;
width: 100%;
height: 500px;
background-color: #e8f4f8;
overflow: hidden;
}
.player {
position: absolute;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 12px;
font-weight: bold;
transition: all 0.1s ease;
}
.object {
position: absolute;
width: 20px;
height: 20px;
background-color: #ff6b6b;
border-radius: 50%;
}
.game-instructions {
padding: 15px;
background-color: #f9f9f9;
border-top: 1px solid #ddd;
}
.game-instructions h3 {
margin-bottom: 10px;
color: #333;
}
.game-instructions p {
margin-bottom: 5px;
font-size: 14px;
color: #666;
}
</style>
</head>
<body>
<div class="game-container">
<div class="game-header">
<h1>多人在线游戏</h1>
<div class="score">得分: <span id="score">0</span></div>
</div>
<div class="game-area" id="game-area"></div>
<div class="game-instructions">
<h3>游戏说明</h3>
<p>• 使用鼠标移动你的角色</p>
<p>• 收集红色圆形物体获得分数</p>
<p>• 查看其他玩家的位置和分数</p>
</div>
</div>
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
<script>
// 连接到服务器
const socket = io('http://localhost:3000');
const gameArea = document.getElementById('game-area');
const scoreElement = document.getElementById('score');
// 游戏状态
const gameState = {
players: new Map(),
objects: [],
myId: null,
score: 0
};
// 创建玩家元素
function createPlayerElement(player) {
const element = document.createElement('div');
element.id = `player-${player.id}`;
element.className = 'player';
element.style.left = `${player.x}px`;
element.style.top = `${player.y}px`;
element.style.backgroundColor = player.color;
element.textContent = player.id.slice(-2);
gameArea.appendChild(element);
}
// 更新玩家位置
function updatePlayerPosition(id, x, y) {
const element = document.getElementById(`player-${id}`);
if (element) {
element.style.left = `${x}px`;
element.style.top = `${y}px`;
}
}
// 移除玩家元素
function removePlayerElement(id) {
const element = document.getElementById(`player-${id}`);
if (element) {
element.remove();
}
}
// 创建物体元素
function createObjectElement(object, index) {
const element = document.createElement('div');
element.id = `object-${index}`;
element.className = 'object';
element.style.left = `${object.x}px`;
element.style.top = `${object.y}px`;
gameArea.appendChild(element);
}
// 移除物体元素
function removeObjectElement(index) {
const element = document.getElementById(`object-${index}`);
if (element) {
element.remove();
}
}
// 生成随机物体
function generateObjects() {
gameState.objects = [];
// 清空现有物体
document.querySelectorAll('.object').forEach(el => el.remove());
// 生成 10 个随机物体
for (let i = 0; i < 10; i++) {
const object = {
x: Math.random() * (gameArea.clientWidth - 20) + 10,
y: Math.random() * (gameArea.clientHeight - 20) + 10
};
gameState.objects.push(object);
createObjectElement(object, i);
}
}
// 检查碰撞
function checkCollisions() {
const myPlayer = gameState.players.get(gameState.myId);
if (!myPlayer) return;
for (let i = 0; i < gameState.objects.length; i++) {
const object = gameState.objects[i];
const distance = Math.sqrt(
Math.pow(myPlayer.x + 15 - (object.x + 10), 2) +
Math.pow(myPlayer.y + 15 - (object.y + 10), 2)
);
if (distance < 25) {
// 碰撞检测
gameState.objects.splice(i, 1);
removeObjectElement(i);
gameState.score += 1;
scoreElement.textContent = gameState.score;
// 通知服务器得分
socket.emit('player score');
break;
}
}
}
// 监听鼠标移动
gameArea.addEventListener('mousemove', (e) => {
const rect = gameArea.getBoundingClientRect();
const x = e.clientX - rect.left - 15;
const y = e.clientY - rect.top - 15;
// 限制玩家在游戏区域内
const clampedX = Math.max(0, Math.min(x, gameArea.clientWidth - 30));
const clampedY = Math.max(0, Math.min(y, gameArea.clientHeight - 30));
// 更新本地玩家位置
const myPlayer = gameState.players.get(gameState.myId);
if (myPlayer) {
myPlayer.x = clampedX;
myPlayer.y = clampedY;
updatePlayerPosition(gameState.myId, clampedX, clampedY);
// 发送移动信息到服务器
socket.emit('player move', { x: clampedX, y: clampedY });
// 检查碰撞
checkCollisions();
}
});
// 监听游戏状态事件
socket.on('game state', (state) => {
// 更新玩家
state.players.forEach(player => {
gameState.players.set(player.id, player);
if (!document.getElementById(`player-${player.id}`)) {
createPlayerElement(player);
}
});
// 更新物体
gameState.objects = state.objects;
document.querySelectorAll('.object').forEach(el => el.remove());
state.objects.forEach((object, index) => {
createObjectElement(object, index);
});
// 设置我的 ID
if (!gameState.myId) {
gameState.myId = socket.id;
}
});
// 监听玩家加入事件
socket.on('player joined', (player) => {
gameState.players.set(player.id, player);
createPlayerElement(player);
});
// 监听玩家移动事件
socket.on('player moved', (data) => {
const player = gameState.players.get(data.id);
if (player) {
player.x = data.x;
player.y = data.y;
updatePlayerPosition(data.id, data.x, data.y);
}
});
// 监听玩家离开事件
socket.on('player left', (id) => {
gameState.players.delete(id);
removePlayerElement(id);
});
// 监听玩家得分事件
socket.on('player scored', (data) => {
const player = gameState.players.get(data.id);
if (player) {
player.score = data.score;
// 如果是自己得分,更新分数显示
if (data.id === gameState.myId) {
gameState.score = data.score;
scoreElement.textContent = data.score;
}
}
});
// 初始化游戏
function initGame() {
generateObjects();
}
// 启动游戏
initGame();
</script>
</body>
</html>7. 总结
Socket.io 是一个功能强大、易用的实时通信库,为开发者提供了构建实时应用的完整解决方案。通过本教程的学习,你应该已经掌握了:
- Socket.io 的核心概念和基本原理
- 如何安装和配置 Socket.io
- 如何使用 Socket.io 的基本功能,如事件系统、房间和命名空间
- 如何使用 Socket.io 的高级特性,如中间件、错误处理和重连机制
- 最佳实践和实用案例
Socket.io 适用于各种实时应用场景,如聊天应用、多人游戏、实时协作工具、实时监控系统等。它提供了可靠的实时通信能力,同时具有良好的兼容性和扩展性。
无论是构建小型应用还是大型企业系统,Socket.io 都能满足你的实时通信需求。