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

如何基于Flutter和Dart开发跨路由器连接的离线聊天应用?

Flutter离线跨路由器聊天应用实现方案

核心前提:打通跨路由器网络

要实现两台路由器下的用户离线通信,首先得打破两个局域网的隔离,最可靠的方式是在两台路由器之间建立站点到站点VPN:

  • 推荐用WireGuard(轻量、高性能)或OpenVPN,配置两台路由器互相作为Peer,允许各自局域网段的IP访问对方网络。配置完成后,两个路由器下的设备就像处于同一内网,可直接通过内网IP通信。

Flutter端通信架构

1. 设备发现

用mDNS(多播DNS)自动发现同一内网(含VPN打通的远端局域网)的在线设备,无需用户手动输入IP:

  • 可使用flutter_mdns库,扫描局域网内的服务实例,获取设备的IP和端口。

2. 点对点通信方案

文本消息:TCP可靠传输

文本消息要求不丢包、顺序正确,用TCP协议实现:

  • 基于Dart原生dart:io的Socket类,建立客户端与服务端的连接。
  • 消息封装为JSON格式,示例:{"type":"text","sender":"user1","content":"Hello","timestamp":1699999999}
  • 维护连接池,每个在线设备对应一个Socket连接,用心跳包(比如每30秒发一次空消息)检测连接状态,断开后自动重连。

文件/视频:分片传输

大文件直接传输易导致内存溢出或中断,采用分片+校验的方式:

  • 分片大小设为1MB左右,发送端先传输文件元数据(文件名、大小、哈希值),再逐个发送分片。
  • 接收端接收分片后校验哈希,全部接收完成后重组文件。
  • 用Dart的dart:io操作文件读写,避免阻塞UI线程,建议用Isolate处理文件IO。

3. 代码示例

TCP服务端监听

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

void startChatServer(int port) async {
  final server = await ServerSocket.bind(InternetAddress.anyIPv4, port);
  print("Chat server running on ${server.address}:${server.port}");

  await for (final socket in server) {
    _handleClientConnection(socket);
  }
}

void _handleClientConnection(Socket socket) {
  print("New client connected: ${socket.remoteAddress}:${socket.remotePort}");

  socket.listen(
    (data) {
      final message = utf8.decode(data);
      print("Received: $message");
      // 可将消息广播给其他连接的客户端
    },
    onError: (err) => print("Connection error: $err"),
    onDone: () {
      print("Client disconnected");
      socket.close();
    },
  );
}

TCP客户端连接与消息发送

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

class ChatClient {
  Socket? _socket;

  Future<void> connect(String ip, int port) async {
    try {
      _socket = await Socket.connect(ip, port);
      print("Connected to server");
      
      _socket?.listen(
        (data) => print("Received: ${utf8.decode(data)}"),
        onError: (err) => print("Connection error: $err"),
        onDone: () => print("Disconnected from server"),
      );
    } catch (e) {
      print("Failed to connect: $e");
    }
  }

  void sendMessage(String content) {
    final message = jsonEncode({
      "type": "text",
      "content": content,
      "timestamp": DateTime.now().millisecondsSinceEpoch
    });
    _socket?.write(message);
  }

  void disconnect() => _socket?.close();
}

文件分片发送示例

import 'dart:io';

Future<void> sendFile(Socket socket, String filePath) async {
  final file = File(filePath);
  final fileSize = await file.length();
  const chunkSize = 1024 * 1024; // 1MB分片

  // 发送文件元数据
  socket.write("FILE_META:${file.path.split('/').last}:$fileSize\n");

  final raf = await file.open();
  int sentBytes = 0;

  while (sentBytes < fileSize) {
    final remaining = fileSize - sentBytes;
    final currentChunkSize = remaining > chunkSize ? chunkSize : remaining;
    final chunk = await raf.read(currentChunkSize);
    
    socket.add(chunk);
    sentBytes += currentChunkSize;
    
    // 此处可回调更新UI传输进度
    print("Sent ${(sentBytes / fileSize * 100).toStringAsFixed(1)}%");
  }

  await raf.close();
  socket.write("FILE_END\n");
  print("File sent complete");
}

权限与兼容性

  • Android:需添加INTERNET、READ_EXTERNAL_STORAGE/READ_MEDIA_VIDEO(Android 13+)、WRITE_EXTERNAL_STORAGE权限。
  • iOS:需开启网络权限,添加NSFileProviderDomainUsageDescription用于文件访问。

状态管理与UI

  • 用Riverpod或Provider管理在线用户列表、消息记录、文件传输进度等状态。
  • UI层基于ListView展示聊天消息,用LinearProgressIndicator显示文件传输进度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 20:41:03