Socket.IO 基础使用 #

安装 Socket.IO #

服务端安装 #

bash
npm install socket.io

客户端安装 #

bash
npm install socket.io-client

CDN 引入 #

html
<script src="/socket.io/socket.io.js"></script>

或使用 CDN:

html
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>

创建服务端 #

基础服务端 #

javascript
const { Server } = require('socket.io');

const io = new Server({
  cors: {
    origin: 'http://localhost:3000',
    methods: ['GET', 'POST']
  }
});

io.on('connection', (socket) => {
  console.log('用户连接:', socket.id);
  
  socket.on('disconnect', () => {
    console.log('用户断开:', socket.id);
  });
});

io.listen(3001);
console.log('Socket.IO 服务运行在 3001 端口');

与 Express 集成 #

javascript
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');

const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
  cors: {
    origin: 'http://localhost:3000',
    methods: ['GET', 'POST']
  }
});

app.get('/', (req, res) => {
  res.send('Hello World');
});

io.on('connection', (socket) => {
  console.log('用户连接:', socket.id);
});

httpServer.listen(3001, () => {
  console.log('服务器运行在 http://localhost:3001');
});

与 Koa 集成 #

javascript
const Koa = require('koa');
const { createServer } = require('http');
const { Server } = require('socket.io');

const app = new Koa();
const httpServer = createServer(app.callback());
const io = new Server(httpServer);

io.on('connection', (socket) => {
  console.log('用户连接:', socket.id);
});

httpServer.listen(3001);

TypeScript 支持 #

typescript
import express from 'express';
import { createServer } from 'http';
import { Server, Socket } from 'socket.io';

const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer);

interface ClientToServerEvents {
  chatMessage: (msg: string) => void;
}

interface ServerToClientEvents {
  chatMessage: (msg: string) => void;
}

interface InterServerEvents {
  ping: () => void;
}

interface SocketData {
  name: string;
  age: number;
}

const io = new Server<
  ClientToServerEvents,
  ServerToClientEvents,
  InterServerEvents,
  SocketData
>(httpServer);

io.on('connection', (socket: Socket) => {
  console.log('用户连接:', socket.id);
  socket.data.name = 'John';
});

创建客户端 #

浏览器客户端 #

html
<!DOCTYPE html>
<html>
<head>
  <title>Socket.IO 客户端</title>
</head>
<body>
  <script src="/socket.io/socket.io.js"></script>
  <script>
    const socket = io('http://localhost:3001');
    
    socket.on('connect', () => {
      console.log('连接成功:', socket.id);
    });
    
    socket.on('disconnect', () => {
      console.log('连接断开');
    });
  </script>
</body>
</html>

Node.js 客户端 #

javascript
const { io } = require('socket.io-client');

const socket = io('http://localhost:3001');

socket.on('connect', () => {
  console.log('连接成功:', socket.id);
});

socket.on('disconnect', () => {
  console.log('连接断开');
});

React 客户端 #

jsx
import { useEffect, useState } from 'react';
import { io } from 'socket.io-client';

function App() {
  const [socket, setSocket] = useState(null);
  const [isConnected, setIsConnected] = useState(false);

  useEffect(() => {
    const newSocket = io('http://localhost:3001');
    
    newSocket.on('connect', () => {
      setIsConnected(true);
      console.log('连接成功:', newSocket.id);
    });
    
    newSocket.on('disconnect', () => {
      setIsConnected(false);
      console.log('连接断开');
    });
    
    setSocket(newSocket);
    
    return () => {
      newSocket.close();
    };
  }, []);

  return (
    <div>
      <p>连接状态: {isConnected ? '已连接' : '未连接'}</p>
    </div>
  );
}

export default App;

Vue 客户端 #

vue
<template>
  <div>
    <p>连接状态: {{ isConnected ? '已连接' : '未连接' }}</p>
  </div>
</template>

<script>
import { io } from 'socket.io-client';

export default {
  data() {
    return {
      socket: null,
      isConnected: false
    };
  },
  mounted() {
    this.socket = io('http://localhost:3001');
    
    this.socket.on('connect', () => {
      this.isConnected = true;
      console.log('连接成功:', this.socket.id);
    });
    
    this.socket.on('disconnect', () => {
      this.isConnected = false;
    });
  },
  beforeUnmount() {
    if (this.socket) {
      this.socket.disconnect();
    }
  }
};
</script>

连接流程 #

连接建立过程 #

text
┌─────────────────────────────────────────────────────────────┐
│                    Socket.IO 连接流程                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌─────────┐                ┌─────────┐                   │
│   │  客户端  │                │  服务端  │                   │
│   └────┬────┘                └────┬────┘                   │
│        │                          │                         │
│        │ 1. HTTP 握手请求         │                         │
│        │─────────────────────────>│                         │
│        │                          │                         │
│        │ 2. 返回 Session ID       │                         │
│        │<─────────────────────────│                         │
│        │                          │                         │
│        │ 3. WebSocket 升级请求    │                         │
│        │─────────────────────────>│                         │
│        │                          │                         │
│        │ 4. WebSocket 连接建立    │                         │
│        │<─────────────────────────│                         │
│        │                          │                         │
│        │ 5. 心跳包(Ping/Pong)   │                         │
│        │<────────────────────────>│                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

连接选项配置 #

javascript
const socket = io('http://localhost:3001', {
  path: '/socket.io',
  transports: ['websocket', 'polling'],
  reconnection: true,
  reconnectionAttempts: Infinity,
  reconnectionDelay: 1000,
  reconnectionDelayMax: 5000,
  timeout: 20000,
  autoConnect: true,
  query: {
    token: 'your-auth-token'
  },
  auth: {
    token: 'your-auth-token'
  },
  extraHeaders: {
    'X-Custom-Header': 'value'
  }
});

连接选项详解 #

text
┌─────────────────────────────────────────────────────────────┐
│                    连接选项详解                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  path              Socket.IO 路径                           │
│  ─────────────────────────────────────────────────────────  │
│  默认: /socket.io                                           │
│  示例: path: '/custom-path'                                 │
│                                                             │
│  transports        传输方式                                 │
│  ─────────────────────────────────────────────────────────  │
│  默认: ['polling', 'websocket']                             │
│  示例: transports: ['websocket']                            │
│                                                             │
│  reconnection      是否自动重连                             │
│  ─────────────────────────────────────────────────────────  │
│  默认: true                                                 │
│  示例: reconnection: false                                  │
│                                                             │
│  reconnectionAttempts 重连次数                              │
│  ─────────────────────────────────────────────────────────  │
│  默认: Infinity                                             │
│  示例: reconnectionAttempts: 10                             │
│                                                             │
│  reconnectionDelay  重连延迟                                │
│  ─────────────────────────────────────────────────────────  │
│  默认: 1000 (毫秒)                                          │
│  示例: reconnectionDelay: 2000                              │
│                                                             │
│  reconnectionDelayMax 最大重连延迟                          │
│  ─────────────────────────────────────────────────────────  │
│  默认: 5000 (毫秒)                                          │
│  示例: reconnectionDelayMax: 10000                          │
│                                                             │
│  timeout           连接超时                                 │
│  ─────────────────────────────────────────────────────────  │
│  默认: 20000 (毫秒)                                         │
│  示例: timeout: 10000                                       │
│                                                             │
│  autoConnect       是否自动连接                             │
│  ─────────────────────────────────────────────────────────  │
│  默认: true                                                 │
│  示例: autoConnect: false                                   │
│                                                             │
│  query             URL 查询参数                             │
│  ─────────────────────────────────────────────────────────  │
│  示例: query: { token: 'xxx' }                              │
│                                                             │
│  auth              认证信息                                 │
│  ─────────────────────────────────────────────────────────  │
│  示例: auth: { token: 'xxx' }                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

连接事件 #

客户端连接事件 #

javascript
const socket = io('http://localhost:3001');

socket.on('connect', () => {
  console.log('连接成功');
  console.log('Socket ID:', socket.id);
});

socket.on('connect_error', (error) => {
  console.error('连接错误:', error.message);
});

socket.on('disconnect', (reason) => {
  console.log('连接断开:', reason);
});

socket.on('reconnect', (attemptNumber) => {
  console.log('重连成功:', attemptNumber);
});

socket.on('reconnect_attempt', (attemptNumber) => {
  console.log('尝试重连:', attemptNumber);
});

socket.on('reconnect_error', (error) => {
  console.error('重连错误:', error);
});

socket.on('reconnect_failed', () => {
  console.log('重连失败');
});

断开原因 #

text
┌─────────────────────────────────────────────────────────────┐
│                    断开原因说明                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  io server disconnect                                       │
│  ─────────────────────────────────────────────────────────  │
│  服务端主动断开连接                                         │
│  客户端不会自动重连                                         │
│                                                             │
│  io client disconnect                                       │
│  ─────────────────────────────────────────────────────────  │
│  客户端主动断开连接                                         │
│  客户端不会自动重连                                         │
│                                                             │
│  ping timeout                                               │
│  ─────────────────────────────────────────────────────────  │
│  心跳超时                                                   │
│  客户端会自动重连                                           │
│                                                             │
│  transport close                                            │
│  ─────────────────────────────────────────────────────────  │
│  传输层关闭                                                 │
│  客户端会自动重连                                           │
│                                                             │
│  transport error                                            │
│  ─────────────────────────────────────────────────────────  │
│  传输层错误                                                 │
│  客户端会自动重连                                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

服务端连接事件 #

javascript
io.on('connection', (socket) => {
  console.log('用户连接:', socket.id);
  
  socket.on('disconnect', (reason) => {
    console.log('用户断开:', socket.id, '原因:', reason);
  });
  
  socket.on('disconnecting', (reason) => {
    console.log('用户正在断开:', socket.id);
    console.log('当前房间:', socket.rooms);
  });
});

手动连接与断开 #

手动连接 #

javascript
const socket = io('http://localhost:3001', {
  autoConnect: false
});

document.getElementById('connect').addEventListener('click', () => {
  socket.connect();
});

手动断开 #

javascript
socket.disconnect();

socket.on('disconnect', () => {
  console.log('已断开连接');
});

重连 #

javascript
socket.disconnect();

setTimeout(() => {
  socket.connect();
}, 3000);

发送和接收消息 #

发送消息 #

javascript
socket.emit('chat message', 'Hello World');

socket.emit('chat message', {
  user: 'John',
  message: 'Hello World',
  timestamp: Date.now()
});

socket.emit('private message', {
  to: 'user-123',
  content: 'Hello!'
});

接收消息 #

javascript
socket.on('chat message', (msg) => {
  console.log('收到消息:', msg);
});

socket.on('chat message', (data) => {
  console.log(`${data.user}: ${data.message}`);
});

socket.onAny((eventName, ...args) => {
  console.log('收到事件:', eventName, args);
});

消息确认 #

javascript
socket.emit('chat message', 'Hello', (acknowledgement) => {
  console.log('消息已确认:', acknowledgement);
});

socket.on('chat message', (msg, callback) => {
  console.log('收到消息:', msg);
  callback('消息已收到');
});

服务端广播 #

广播给所有客户端 #

javascript
io.emit('news', { message: 'Hello everyone!' });

广播给除发送者外的所有客户端 #

javascript
socket.broadcast.emit('news', { message: 'Hello others!' });

广播给特定房间 #

javascript
io.to('room-1').emit('news', { message: 'Hello room 1!' });

socket.to('room-1').emit('news', { message: 'Hello room 1!' });

广播示例 #

text
┌─────────────────────────────────────────────────────────────┐
│                    广播方式对比                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  io.emit()                                                   │
│  ─────────────────────────────────────────────────────────  │
│  发送给所有连接的客户端                                     │
│                                                             │
│  socket.broadcast.emit()                                     │
│  ─────────────────────────────────────────────────────────  │
│  发送给除当前 socket 外的所有客户端                         │
│                                                             │
│  io.to('room').emit()                                        │
│  ─────────────────────────────────────────────────────────  │
│  发送给特定房间的所有客户端                                 │
│                                                             │
│  socket.to('room').emit()                                    │
│  ─────────────────────────────────────────────────────────  │
│  发送给特定房间除当前 socket 外的客户端                     │
│                                                             │
│  socket.emit()                                               │
│  ─────────────────────────────────────────────────────────  │
│  只发送给当前 socket                                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

完整示例:聊天室 #

服务端代码 #

javascript
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');

const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
  cors: {
    origin: '*'
  }
});

io.on('connection', (socket) => {
  console.log('用户连接:', socket.id);
  
  socket.on('join', (username) => {
    socket.username = username;
    socket.broadcast.emit('user joined', {
      username,
      message: `${username} 加入了聊天室`
    });
  });
  
  socket.on('chat message', (msg) => {
    io.emit('chat message', {
      username: socket.username,
      message: msg,
      timestamp: Date.now()
    });
  });
  
  socket.on('typing', () => {
    socket.broadcast.emit('typing', socket.username);
  });
  
  socket.on('disconnect', () => {
    if (socket.username) {
      socket.broadcast.emit('user left', {
        username: socket.username,
        message: `${socket.username} 离开了聊天室`
      });
    }
  });
});

httpServer.listen(3001, () => {
  console.log('聊天室服务器运行在 http://localhost:3001');
});

客户端代码 #

html
<!DOCTYPE html>
<html>
<head>
  <title>聊天室</title>
  <style>
    #messages { height: 300px; overflow-y: scroll; border: 1px solid #ccc; padding: 10px; }
    #typing { color: #666; font-style: italic; }
  </style>
</head>
<body>
  <div id="messages"></div>
  <div id="typing"></div>
  <input type="text" id="username" placeholder="用户名">
  <button onclick="join()">加入</button>
  <br>
  <input type="text" id="messageInput" placeholder="消息" disabled>
  <button onclick="sendMessage()" id="sendBtn" disabled>发送</button>

  <script src="/socket.io/socket.io.js"></script>
  <script>
    const socket = io('http://localhost:3001');
    const messages = document.getElementById('messages');
    const typing = document.getElementById('typing');
    const messageInput = document.getElementById('messageInput');
    const sendBtn = document.getElementById('sendBtn');
    
    let typingTimeout;
    
    socket.on('connect', () => {
      console.log('连接成功');
    });
    
    socket.on('chat message', (data) => {
      const div = document.createElement('div');
      div.textContent = `${data.username}: ${data.message}`;
      messages.appendChild(div);
      messages.scrollTop = messages.scrollHeight;
    });
    
    socket.on('user joined', (data) => {
      const div = document.createElement('div');
      div.style.color = 'green';
      div.textContent = data.message;
      messages.appendChild(div);
    });
    
    socket.on('user left', (data) => {
      const div = document.createElement('div');
      div.style.color = 'red';
      div.textContent = data.message;
      messages.appendChild(div);
    });
    
    socket.on('typing', (username) => {
      typing.textContent = `${username} 正在输入...`;
      clearTimeout(typingTimeout);
      typingTimeout = setTimeout(() => {
        typing.textContent = '';
      }, 1000);
    });
    
    function join() {
      const username = document.getElementById('username').value;
      if (username) {
        socket.emit('join', username);
        messageInput.disabled = false;
        sendBtn.disabled = false;
        document.getElementById('username').disabled = true;
      }
    }
    
    function sendMessage() {
      const message = messageInput.value;
      if (message) {
        socket.emit('chat message', message);
        messageInput.value = '';
      }
    }
    
    messageInput.addEventListener('input', () => {
      socket.emit('typing');
    });
    
    messageInput.addEventListener('keypress', (e) => {
      if (e.key === 'Enter') {
        sendMessage();
      }
    });
  </script>
</body>
</html>

服务端配置选项 #

javascript
const io = new Server(httpServer, {
  cors: {
    origin: ['http://localhost:3000', 'https://example.com'],
    methods: ['GET', 'POST'],
    allowedHeaders: ['my-custom-header'],
    credentials: true
  },
  
  pingInterval: 25000,
  pingTimeout: 20000,
  
  upgradeTimeout: 10000,
  maxHttpBufferSize: 1e6,
  
  transports: ['polling', 'websocket'],
  allowEIO3: true,
  
  cookie: {
    name: 'io',
    httpOnly: true,
    sameSite: 'strict'
  }
});

配置选项详解 #

text
┌─────────────────────────────────────────────────────────────┐
│                    服务端配置选项                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  cors              跨域配置                                 │
│  ─────────────────────────────────────────────────────────  │
│  cors: {                                                     │
│    origin: 'http://localhost:3000',                         │
│    methods: ['GET', 'POST'],                                │
│    credentials: true                                        │
│  }                                                          │
│                                                             │
│  pingInterval      心跳间隔                                 │
│  ─────────────────────────────────────────────────────────  │
│  默认: 25000 (毫秒)                                         │
│  服务端发送 ping 的间隔                                     │
│                                                             │
│  pingTimeout       心跳超时                                 │
│  ─────────────────────────────────────────────────────────  │
│  默认: 20000 (毫秒)                                         │
│  等待 pong 响应的超时时间                                   │
│                                                             │
│  upgradeTimeout    升级超时                                 │
│  ─────────────────────────────────────────────────────────  │
│  默认: 10000 (毫秒)                                         │
│  HTTP 长轮询升级到 WebSocket 的超时                         │
│                                                             │
│  maxHttpBufferSize 最大消息大小                             │
│  ─────────────────────────────────────────────────────────  │
│  默认: 1e6 (1MB)                                            │
│  单条消息的最大字节数                                       │
│                                                             │
│  transports        传输方式                                 │
│  ─────────────────────────────────────────────────────────  │
│  默认: ['polling', 'websocket']                             │
│  允许的传输方式                                             │
│                                                             │
│  allowEIO3         兼容 v3 客户端                           │
│  ─────────────────────────────────────────────────────────  │
│  默认: false                                                │
│  是否允许 Socket.IO v3 客户端连接                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

调试技巧 #

启用调试日志 #

javascript
const io = new Server(httpServer, {
  cors: { origin: '*' }
});

io.engine.on('connection_error', (err) => {
  console.log('连接错误:', err.req);
  console.log('错误码:', err.code);
  console.log('错误消息:', err.message);
  console.log('错误上下文:', err.context);
});

客户端调试 #

javascript
localStorage.debug = 'socket.io-client:*';

const socket = io('http://localhost:3001');

查看连接信息 #

javascript
io.on('connection', (socket) => {
  console.log('连接信息:');
  console.log('- ID:', socket.id);
  console.log('- Handshake:', socket.handshake);
  console.log('- Rooms:', socket.rooms);
  console.log('- Transport:', socket.conn.transport.name);
});

下一步 #

现在你已经掌握了 Socket.IO 的基础使用,接下来学习 事件系统,深入了解事件通信的更多细节!

最后更新:2026-03-29