上个月我把一个基于 Flutter 写的工具类 App 往 OpenHarmony 开发板上迁移,编译、打包、权限申请都过了,页面也正常渲染,结果点击“连接服务器”按钮后,界面卡了大概 10 秒才弹出 error。一开始我以为是 Dart 代码兼容性问题,把 Socket 创建、数据收发全部换成了最保守的写法,依然超时。后来把鸿蒙侧日志和网络报文拉出来比对,才发现问题根本不在 Dart 代码里,而是出在权限声明和设备网络的配置上。
如果你也是头一次在 OpenHarmony 上跑 Flutter 应用,并且要处理 Socket 通信、TCP 长连接,或者“本地数据库 + 后端同步”这类依赖网络的功能,建议把这篇看完。这不止是一份 Flutter 网络编程的代码示例,更是我把真实设备上踩过的坑、排查链路和设计取舍完整复述一遍的手记。适合已经会跑 Flutter Demo、但还没在鸿蒙设备上做过网络类项目的开发者,也适合从 Android/iOS 转过来的 Flutter 开发者做对比参考。
1. 移植到 OpenHarmony 后,Socket 连接失败的第一现场
1.1 先检查的不是代码,而是权限和 module.json5
在 Android 上要联网,第一件事是在 AndroidManifest.xml 里声明 android.permission.INTERNET。OpenHarmony 也有类似机制,位置在模块的 module.json5 文件里。很多人从 Android 迁过来只记得跑通页面,忘了这一步,结果所有网络请求静默失败。
打开 entry/src/main/module.json5,在 module 节点下加 requestPermissions:
json复制{
"module": {
"name": "entry",
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
这个权限不声明,Socket.connect 往往不会立刻抛 SocketException,而是表现为长时间无响应,最后走完系统默认超时后才报错。为什么?因为系统在底层网络策略阶段就把请求拦了,应用层拿不到即时反馈。这个“不报错、只卡顿”的现象非常有迷惑性,我身边已经不止一个人在这个问题上耗掉半天。
还要注意一点:某些开发板系统镜像或模拟器默认放开了网络权限,你在模拟器上测不出来,换到真机受限网络策略下才暴露。这类问题“时好时坏”的另一个来源是:不同版本的 OpenHarmony 系统对网络权限的处理策略不完全一致。所以移植后第一件事,就是确认权限不是“可能配了”,而是“确实配了”。
1.2 插件兼容性是 OpenHarmony 独有的变量
OpenHarmony 的 Flutter 生态还在完善中,你在 pub.dev 上找到的插件,很可能是只实现了 Android 和 iOS 原生代码的。纯 Dart 包一般没问题,因为它只在应用进程内运行,不碰系统底层能力;但凡是依赖原生网络栈、原生加密库或者系统网络状态接口的插件,就要确认是否有 ohos 目录或是否声明支持 OpenHarmony。
举个例子,dio 是纯 Dart 实现的 HTTP 客户端,理论上有 Dart VM 就能跑,不需要依赖 OkHttp,所以在 OpenHarmony 上核心逻辑可以正常工作。但有些基于 OkHttp 二次封装的 Flutter 网络库,在 OpenHarmony 上很容易遇到 MissingPluginException,甚至编译阶段就失败。
判断方法很简单:去插件的源码目录里看有没有 ohos 或 harmony 文件夹。没有的话,要么找替代方案,要么自己用 MethodChannel 包一层鸿蒙原生实现。不要等到运行期再排查,编译都过不了的时候最浪费时间。
1.3 把 Dart 层日志和 hilog 分开看
flutter run 只能看到 Dart 层标准输出,鸿蒙系统侧的错误要用 hilog 查看。这两个日志不是一回事,很多人混淆了。
实际排查时我习惯开两个终端:
- 一个跑
flutter run,专注看 Dart 层异常,比如SocketException: Connection refused这种就属于 Dart 层,Flutter 日志能直接看到。 - 另一个用
hilog过滤关键字,如hilog | grep -iE "network|socket|permission",看系统网络栈有没有报错。
如果 Socket 连接一直挂起、没有异常、没有日志,问题大概率在系统层:权限没生效、设备没连上局域网、或者目标端口被防火墙挡住。不要一上来就怀疑 Flutter 框架,OpenHarmony 上的 Flutter 已经能正常跑 dart:io,Socket 这块的能力是完整的。
按“权限 → 网络连通性 → 代码 → 协议”的顺序排查,比随机试代码高效得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Socket 编程绕不开的底层逻辑:连接、粘包与心跳
2.1 一次 connect 背后发生了什么
Socket.connect 看似只是一个函数调用,但它背后是 TCP 三次握手过程:客户端发 SYN,服务端回 SYN+ACK,客户端再回 ACK,然后 connect 才返回成功。
这里有个容易误解的点:connect 成功只代表 TCP 连接建立了,不代表服务端业务层已经准备好接收数据。用打电话来类比,三次握手相当于“对方接了电话”,但接电话的人可能还在找耳机、没进入对话状态。很多人在客户端成功连接后立刻发一大包数据,然后被服务端断开,于是怀疑网络,其实服务端可能只是在等待业务层的握手确认或第一个协议消息,客户端发的东西它根本解析不了。
理论上一个端口同时能排队的半连接数量是有限的,如果服务端处理不过来,连接建立本身就会变慢或失败。客户端侧最直接的影响就是:connect 操作可能长时间不返回。所以在 Flutter 里,连接超时是必须设置的,不能指望系统默认行为。还有一个细节:如果在弱网
