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

Flutter中如何从RawDatagramSocket接收并存储数据报列表?

如何在Dart中接收并存储UDP Datagram列表?

你的代码没能正常工作,主要是对Dart UDP socket的异步接收逻辑理解有偏差,我来给你梳理下问题所在,再提供两种可行的实现方式:

先说说你之前代码的问题

  1. 第一种方法的错误:socket.listen的回调参数是socket的状态事件(比如RawSocketEvent.read),你不需要在回调里再调用socket.receive()——正确的做法是当事件为RawSocketEvent.read时,再调用receive()获取Datagram。另外,listen是异步执行的,你直接返回列表的话,此时还没收到任何数据,自然是空列表。
  2. 第二种方法的错误:先调用socket.close()再用toList(),此时socket已经关闭,只能捕获到关闭事件,根本不会收集到之前收到的Datagram。

可行方案一:通过Stream监听+Completer收集数据

这种方式利用socket的流事件,结合Completer来等待接收终止信号(比如超时、收到特定结束消息),异步收集所有Datagram:

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

Future<List<Datagram>> collectUdpDatagrams(InternetAddress address, int port, {Duration? timeout}) async {
  final List<Datagram> datagrams = [];
  final socket = await RawDatagramSocket.bind(address, port);
  final completer = Completer<List<Datagram>>();

  socket.listen((event) {
    if (event == RawSocketEvent.read) {
      final datagram = socket.receive();
      if (datagram != null) {
        datagrams.add(datagram);
        // 示例:收到"END"字符串就停止接收
        final message = String.fromCharCodes(datagram.data);
        if (message.trim() == "END") {
          socket.close();
          completer.complete(datagrams);
        }
      }
    } else if (event == RawSocketEvent.closed) {
      // 如果socket意外关闭,返回已收集的数据
      if (!completer.isCompleted) {
        completer.complete(datagrams);
      }
    }
  });

  // 可选:超时自动停止接收
  if (timeout != null) {
    Timer(timeout, () {
      if (!completer.isCompleted) {
        socket.close();
        completer.complete(datagrams);
      }
    });
  }

  return completer.future;
}

// 调用示例
void main() async {
  final datagrams = await collectUdpDatagrams(InternetAddress.anyIPv4, 8080, timeout: Duration(seconds: 5));
  print("共收到${datagrams.length}个Datagram:");
  for (var d in datagrams) {
    print("来自 ${d.address}:${d.port},内容:${String.fromCharCodes(d.data)}");
  }
}

可行方案二:循环调用receive()直到停止

这种方式更直观,通过异步循环调用socket.receive(),直到socket关闭或者触发终止条件:

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

Future<List<Datagram>> collectUdpDatagramsLoop(InternetAddress address, int port, {Duration? timeout}) async {
  final List<Datagram> datagrams = [];
  final socket = await RawDatagramSocket.bind(address, port);
  bool keepListening = true;

  // 可选:超时自动停止
  timeout?.then((_) {
    keepListening = false;
    socket.close();
  });

  while (keepListening) {
    final datagram = await socket.receive();
    if (datagram == null) break; // socket关闭,退出循环
    datagrams.add(datagram);
    
    // 示例:收到特定消息停止
    final message = String.fromCharCodes(datagram.data);
    if (message.trim() == "END") {
      keepListening = false;
      socket.close();
    }
  }

  return datagrams;
}

// 调用示例
void main() async {
  final datagrams = await collectUdpDatagramsLoop(InternetAddress.anyIPv4, 8080, timeout: Duration(seconds: 5));
  print("收集到的Datagram数量:${datagrams.length}");
  datagrams.forEach((d) => print("内容:${String.fromCharCodes(d.data)}"));
}

关键注意点

  • UDP是无连接协议,没有“结束”的天然信号,所以你必须自己定义停止条件(比如超时、特定结束消息、手动关闭socket)
  • 所有接收操作都是异步的,必须通过Future等待收集完成,不能同步返回列表
  • 当socket触发RawSocketEvent.read事件时,才是调用receive()获取Datagram的正确时机

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 07:02:34