Flutter for OpenHarmony:shelf_web_socket 快速构建 WebSocket 服务端,实现端到端实时通信(WebSocket 服务器) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:shelf_web_socket 快速构建 WebSocket 服务端,实现端到端实时通信(WebSocket 服务器) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net

在这里插入图片描述

前言

在移动应用开发中,我们通常扮演“客户端”的角色,去连接远程的 WebSocket 服务。但有时,我们需要在设备本身运行一个微型服务器,例如用于局域网内的设备发现、P2P 文件传输信令,或者在调试模式下作为数据广播源。

shelf_web_socket 是基于 Dart 标准 Web 服务器框架 shelf 的 WebSocket 处理器。它能让你在 Flutter 应用(包括 OpenHarmony)中轻松启动一个能够处理 WebSocket 连接的 HTTP 服务。

一、核心概念

  • Shelf: Dart 的 Web 服务器中间件管道框架(类似 Express.js)。
  • Handler: 处理请求并返回响应的函数。
  • WebSocketChannel: 下层封装,让 WebSocket 操作像 Stream 一样简单。

建立连接 ws://ip:port

协议升级请求 (Upgrade Request)

升级成功 (Success)

数据流/接收端 (Stream/Sink)

WebSocket 客户端\n(App/浏览器)

鸿蒙应用\n(shelf_web_socket)

处理器 (shelf handler)

通信通道 (WebSocketChannel)

二、集成与基础用法

2.1 添加依赖

dependencies:shelf: ^1.4.0 shelf_web_socket: ^3.0.0 web_socket_channel: ^3.0.0 

2.2 启动基础服务

import'package:shelf/shelf_io.dart'as shelf_io;import'package:shelf_web_socket/shelf_web_socket.dart';import'package:web_socket_channel/web_socket_channel.dart';voidmain()async{// 定义 WebSocket 处理器var handler =webSocketHandler((WebSocketChannel webSocket){ webSocket.stream.listen((message){print('收到消息: $message'); webSocket.sink.add('服务端已收到: $message');});});// 启动服务,监听所有 IP (0.0.0.0)var server =await shelf_io.serve(handler,'0.0.0.0',8080);print('WebSocket 服务已启动: ws://${server.address.host}:${server.port}');}
在这里插入图片描述

三、进阶场景与示例

3.1 示例一:广播消息

实现一个聊天室功能,将一个客户端发来的消息广播给所有连接者。

import'package:shelf_web_socket/shelf_web_socket.dart';import'package:web_socket_channel/web_socket_channel.dart';finalList<WebSocketChannel> _clients =[];var broadcastHandler =webSocketHandler((WebSocketChannel webSocket){ _clients.add(webSocket);print('新客户端连接,当前在线: ${_clients.length}'); webSocket.stream.listen((message){// 广播给其他客户端for(var client in _clients){if(client != webSocket){ client.sink.add(message);}}}, onDone:(){ _clients.remove(webSocket);print('客户端断开,剩余: ${_clients.length}');});});
在这里插入图片描述

3.2 示例二:鉴权校验

在建立连接前检查 Header 中的 Token。

import'package:shelf/shelf.dart';import'package:shelf_web_socket/shelf_web_socket.dart';HandlerauthMiddleware(Handler innerHandler){return(Request request){if(request.headers['Authorization']!='Bearer my_secret_token'){returnResponse.forbidden('未授权访问');}returninnerHandler(request);};}// 使用 Pipeline 组装// var handler = Pipeline().addMiddleware(authMiddleware).addHandler(wsHandler);
在这里插入图片描述

3.3 示例三:处理二进制数据

WebSocket 不仅能传字符串,还能传字节流(如图片片段)。

import'dart:typed_data';var binaryHandler =webSocketHandler((WebSocketChannel webSocket){ webSocket.stream.listen((message){if(message isList<int>){print('收到二进制数据,长度: ${message.length}');// 处理二进制逻辑...}else{print('收到文本: $message');}});});
在这里插入图片描述

四、OpenHarmony 平台适配

4.1 网络权限

作为服务端,你需要监听端口,这同样需要 Internet 权限。

"requestPermissions":[{"name":"ohos.permission.INTERNET"}]

4.2 后台运行限制

移动操作系统通常限制应用在后台运行服务。如果你的 WebSocket 服务需要长期运行,建议在前台 Service 中启动,或仅在 App 前台可见时运行。

五、完整实战示例:局域网即时画板服务端

本示例将在 OpenHarmony 设备上启动一个 WebSocket 服务。任何连接到该服务的客户端(可以是另一个 App 或浏览器)发送的坐标点,都会被广播给其他人,实现多端协同绘图。

5.1 示例代码

import'dart:async';import'dart:io';import'package:flutter/material.dart';import'package:shelf/shelf_io.dart'as shelf_io;import'package:shelf_web_socket/shelf_web_socket.dart';import'package:web_socket_channel/web_socket_channel.dart';voidmain(){runApp(constMaterialApp(home:ServerPage()));}classServerPageextendsStatefulWidget{constServerPage({super.key});@overrideState<ServerPage>createState()=>_ServerPageState();}class _ServerPageState extendsState<ServerPage>{HttpServer? _server;finalList<WebSocketChannel> _sockets =[];finalList<String> _logs =[];String _ipInfo ='获取中...';@overridevoidinitState(){super.initState();_getIpAddress();}Future<void>_getIpAddress()async{try{final interfaces =awaitNetworkInterface.list(type:InternetAddressType.IPv4);final ip = interfaces.first.addresses.first.address;setState(()=> _ipInfo = ip);}catch(e){setState(()=> _ipInfo ='无法获取 IP');}}// 启动服务Future<void>_startServer()async{var handler =webSocketHandler((WebSocketChannel webSocket){ _sockets.add(webSocket);_log('新连接接入'); webSocket.stream.listen((message){_log('广播数据: $message');// 广播坐标数据for(var socket in _sockets){if(socket != webSocket){ socket.sink.add(message);}}}, onDone:(){ _sockets.remove(webSocket);_log('连接断开');});});try{ _server =await shelf_io.serve(handler,InternetAddress.anyIPv4,8080);_log('服务启动于 ws://$_ipInfo:8080');}catch(e){_log('启动失败: $e');}}Future<void>_stopServer()async{await _server?.close(force:true);for(var s in _sockets){await s.sink.close();} _sockets.clear(); _server =null;_log('服务已停止');}void_log(String msg){if(!mounted)return;setState((){ _logs.insert(0,'[${DateTime.now().hour}:${DateTime.now().minute}] $msg');});}@overridevoiddispose(){_stopServer();super.dispose();}@overrideWidgetbuild(BuildContext context){returnScaffold( appBar:AppBar(title:constText('WebSocket Server')), body:Column( children:[Container( padding:constEdgeInsets.all(20), color:Colors.blue[50], child:Column( children:[Text('本机 IP: $_ipInfo', style:constTextStyle(fontSize:18, fontWeight:FontWeight.bold)),constSizedBox(height:10),Row( mainAxisAlignment:MainAxisAlignment.center, children:[ElevatedButton( onPressed: _server ==null? _startServer :null, child:constText('启动服务'),),constSizedBox(width:20),ElevatedButton( style:ElevatedButton.styleFrom(backgroundColor:Colors.red[100]), onPressed: _server !=null? _stopServer :null, child:constText('停止'),),],),],),),constDivider(),Expanded( child:ListView.builder( itemCount: _logs.length, itemBuilder:(context, index)=>ListTile( leading:constIcon(Icons.info_outline, size:16), title:Text(_logs[index], style:constTextStyle(fontSize:14)),),),),],),);}}
在这里插入图片描述

六、总结

通过 shelf_web_socket,我们让 OpenHarmony 手机不仅仅是信息的消费者,更成为了信息的生产者和中转站。

最佳实践

  1. 异常处理:Websocket 连接很容易因网络波动断开,务必处理 onDoneonError
  2. 资源释放:在组件销毁或应用退出时,务必关闭 Server 和所有 Channel连接,防止端口占用。
  3. 心跳机制:为了保持连接活性,建议在应用层实现 Ping/Pong 心跳包。

Read more

【C++----红黑树封装set / map底层大致封装】在C++的世界里,每一次编译都是对智慧的考验,每一次调试都是对耐心的磨砺。开发者们在这里不断学习、成长,用代码编织出一个个精彩纷呈的故事。

【C++----红黑树封装set / map底层大致封装】在C++的世界里,每一次编译都是对智慧的考验,每一次调试都是对耐心的磨砺。开发者们在这里不断学习、成长,用代码编织出一个个精彩纷呈的故事。

红黑树 set / map封装 * 1 封装红⿊树实现set和map * 1.1对底层源码及框架分析 * 2. 模拟实现map和set * 2.1 实现出复⽤红⿊树的框架,并⽀持insert * 2.2 ⽀持iterator的实现 * 2.2.1红黑树迭代器结构 * 2.2.2 迭代器++ * 2.2.4 iterator-- * 3 注意须知 [实现map/set] * 3.1 map[]实现 * 3.2代码实现 1 封装红⿊树实现set和map 1.1对底层源码及框架分析 SGI-STL30版本源代码,map和set的源代码在map/set/stl_

By Ne0inhk
【C++】现代C++的新特性constexpr,及其在C++14、C++17、C++20中的进化

【C++】现代C++的新特性constexpr,及其在C++14、C++17、C++20中的进化

各位读者大佬好,我是落羽!一个坚持不断学习进步的学生。 如果您觉得我的文章还不错,欢迎多多互三分享交流,一起学习进步! 也欢迎关注我的blog主页:落羽的落羽 文章目录 * 一、从C++11引入 * 1. 常量表达式和constexpr关键字的概念 * 2. constexpr修饰函数 * 二、constexpr在C++14中的进化 * 三、constexpr在C++17中的进化 * 四、constexpr在C++20中的进化 一、从C++11引入 1. 常量表达式和constexpr关键字的概念 现代C++,从C++11开始,引入了常量表达式和constexpr关键字的概念,并且在之后的C++标准中不断更新 常量表达式是指,值不会改变并且在编译过程中就能得到计算结果的表达式。用字面量、常量表达式初始化的const对象都是常量表达式。但是用变量初始化的const对象不是常量表达式。 constint a =1;//a是常量表达式constint b = a +1;//b是常量表达式int c

By Ne0inhk
自动驾驶中间件iceoryx - (附录)C++ 内存模型与原子操作详解

自动驾驶中间件iceoryx - (附录)C++ 内存模型与原子操作详解

附录A: C++ 内存模型与原子操作详解 📚 本附录内容 本附录深入讲解 C++ 11引入的内存模型(Memory Model)和原子操作(Atomic Operations), 这是理解 iceoryx 等高性能进程间通信系统无锁机制的核心基础。 适合读者:想深入理解 acquire/release 内存序语义需要实现或优化无锁数据结构想理解 iceoryx 内部同步机制的原理对并发编程和性能优化感兴趣的开发者 与主文档的关系:本附录是 第5章 同步与通知机制 的扩展阅读主文档 5.2.3 节提供了简化版本,适合快速学习本附录提供完整的技术细节和深入分析 目录 * A.1 为什么需要内存序 * A.2 C++ 内存序类型 * A.3 实例:生产者-消费者 * A.4 iceoryx 中的内存序使用 * A.5

By Ne0inhk
【C++】深入拆解二叉搜索树:从递归与非递归双视角,彻底掌握STL容器的基石

【C++】深入拆解二叉搜索树:从递归与非递归双视角,彻底掌握STL容器的基石

【C++】深入拆解二叉搜索树:从递归与非递归双视角,彻底掌握STL容器的基石 * 摘要 * 目录 * 一、概念 * 二、 性能分析 * 三、key结构非递归模拟实现 * 1. 二叉搜索树的插入 * 2. 二叉搜索树的查找 * 3. 二叉搜索树的删除 * 4. 二叉搜索树的中序遍历 * 四、key结构递归的模拟实现 * 1. 递归与非递归二叉搜索树核心操作的对比 * 2. 递归插入 * 3. 递归查找 * 4. 递归删除 * 总结 摘要 二叉搜索树(BST)是一种重要的数据结构,它通过"左子树所有节点值小于根节点,右子树所有节点值大于根节点"的特性实现高效的元素组织。本文详细解析了BST的核心概念、性能特点,并分别通过非递归和递归两种方式完整实现了插入、查找、删除等关键操作,深入探讨了指针引用在递归实现中的巧妙应用,以及两种实现方式在时间复杂度、空间复杂度和适用场景上的差异。 目录

By Ne0inhk