You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中如何检测TCP Socket连接状态并监听断连事件

Dart Socket 连接状态判断与断开监听实现

Dart 标准库的Socket基于TCP协议实现,没有提供直接标记连接存活的布尔属性,连接状态需要结合事件监听、心跳机制共同判定,具体实现方案如下:

一、单次主动检查连接状态

仅能检查当前进程已经感知到的连接状态,无法识别网络静默断开(比如网线拔出、NAT超时、中间链路断网但双方都未收到断开报文的场景):

/// 返回true代表本地已确认连接处于可用状态
bool checkSocketAlive(Socket socket) {
  final rawSocket = socket.rawSocket;
  if (rawSocket == null) return false;
  // RawSocket状态为open时连接才可用,其余closed/readClosed/writeClosed都代表连接已不可用
  return rawSocket.state == RawSocketState.open;
}

如果需要100%准确判定连接可达,必须配合应用层心跳:给服务端发送约定的探测包,在超时时间内收到响应即可确认连接正常。

二、实时监听连接状态、断开自动触发通知

这是生产环境长连接场景的标准实现,不需要轮询,可覆盖所有断开场景:

  • 利用Socket自带的事件流捕获正常断开、连接重置、网络错误等可直接感知的断开事件
  • 加入定时心跳机制,补全静默断开场景的感知
  • 所有断开场景统一触发回调,避免重复通知

完整实现代码:

import 'dart:async';
import 'dart:io';

class StableSocket {
  Socket? _socket;
  Timer? _heartbeatTimer;
  int _missedHeartbeatCount = 0;

  /// 连接断开时触发,参数为断开原因
  final void Function(dynamic disconnectReason)? onDisconnect;
  /// 连接建立成功时触发
  final void Function(Socket socket)? onConnect;
  /// 收到业务数据时触发
  final void Function(List<int> data)? onData;

  // 心跳配置,可根据业务场景调整
  final Duration heartbeatPeriod = const Duration(seconds: 45);
  final int maxMissedHeartbeat = 2;

  Future<void> connect(String host, int port) async {
    // 先清理之前的连接资源
    _cleanup();
    try {
      _socket = await Socket.connect(host, port);
      _missedHeartbeatCount = 0;
      onConnect?.call(_socket!);

      // 监听连接关闭事件
      _socket!.done.then((_) => _triggerDisconnect("connection closed by remote"));
      
      // 监听socket数据流
      _socket!.listen(
        (data) {
          // 收到任意数据都代表连接存活,重置心跳计数
          _missedHeartbeatCount = 0;
          onData?.call(data);
        },
        onError: (err) => _triggerDisconnect(err),
        onDone: () => _triggerDisconnect("socket stream closed"),
        cancelOnError: true,
      );

      // 启动心跳探测
      _startHeartbeat();
    } catch (e) {
      _triggerDisconnect("connect failed: $e");
    }
  }

  void _startHeartbeat() {
    _heartbeatTimer?.cancel();
    _heartbeatTimer = Timer.periodic(heartbeatPeriod, (timer) {
      if (_socket == null) {
        timer.cancel();
        return;
      }
      _missedHeartbeatCount++;
      if (_missedHeartbeatCount > maxMissedHeartbeat) {
        _triggerDisconnect("heartbeat timeout, no response from server");
        return;
      }
      // 发送心跳包,字节内容需要和服务端约定一致,不要和业务报文冲突
      _socket!.add([0x00]);
    });
  }

  void _triggerDisconnect(dynamic reason) {
    if (_socket == null) return;
    _cleanup();
    onDisconnect?.call(reason);
  }

  void _cleanup() {
    _heartbeatTimer?.cancel();
    _socket?.close();
    _socket = null;
    _missedHeartbeatCount = 0;
  }

  /// 主动断开连接
  void close() {
    _triggerDisconnect("active close by local");
  }
}

注意事项

  • 心跳包格式必须和服务端约定一致,避免被服务端当成非法报文断开连接
  • 国内移动网络的NAT超时普遍在3-5分钟,心跳间隔设为30-60s即可兼顾流量消耗和连接可靠性
  • 不要靠_socket != null、socket.remoteAddress存在这类条件判断连接状态,Socket对象销毁前这些属性会一直保留有效值,哪怕连接已经断开

内容的提问来源于stack exchange,提问作者LeonardvV

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 14:36:24