Flutter中如何从RawDatagramSocket接收并存储数据报列表?
如何在Dart中接收并存储UDP Datagram列表?
你的代码没能正常工作,主要是对Dart UDP socket的异步接收逻辑理解有偏差,我来给你梳理下问题所在,再提供两种可行的实现方式:
先说说你之前代码的问题
- 第一种方法的错误:
socket.listen的回调参数是socket的状态事件(比如RawSocketEvent.read),你不需要在回调里再调用socket.receive()——正确的做法是当事件为RawSocketEvent.read时,再调用receive()获取Datagram。另外,listen是异步执行的,你直接返回列表的话,此时还没收到任何数据,自然是空列表。 - 第二种方法的错误:先调用
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
相关产品推荐
相关产品推荐

