Flutter 开发者必备:WebSocket 实用指南

本文首发于公众号:移动开发那些事 Flutter 开发者必备:WebSocket 实用指南

1 简介

Websocket 是网络通信(应用层)协议,是客户端应用中解决实时通信的解决方案之一。本文将从 Websocket 的基本概念出发,结合其实现的原理详细解析其在 Flutter开发中要如何使用该协议来实现业务需求,帮助开发者全面掌握这一关键技术。

2 Websocket 介绍

Websocket是一种基于TCP全双工通信协议 ,由HTML5标准定义,旨在解决HTTP协议在实时通信场景中的局限性。与传统的 HTTP协议相比,Websocket 最大的特点是建立连接后保持持久连接状态 ,允许客户端与服务器之间进行双向的数据传输,而无需传统HTTP 那样每次通信都重新建立连接。

HTTP 协议中,通信只能由客户端发起,服务器无法主动向客户端推送数据。如果需要实现类似实时更新的功能,只能通过轮询(Polling)或长轮询(Long Polling)等方式模拟,这些方式不仅会消耗大量的网络带宽和服务器资源,还会导致数据传输延迟。而Websocket 通过一次握手建立连接后,客户端和服务器可以随时向对方发送数据,实现了真正意义上的实时双向通信。

看到这里,很多朋友肯定会有疑问,现在HTTP 2.0的协议也是支持服务器推送的,为啥说要实现类似的推送的功能要通过轮询来实现呢?因为HTTP 2.0的推送是有条件的

  • 2.0的推送的时机是在客户端请求某个资源时,能把相关的资源推送到客户端缓存;(推送的资源只能是浏览器能直接使用的缓存资源)

2.1 Websocket 推送与HTTP 2.0的区别

对比维度 WebSocket 推送 HTTP/2 服务器推送(Server Push)
协议本质 独立的全双工通信协议,需单独建立连接 基于 HTTP/2 协议的扩展功能,复用 HTTP 连接
推送方向 全双工:客户端与服务器可双向主动推送数据 单向:仅服务器可主动向客户端推送关联资源
连接方式 需通过 HTTP 握手升级为 WebSocket 连接,连接长期保持 基于已建立的 HTTP/2 连接,无需额外握手
推送触发条件 无依赖,服务器可随时主动推送任意数据 需基于客户端的请求触发,推送与请求相关的资源
数据格式 支持文本、二进制等任意格式,协议本身不限制 主要推送 HTTP 资源(如 CSS、JS、图片等)
适用场景 实时通信场景(如聊天、弹幕、实时数据监控) 网页资源预加载(如提前推送页面依赖的 CSS/JS)
连接生命周期 连接长期存在,除非主动关闭 随 HTTP/2 连接生命周期结束而终止(通常较短)
状态管理 保持连接状态,适合持续交互 无状态,推送资源后不维护长期交互状态
兼容性 需浏览器和服务器同时支持 WebSocket 协议 依赖 HTTP/2 协议支持,需客户端和服务器均兼容
资源消耗 长期保持连接,服务器资源占用较高 复用现有连接,资源消耗相对较低

2.2 协议格式

Websocket 协议的 URL 格式与 HTTP 有所区别,使用ws://(非加密)或wss://(加密,基于 TLS)作为协议前缀,例如ws://``example.com/realtimewss://``secure.example.com/chat。这种协议设计使得Websocket 能够与 HTTP 协议共用端口(80 和 443),从而避免了防火墙对非标准端口的限制。

2.3 Websocket特点

2.3.1 实现即时双向通信

Websocket 最核心的作用是打破传统 HTTP 的单向请求限制,让客户端与服务器之间能够实时互发数据 。例如在即时聊天应用中,用户发送消息后,服务器可以立即将消息推送给接收方的客户端,无需客户端频繁发起请求;在多人协作工具中,某一用户的操作变更能通过 Websocket实时同步到其他用户的设备上,确保所有参与者看到的数据保持一致。

2.3.2 降低资源消耗与延迟

相比轮询等模拟实时通信的方式,Websocket 通过持久连接减少了重复建立连接的开销。在移动应用中,这意味着更低的网络流量消耗和更长的设备续航时间,尤其适合移动场景。同时,数据传输的实时性也得到显著提升,延迟可降低至毫秒级,这对于在线游戏、实时语音交互的应用至关重要;

3 Websocket核心流程

3.1 建立连接:基于 HTTP 的握手过程

Websocket 连接的建立采用 "握手" 机制,本质上是通过 HTTP 请求发起,然后升级为 Websocket 协议

  • 客户端向服务器发送一个特殊的 HTTP GET 请求,请求头中包含Upgrade: websocketConnection: Upgrade字段,表明希望将连接升级为 Websocket 协议;

  • 服务器收到请求后,若支持 Websocket 协议,会返回 101 状态码(Switching Protocols),并在响应头中包含Sec-WebSocket-Accept等字段,完成握手;

  • 握手成功后,HTTP 连接正式升级为 Websocket 连接,客户端与服务器进入全双工通信状态,此时可发送业务数据;

3.2 传输数据:帧格式与消息处理

Websocket 协议采用帧(Frame)作为数据传输的基本单位,所有数据都被分割为帧进行传输,并且支持文本消息和二进制消息两种类型。文本消息通常用于传输 JSON、字符串等文本数据,二进制消息则适用于图片、音频等二进制数据。

4 Flutter 中 Websocket 的使用

Flutter应用一般通过官方提供的web_socket_channel包实现对 Websocket 的支持,该库封装了原生平台的WebSocket实现(Android使用OkHttp,iOS使用NSURLSession),并提供统一的Dart接口。其核心流程可分为连接建立、数据传输、连接管理三个阶段,每个阶段都涉及特定的技术细节和处理逻辑。 以下代码示例展示了 Flutter 中建立 Websocket 连接的基本方式:

4.1 建立连接

go 复制代码
import 'package:web_socket_channel/io.dart';

import 'package:web_socket_channel/web_socket_channel.dart';

// 1  建立与服务器的Websocket连接
// 步骤1:连接到WebSocket服务器(假设服务器地址为ws://echo.websocket.events)
final _channel = WebSocketChannel.connect('ws://echo.websocket.events');
 // 步骤2:监听连接是否建立,
_channel.ready.then((_) {
	// 更新已连接的标记位
  print('Websocket连接已建立');
}).catchError((error) {
	// 这里处理连接失败的一些情况
	print('连接建立失败: \$error');

});

4.2 数据传输

  • 消息监听:通过_channel.stream.listen()订阅消息流,onData回调会在收到服务器消息时触发。

  • 消息发送:_channel.sink.add()将消息写入流的接收端(StreamSink),底层自动封装为WebSocket文本帧并发送。

  • 连接关闭:_channel.sink.close()发送关闭帧,通知服务器终止连接;onDone回调会在连接完全关闭后触发。

less 复制代码
import 'dart:convert';
// 发送文本消息
	_channel.sink.add('Hello, Websocket!');
// 发送JSON数据
	_channel_channel.sink.add(json.encode({'type': 'message', 'content': 'Flutter Websocket'}));
// 接收消息
 // 监听服务器消息(Stream订阅)
  _channel.stream.listen(
      (message) {
        // 收到消息后更新UI
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('收到消息: $message')),
        );
      },
      onError: (error) {
        // 处理连接或通信错误(如网络中断)
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('错误: $error')),
        );
      },
      onDone: () {
        // 连接关闭时触发(如主动调用close()或服务器断开)
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('连接已关闭')),
        );
      },
    );

在底层实现中,web_socket_channel会将发送的消息拆分为符合 Websocket 帧格式的数据包(包含 opcode、掩码、数据长度等信息),并在接收时将帧重组为完整的消息。这种帧机制确保了数据传输的高效性和可靠性,同时支持消息的分片传输 ------ 对于大型消息,可分割为多个帧逐步发送,避免单次传输的数据量过大。

4.3 连接管理

由于网络波动、服务器维护等原因,Websocket 连接可能会意外断开。为保证通信的稳定性,移动应用需要实现完善的连接管理机制,核心包括心跳检测和自动重连。

心跳检测的原理是客户端与服务器定期互发心跳包(通常是一段特定的字符串或空消息),若在规定时间内未收到对方的心跳响应,则判定连接异常。在 Flutter 中可通过定时任务实现:

dart 复制代码
import 'dart:async';

// 启动心跳检测

Timer.periodic(Duration(seconds: 30), (timer) {
if (channel.closeCode == null) { // 连接未关闭
	channel.sink.add('heartbeat'); // 发送心跳包
} else {
	timer.cancel(); // 连接已关闭,取消定时任务
 }
});

自动重连 则是在连接断开后,通过定时重试的方式重新建立连接。可结合onDone回调实现:

scss 复制代码
void _connect() {
final _channel = WebSocketChannel.connect('ws://echo.websocket.events');
_channel.stream.listen(
	(message) => print('收到消息: \$message'),
  onDone: () {
    print('连接断开,尝试重连...');
    Future.delayed(Duration(seconds: 5), _connect); // 5秒后重试
);

}

在实际应用中,重连机制还需考虑重连次数限制、网络状态检测(如通过connectivity_plus包判断设备是否联网)等因素,避免无效的重连尝试消耗过多资源。

5 总结

本文主要从Websocket的特点出发,介绍了其建立连接,传输数据和管理连接方法的一些使用及基础原理,同时也介绍了如何在Flutter中使用这一技术。主要是想帮助开发者了解Websocket,并在有实际业务需求的场景下能正确使用该技术。 当然在实际开发中,还需结合具体业务场景优化通信策略,如合理设计消息格式、控制数据传输频率等,以在实时性和资源消耗之间取得最佳平衡。

相关推荐
小林的技术分享4 小时前
关于排查 Flutter 3.27.0 版本Android端无法禁用Impeller引擎的过程记录
前端·flutter
coder_pig19 小时前
🤡 公司Android老项目升级踩坑小记
android·flutter·gradle
w_y_fan1 天前
双token机制:flutter_secure_storage 实现加密存储
前端·flutter
dragon7251 天前
关于image组件设置宽高不生效问题的探究
flutter
会煮咖啡的猫1 天前
Flutter 是否需要 UI 组件库?
flutter
眼镜会飞1 天前
Flutter 3.x新版android端的build.gradle.kts文件配置arm64-v8a和armeabi-v7a等
android·前端·flutter
恋猫de小郭1 天前
Flutter 小技巧之有趣的 UI 骨架屏框架 skeletonizer
android·前端·flutter
一狐九1 天前
Flutter如何通过GlobalKey调用组件内的方法
前端·flutter
张风捷特烈1 天前
鸿蒙纪·Flutter卷#03 | 从配置证书到打包发布
android·flutter·harmonyos