FSA全栈行动 FSA全栈行动
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
  • 内功心法

    • 《深入浅出设计模式Java版》笔记
前端
后端
  • 产品
  • 代理
  • 提效
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr

公众号:FSA全栈行动

记录学习过程中的知识
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
  • 内功心法

    • 《深入浅出设计模式Java版》笔记
前端
后端
  • 产品
  • 代理
  • 提效
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr
  • AndroidUI

  • Android第三方SDK

  • Android混淆

  • Android仓库

  • Android新闻

  • Android系统开发

  • Android源码

  • Android注解AOP

  • Android脚本

  • AndroidTv开发

  • AndroidNDK

  • Android音视频

  • Android热修复

  • Android性能优化

  • Android云游戏

  • Android插件化

  • iOSUI

  • iOS工具

  • iOS底层原理与应用

  • iOS组件化

  • iOS音视频

  • iOS疑难杂症

  • iOS之Swift

  • iOS之RxSwift

  • iOS开源项目

  • iOS逆向

  • Flutter开发

    • Dart - 抽象类的实例化
    • Flutter - 打印好用的Debug日志
    • Flutter - 混合开发
    • Flutter - 解决混合开发iOS脚本打包遇到的问题
    • Flutter - 低版本在iOS14上遇到的问题与解决方案
    • Flutter - 解决原生弹窗的触摸事件被Flutter响应的问题
    • Flutter - 实现列表上下拉切换header
    • Flutter - 获取ListView当前正在显示的Widget信息
    • Flutter - 列表滚动定位超强辅助库,墙裂推荐!🔥
    • Flutter - 快速实现聊天会话列表的效果,完美💯
    • Flutter - 聊天输入框更新文本时的必备优化点🔖
    • Flutter - 我给官方提PR,解决run命令卡住问题 😃
    • Flutter - 探索run命令到底做了什么 🤔
    • Flutter - 引擎调试(iOS篇)🛠
    • Flutter - 引擎调试bug到提交PR实战 🐞
    • Flutter - 船新升级😱支持观察第三方构建的滚动视图💪
    • Flutter - 瀑布流交替播放视频 🎞
    • Flutter - IM保持消息位置大升级(支持ChatGPT生成式消息) 🤖
    • Flutter - 滚动视图中的表单防遮挡 🗒
    • Flutter - 秒杀1/2曝光统计 📊
    • 一天内加入 Flutter 和 FlutterCandies 两大组织是什么体验 🧐
    • Flutter - 如何快速搓一个微信通讯录列表(azlist) 📓
    • Flutter - 混编项目集成Shorebird热更新🐦(安卓篇)
    • Flutter - 混编项目集成Shorebird热更新🐦(iOS篇)
    • Flutter - 解决返回原生页面时dispose方法未被触发的问题 🐞
    • Flutter - 升级3.19之后页面多次rebuild?🤨
    • Flutter - 热更新 Shorebird 1.0 正式版来了 🐦
    • Flutter - 使用Pigeon实现视频缓存插件 🐌
    • Flutter - 轻松搞定屏幕旋转功能 😎
    • Flutter - 解决Connection closed before full header was received
    • Flutter - 实现聊天键盘与功能面板的丝滑切换 🍻
    • Flutter - 支持观察NestedScrollView,兼容性更强 😈
    • Flutter - 聊天键盘与面板丝滑切换的强势升级 🍻
    • Flutter - 升级到3.24后页面还会多次rebuild吗?🧐
    • Flutter - 轻松实现PageView卡片偏移效果
    • Flutter - 轻松搞定炫酷视差(Parallax)效果
    • Flutter - 危!3.24版本苹果审核被拒!
    • Flutter - 子部件任意位置观察滚动数据
    • Flutter - iOS编译加速
    • Flutter - Xcode16 还原编译速度
    • Flutter - GetX Helper 助你规范应用 tag
    • Flutter - GetX Helper 如何应用于旧页面
    • Flutter - 聊天面板库动画生硬?这次让你丝滑个够
    • Flutter - 使用本地 DevTools 验证 SVG 加载优化
    • Flutter - 详情页 TabBar 与模块联动?秒了!
    • Flutter - 详情页初始锚点与优化
    • Flutter - Melos Pub workspaces 实践
    • Flutter webview 崩溃率上升怎么办?我的分析与解决方案
    • Flutter 多仓库本地 Monorepo 方案与体验优化
    • Flutter 拖拉对比组件,换装图片前后对比必备
    • Flutter 3.44 插件内置 Kotlin (KGP) 双向兼容适配指南
    • Flutter 无障碍开发实战:玩转 Semantics 解决视障用户使用痛点
    • 玩转 WebSocket:从原理到 Flutter 实战
      • 一、为什么需要 WebSocket
      • 二、原理与生命周期
        • 1、握手过程 (Handshake)
        • 2、连接生命周期
      • 三、实战中的稳定性保障
        • 1、心跳机制 (Heartbeat)
        • 2、指数退避重连 (Exponential Backoff)
      • 四、Flutter 中的落地实践
      • 五、面试锦囊
  • 移动端
  • Flutter开发
GitLqr
2026-07-22
目录

玩转 WebSocket:从原理到 Flutter 实战

欢迎关注微信公众号:[FSA全栈行动 👋]

# 一、为什么需要 WebSocket

平时我们开发 App,绝大多数场景都在用 REST (HTTP) 协议。REST 的逻辑很简单:客户端发一个请求,服务器回一个响应,然后连接就断了。

但如果你要做的是聊天应用、实时股价行情,或者是像滴滴那样在地图上实时跟踪司机的轨迹,REST 就显得有点“笨”了。因为在这些场景下,服务器需要根据数据的变化,主动把消息“推”给 App。

如果强行用 REST 来实现,你只能让客户端不停地去问服务器:“有新消息吗?”、“有新消息吗?”……这种操作叫 Polling (轮询),它不仅浪费流量,还非常耗电。这时候,WebSocket 就派上用场了。

我们可以通过下面这个表格,直观对比一下两者的核心区别:

维度

REST (HTTP)

WebSocket

通信模式

一问一答 (Request-Response)

双向通信 (Full-Duplex)

连接状态

请求结束即关闭 (Short-lived)

持续连接 (Persistent)

实时性

较低 (依赖轮询间隔)

极高 (服务器可即时推送)

适用场景

获取普通数据、提交表单

聊天、实时行情、多人协作

# 二、原理与生命周期

很多同学可能会问,WebSocket 是一个新的协议吗?其实不是,它最初是靠 HTTP “升级”过来的。

# 1、握手过程 (Handshake)

WebSocket 的建立过程其实就是一次普通的 HTTP 请求。

  1. 客户端发送一个带有特殊请求头的 HTTP GET 请求,头信息里包含 Upgrade: websocket。

  2. 如果服务器支持,会返回一个 101 Switching Protocols 状态码。

  3. 从这一刻起,这个连接就“变身”成了 WebSocket,双方可以随时随地互发消息,再也不用先问后答了。

这里有个细节:因为它是从 HTTP 升级来的,所以它直接复用 80 (HTTP) 或 443 (HTTPS) 端口。不过在生产环境中,我们务必使用 wss://(基于 TLS 的加密版本),否则数据在传输过程中很容易被监听。

# 2、连接生命周期

理解 WebSocket 的状态转换对我们处理异常非常重要。它主要经历以下几个阶段:

  • connecting:正在尝试建立连接,正在进行握手。

  • open:连接成功,现在双方可以自由收发消息了。

  • message:收到消息(或者发送消息)。这是最频繁的状态。

  • close / error:连接结束。如果是正常关闭,会带上 1000 状态码;如果是网络突然断了,通常会报 1006 (Abnormal Closure)。

记住: 如果遇到 1006 错误,通常意味着连接异常断开,这时候我们就需要处理重连逻辑了。

# 三、实战中的稳定性保障

在真实的网络环境下,连接掉线是常态。为了让 WebSocket 足够稳,我们需要做两件事:心跳检测 和 指数退避重连。

# 1、心跳机制 (Heartbeat)

有时候,虽然你的 WebSocket 对象显示还在 open 状态,但实际上网络可能已经“死”了(比如手机进电梯了)。为了防止这种“假死”现象,我们会定期发送一个很小的包(通常叫 ping),如果对方在规定时间内没回(pong),我们就认为连接已断,需要重连。

# 2、指数退避重连 (Exponential Backoff)

如果网络真的断了,我们肯定要尝试重连。但千万不要用 while(true) 那种死循环疯狂重连,否则如果服务器宕机了,你的 App 会像轰炸一样去请求服务器,反而增加了负担。

比较优雅的做法是“指数退避”:第一次重连等 1 秒,第二次等 2 秒,第三次等 4 秒……以此类推,直到一个上限(比如 32 秒)。这样既能保证在网络恢复时尽快连上,又不会过度消耗资源。

# 四、Flutter 中的落地实践

在 Flutter 中,我们通常使用 web_socket_channel 这个包。它把 WebSocket 封装成了 Stream,这跟 Dart 的异步编程范式结合得非常完美。

下面我写了一个简单的 SocketService,它集成了监听、发送、心跳和带退避机制的重连逻辑:

import 'dart:async';
import 'package:web_socket_channel/web_socket_channel.dart';

class SocketService {
  WebSocketChannel? _channel;
  StreamSubscription? _sub;
  Timer? _heartbeat;
  int _retryDelay = 1; // 起始重连间隔(秒)

  void connect() {
    // 使用 wss:// 保证安全
    _channel = WebSocketChannel.connect(Uri.parse('wss://example.com/socket'));

    _sub = _channel!.stream.listen(
      _onMessage,
      onError: (_) => _reconnect(),
      onDone: _reconnect, // 当连接被服务器关闭时触发
    );

    _startHeartbeat();
    _retryDelay = 1; // 连接成功后,重置重连间隔
  }

  void _onMessage(dynamic data) {
    if (data == 'pong') return; // 忽略心跳回包
    print('收到消息: $data');
  }

  void send(String msg) => _channel?.sink.add(msg);

  void _startHeartbeat() {
    // 每 15 秒发一次心跳
    _heartbeat = Timer.periodic(const Duration(seconds: 15), (_) {
      _channel?.sink.add('ping');
    });
  }

  void _reconnect() {
    _cleanUp();
    // 延时重连,实现指数退避
    Future.delayed(Duration(seconds: _retryDelay), connect);
    _retryDelay = (_retryDelay * 2).clamp(1, 32);
  }

  void _cleanUp() {
    _heartbeat?.cancel();
    _sub?.cancel();
    _channel?.sink.close();
  }

  // 重点:一定要在组件销毁时调用,防止内存泄漏
  void dispose() => _cleanUp();
}

这里有几个一定要注意的细节:

  1. 防止内存泄漏:在 Flutter 中,一定要在 State 的 dispose() 方法里调用 socketService.dispose()。否则,哪怕页面关了,Stream 还在跑,后台还在收消息,这会导致严重的资源浪费。

  2. 监听 onDone:一定要处理 onDone 回调,因为很多时候连接是由于服务端主动断开导致的。

  3. 处理单订阅 vs 多订阅:WebSocketChannel 的 stream 是单订阅的。如果你在多个地方 listen 同一个 stream,记得使用 stream.asBroadcastStream()。

# 五、面试锦囊

如果面试官问你:“请介绍一下 WebSocket。”

你可以这样有逻辑地回答:

“WebSocket 是一种基于 TCP 的全双工通信协议,它能实现客户端和服务器之间的实时双向通信。

它不同于传统的 HTTP 协议,HTTP 是‘一问一答’的模式,而 WebSocket 在通过 HTTP 协议完成 Upgrade 握手后,会维持一个持久的连接,允许服务器主动向客户端推送数据。在生产环境下,我们通常使用加密的 wss:// 协议。

在实际开发中,为了保证连接的稳定性,我们需要实现心跳机制(ping/pong)来检测‘假死’连接,并采用指数退避算法来实现断线重连,以避免频繁冲击服务器。在 Flutter 开发中,我通常使用 web_socket_channel 并配合 Stream 来处理数据,同时会非常注意在组件销毁时及时关闭 sink,防止连接泄漏。”


本文旨在分享实战经验,如果对你有帮助,欢迎点赞关注。

#Dart#Flutter#WebSocket#HTTP
上次更新: 2026/07/22, 14:44:02
Flutter 无障碍开发实战:玩转 Semantics 解决视障用户使用痛点

← Flutter 无障碍开发实战:玩转 Semantics 解决视障用户使用痛点

最近更新
01
Flutter 无障碍开发实战:玩转 Semantics 解决视障用户使用痛点
07-21
02
Flutter 3.44 插件内置 Kotlin (KGP) 双向兼容适配指南
07-05
03
Obsidian - 使用 Share Note 分享笔记并自部署
06-21
更多文章>
Theme by Vdoing | Copyright © 2020-2026 FSA全栈行动
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×