首页
/ Socket.IO React Native 示例:移动 App 通过 socket.io-client 连接实时服务器的完整实战指南

Socket.IO React Native 示例:移动 App 通过 socket.io-client 连接实时服务器的完整实战指南

2026-09-03 17:58:10作者:曹令琨Iris

本文基于 socket.io 仓库中的 React Native 示例,完整讲解如何在 React Native 0.73 项目中接入 Socket.IO:从启动 Metro 与移动端 App、部署 Node 端 Socket.IO 服务器,到用 socket.io-client 建立连接、监听连接状态与传输层升级(polling 到 WebSocket 的切换)。读完本文,你将掌握一个可直接在 Android/iOS 模拟器上运行并验证实时通信链路的最小可用工程。

示例工程结构与版本依赖

该示例位于 examples/ReactNativeExample 目录,是一个用 @react-native-community/cli 引导生成的标准 React Native 工程,在此基础上叠加了 Socket.IO 客户端与服务器两部分代码:

package.json 可以看到关键版本约束,这也是本示例可运行的适用前提:

依赖 版本 说明
react-native 0.73.6 React Native 运行时
react 18.2.0 React 版本
socket.io-client ^4.7.5 客户端实时通信库
typescript 5.0.4 配合 @react-native/typescript-config 使用
node >=18 engines 字段声明的 Node 版本下限

服务端的 server/package.json 单独声明了 socket.io: ^4.7.5,并将 "type": "module" 设为 ESM 模式,因此服务器代码使用 import 语法。

Step 1:启动 Metro 服务器

示例的 README 要求先完成 React Native 官方环境搭建(至"创建新应用"步骤)。随后,在工程根目录启动 Metro——React Native 内置的 JavaScript 打包器:

# using npm
npm start

# OR using Yarn
yarn start

npm start 实际执行的是 package.json 中的 react-native start 脚本,Metro 会监听文件变化并为模拟器/设备提供 JS bundle。注意:此终端需要保持运行。

Step 2:启动 Android 或 iOS 应用

让 Metro 在独立终端中运行,另开一个终端,从工程根目录启动目标平台的 App:

Android

# using npm
npm run android

# OR using Yarn
yarn android

iOS

# using npm
npm run ios

# OR using Yarn
yarn ios

对应的 package.json 脚本分别为 react-native run-androidreact-native run-ios。配置正确时,App 应很快出现在 Android 模拟器或 iOS 模拟器中;当然,也可以在 Android Studio 或 Xcode 中直接运行。

Step 3:启动 Socket.IO 服务器

这是示例中最关键的一步,README 给出的操作是:

cd server

npm install

npm start

npm start 执行 node index.js,服务端代码非常精简(server/index.js):

import { Server } from 'socket.io';

const io = new Server();

io.on('connection', (socket) => {
  console.log(`connect: ${socket.id}`, socket.request.headers);

  socket.on('disconnect', () => {
    console.log(`disconnect: ${socket.id}`);
  });
});

io.listen(3000);

它完成三件事:

  1. new Server() 创建 Socket.IO 服务器实例;
  2. 监听 connection 事件,打印客户端 socket id 与请求头,客户端断开时打印 disconnect
  3. io.listen(3000) 独立监听 3000 端口(不挂载到 Express 等 HTTP 框架上)。

这样客户端每连入/断开一个连接,终端都会出现形如 connect: <socket.id> 的日志,可以作为服务端验证握手成功的依据。

Step 4:客户端连接——为什么必须使用局域网 IP

socket.js 是整个示例客户端的核心:

import { io } from 'socket.io-client';

export const socket = io('http://192.168.0.10:3000'); //	use the IP address of your machine

这里有两个值得注意的技术点:

  • 必须替换为开发机的 IP 地址(注释明确要求 "use the IP address of your machine")。因为 Android 模拟器与 iOS 模拟器运行在独立网络栈中,从模拟器访问 localhost:3000 指向的是模拟器自身而非宿主机,所以必须使用宿主机在局域网中的可达 IP(如示例中的 192.168.0.10)。
  • 单例导出socket 以模块顶层单例形式导出,整个 App 共享同一个 Socket.IO 连接与底层的 Engine.IO 引擎实例。

App.tsx 通过 import { socket } from './socket' 引入该单例并挂载事件监听(App.tsx):

const [isConnected, setIsConnected] = useState(false);
const [transport, setTransport] = useState('N/A');

useEffect(() => {
  if (socket.connected) {
    onConnect();
  }

  function onConnect() {
    setIsConnected(true);
    setTransport(socket.io.engine.transport.name);

    socket.io.engine.on('upgrade', (transport) => {
      setTransport(transport.name);
    });
  }

  function onDisconnect() {
    setIsConnected(false);
    setTransport('N/A');
  }

  socket.on('connect', onConnect);
  socket.on('disconnect', onDisconnect);

  return () => {
    socket.off('connect', onConnect);
    socket.off('disconnect', onDisconnect);
  };
}, []);

return (
  <View style={styles.container}>
    <Text>Status: { isConnected ? 'connected' : 'disconnected' }</Text>
    <Text>Transport: { transport }</Text>
  </View>
);

界面最终只显示两行信息:连接状态(connected / disconnected)和当前传输层名称(Transport: <name>)。这段代码是理解 Socket.IO 客户端连接模型的一个非常好的最小样本:

  1. 先判断再监听socket 是单例,useEffect 执行时连接可能已建立,所以先用 socket.connected 判断并立即调用 onConnect(),避免错过 connect 事件;
  2. 连接状态由 connect / disconnect 事件驱动connect 置为已连接,disconnect 复位为未连接并把传输层清为 N/A
  3. 传输层通过 socket.io.engine.transport.name 读取socket.ioManager,其 engine 是 Engine.IO 的 Socket 实例,transport 即当前生效的底层传输;
  4. 监听 upgrade 事件感知传输层切换:当 Engine.IO 从长轮询升级到 WebSocket 时,会触发 upgrade 事件并携带新的 transport 对象,示例据此实时更新界面显示的传输层名称;
  5. 组件卸载时解绑useEffect 的清理函数用 socket.off 移除两个监听器,防止 React 组件重挂载造成监听器泄漏。

传输层升级机制的源码佐证

upgrade 事件并非 Socket.IO 层的事件,而是来自底层的 Engine.IO 客户端。从 engine.io-client 的 Socket 实现 可以看到:

  • upgrade?: boolean 选项控制"客户端是否应尝试将传输层从当前类型升级到 WebSocket";
  • Engine.IO 的 Socket 定义了 upgrade: (transport: Transport) => void 事件,并有对应的 upgradeError 事件;
  • upgrades: string[] 表示可升级到的传输层列表,默认连接流程会先建立 polling 连接,再按升级路径尝试 WebSocket(见 socket.ts 中 upgrade 相关注释 与事件定义)。

因此在示例 UI 中,你通常会看到 Transport 先显示 polling(XHR 长轮询),随后在升级到 WebSocket 后刷新为 websocket——这正是 App.tsx 里监听 upgrade 事件的意义。

修改 App 并热重载(Step 4)

README 的最后一步是修改代码并观察热重载:

  1. 用文本编辑器打开 App.tsx 修改内容;
  2. Android:按两次 R 键,或从开发者菜单选择 Reload(Windows/Linux 为 Ctrl + M,macOS 为 Cmd + M);
  3. iOS:在模拟器中按 Cmd + R 重新加载应用。

由于 Metro 在 Step 1 中一直在运行,保存 App.tsx 后重新加载即可看到 Socket.IO 连接信息与界面变化。此外 App.test.tsx 提供了一个最简的 Jest 渲染测试(renderer.create(<App />)),可用 npm test 执行,验证组件能被渲染。

运行前提与实操注意事项

  • Node 版本:客户端工程 package.json 声明 "node": ">=18",请确保 Node 18+;
  • 服务器端口server/index.js 固定监听 3000 端口,若被占用需自行调整并同步修改 socket.js 中的 URL;
  • IP 必须可达socket.js 中的 192.168.0.10 只是占位示例,必须替换为运行服务器的开发机局域网 IP,否则模拟器无法建立连接(这是移动端调试与浏览器调试最常见的差异点);
  • 两端版本对齐:客户端 socket.io-client ^4.7.5 与服务端 socket.io ^4.7.5 均为 4.x 系列,遵循仓库 docs/socket.io-protocol 中描述的 v5 协议世代(Socket.IO 4.x 客户端/服务端协议),跨大版本混用需自行验证兼容性。

小结

这个示例的价值在于用最少的代码串通了"React Native 端 + Node 端"的完整实时通信链路:server/index.js 展示了 Socket.IO 服务器的最小独立部署方式,socket.js 展示了移动端连接单例的建立方式与局域网 IP 的使用要求,App.tsx 则演示了如何读取连接状态、当前传输层,以及如何通过 Engine.IO 的 upgrade 事件观察"轮询升级到 WebSocket"这一 Socket.IO 的默认传输层策略。按 README 的四个步骤(Metro → 移动端 App → Socket.IO 服务器 → 修改与热重载)操作,即可在模拟器上得到一个可验证收发链路的工作示例。

登录后查看全文
热门项目推荐
相关项目推荐