最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Flutter中實(shí)現(xiàn)TCP通信的關(guān)鍵步驟與代碼示例

 更新時(shí)間:2025年09月16日 08:42:02   作者:Dabei  
在移動(dòng)端開(kāi)發(fā)中,除了常見(jiàn)的 HTTP、MQTT 之外,很多場(chǎng)景需要直接使用 TCP 通信,例如局域網(wǎng)設(shè)備控制、實(shí)時(shí)傳輸?shù)?本文將介紹在 Flutter/Dart 中實(shí)現(xiàn)一個(gè) TCP 客戶端的基本過(guò)程,并解析關(guān)鍵代碼點(diǎn),需要的朋友可以參考下

引言

在移動(dòng)端開(kāi)發(fā)中,除了常見(jiàn)的 HTTP、MQTT 之外,很多場(chǎng)景需要直接使用 TCP 通信,例如局域網(wǎng)設(shè)備控制、實(shí)時(shí)傳輸?shù)?。本文將介紹在 Flutter/Dart 中實(shí)現(xiàn)一個(gè) TCP 客戶端的基本過(guò)程,并解析關(guān)鍵代碼點(diǎn)。文章同時(shí)給出 自動(dòng)重連心跳保活 的完整示例代碼,便于直接落地。

1. 基本思路

  1. 使用 Socket.connect 建立連接
  2. socket 轉(zhuǎn)換為 流(Stream) 進(jìn)行監(jiān)聽(tīng),實(shí)時(shí)接收消息
  3. 使用 LineSplitter 按行切分消息,避免 TCP 粘包/分包問(wèn)題(前提:每條消息以 \n 結(jié)尾,且內(nèi)容不含換行)
  4. 加入 超時(shí)/心跳自動(dòng)重連(指數(shù)退避 + 抖動(dòng))

2. 建立 TCP 連接(明文)

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

class TcpClient {
  final String host;
  final int port;
  Socket? _socket;
  StreamSubscription<String>? _subscription;

  TcpClient(this.host, this.port);

  Future<void> connect() async {
    try {
      _socket = await Socket.connect(host, port, timeout: const Duration(seconds: 5));
      print('? Connected to: ${_socket!.remoteAddress.address}:${_socket!.remotePort}');

      _subscription = _socket!
          .transform(utf8.decoder)
          .transform(const LineSplitter())
          .listen(_onLine, onError: _onError, onDone: _onDone);
    } catch (e) {
      print('?? Connection failed: $e');
      rethrow;
    }
  }

  void _onLine(String line) {
    print('?? Received line: $line');
  }

  void _onError(Object e, [StackTrace? st]) {
    print('? Socket error: $e');
    disconnect();
  }

  void _onDone() {
    print('?? Server closed connection');
    disconnect();
  }

  void send(String message) {
    final s = _socket;
    if (s != null) {
      s.write(message + '\n'); // 每條消息后加換行
      print('?? Sent: $message');
    }
  }

  void disconnect() {
    _subscription?.cancel();
    _subscription = null;
    _socket?.destroy();
    _socket = null;
    print('?? Disconnected');
  }
}

3. 心跳與空閑超時(shí)

class HeartbeatManager {
  final void Function() onSendHeartbeat;
  final Duration interval;
  Timer? _timer;

  HeartbeatManager({required this.onSendHeartbeat, this.interval = const Duration(seconds: 30)});

  void start() {
    _timer ??= Timer.periodic(interval, (_) => onSendHeartbeat());
  }

  void stop() {
    _timer?.cancel();
    _timer = null;
  }
}

4. 自動(dòng)重連(指數(shù)退避 + 抖動(dòng))

import 'dart:math';

class ReconnectPolicy {
  final Duration minBackoff;
  final Duration maxBackoff;
  int _attempt = 0;
  final Random _rnd = Random();

  ReconnectPolicy({this.minBackoff = const Duration(seconds: 1), this.maxBackoff = const Duration(seconds: 30)});

  Duration nextDelay() {
    final base = minBackoff.inMilliseconds * pow(2, _attempt).toInt();
    final capped = min(base, maxBackoff.inMilliseconds);
    final jitter = (capped * (0.2 * (_rnd.nextDouble() * 2 - 1))).round();
    _attempt = min(_attempt + 1, 10);
    return Duration(milliseconds: max(0, capped + jitter));
  }

  void reset() => _attempt = 0;
}

5. 最佳實(shí)踐小結(jié)

  • 行分隔協(xié)議:確保發(fā)送端每條消息都以 \n 結(jié)尾,且消息體不包含換行
  • 統(tǒng)一編碼:收發(fā)都用 UTF?8
  • 心跳?;?/strong>:15–30 秒 1 次,收不到響應(yīng) → 重連
  • 自動(dòng)重連:指數(shù)退避 + 抖動(dòng)
  • 超時(shí)治理:連接超時(shí)、請(qǐng)求超時(shí)、空閑超時(shí)
  • 可觀測(cè)性:埋點(diǎn)連接時(shí)延、失敗原因、重連次數(shù)、心跳 RTT 等

6. 完整示例代碼(可直接運(yùn)行)

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';

class ReconnectPolicy {
  final Duration minBackoff;
  final Duration maxBackoff;
  int _attempt = 0;
  final Random _rnd = Random();

  ReconnectPolicy({this.minBackoff = const Duration(seconds: 1), this.maxBackoff = const Duration(seconds: 30)});

  Duration nextDelay() {
    final base = minBackoff.inMilliseconds * pow(2, _attempt).toInt();
    final capped = min(base, maxBackoff.inMilliseconds);
    final jitter = (capped * (0.2 * (_rnd.nextDouble() * 2 - 1))).round();
    _attempt = min(_attempt + 1, 10);
    return Duration(milliseconds: max(0, capped + jitter));
  }

  void reset() => _attempt = 0;
}

class HeartbeatManager {
  final void Function() onSendHeartbeat;
  final Duration interval;
  Timer? _timer;

  HeartbeatManager({required this.onSendHeartbeat, this.interval = const Duration(seconds: 30)});

  void start() {
    _timer ??= Timer.periodic(interval, (_) => onSendHeartbeat());
  }

  void stop() {
    _timer?.cancel();
    _timer = null;
  }
}

class RobustLineClient {
  final String host;
  final int port;
  Socket? _socket;
  StreamSubscription<String>? _sub;

  final HeartbeatManager _hb;
  final ReconnectPolicy _policy = ReconnectPolicy();
  Timer? _idleTimer;

  RobustLineClient({required this.host, required this.port})
      : _hb = HeartbeatManager(onSendHeartbeat: () {/* later bound */}, interval: const Duration(seconds: 20));

  Future<void> start() async {
    await _connect();
  }

  Future<void> _connect() async {
    try {
      _socket = await Socket.connect(host, port, timeout: const Duration(seconds: 5));
      print('? connected');
      _policy.reset();
      _hb.start();

      _sub = _socket!
          .transform(utf8.decoder)
          .transform(const LineSplitter())
          .listen(_onLine, onError: _onError, onDone: _onDone);

      // 心跳綁定到 send
      _hb.onSendHeartbeat.call = () => send('ping');

      _resetIdleTimeout();
    } catch (e) {
      print('?? connect failed: $e');
      await _scheduleReconnect();
    }
  }

  void _onLine(String line) {
    _resetIdleTimeout();
    print('?? $line');
  }

  void _onError(Object e, [StackTrace? st]) {
    print('? $e');
    _teardown();
    _scheduleReconnect();
  }

  void _onDone() {
    print('?? closed by server');
    _teardown();
    _scheduleReconnect();
  }

  void _teardown() {
    _idleTimer?.cancel();
    _idleTimer = null;
    _hb.stop();
    _sub?.cancel();
    _sub = null;
    _socket?.destroy();
    _socket = null;
  }

  Future<void> _scheduleReconnect() async {
    final delay = _policy.nextDelay();
    print('? reconnect in ${delay.inMilliseconds} ms');
    await Future.delayed(delay);
    await _connect();
  }

  void _resetIdleTimeout() {
    _idleTimer?.cancel();
    _idleTimer = Timer(const Duration(seconds: 60), () {
      print('? idle timeout -> reconnect');
      _teardown();
      _scheduleReconnect();
    });
  }

  void send(String message) {
    _socket?.write(message + '\n');
  }
}

以上就是Flutter中實(shí)現(xiàn)TCP通信的關(guān)鍵步驟與代碼示例的詳細(xì)內(nèi)容,更多關(guān)于Flutter實(shí)現(xiàn)TCP通信的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

临沂市| 忻城县| 卓尼县| 东明县| 蕉岭县| 花垣县| 郁南县| 郑州市| 集安市| 巴林右旗| 乾安县| 鹿邑县| 安徽省| 镇赉县| 合水县| 琼结县| 仙游县| 昌黎县| 隆安县| 全椒县| 秦安县| 贵阳市| 双城市| 张家口市| 甘洛县| 满城县| 元阳县| 惠安县| 轮台县| 辽中县| 双鸭山市| 湘潭县| 治多县| 和龙市| 夏邑县| 马尔康县| 绥德县| 察雅县| 盐池县| 札达县| 红原县|