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

Windows下UDP监听异常处理:如何忽略OS错误持续运行

Windows下Dart UDP监听时忽略无效端口发送异常的解决方案

问题现象

在Windows 10系统中使用Dart的RawDatagramSocket绑定并监听UDP端口时,向无服务运行的端口发送数据会引发异常,且会导致监听终止,无法继续接收后续数据:

  1. 当先向有监听的端口(如9999)发送数据,再向无服务端口(如12345)发送时,监听会静默停止,无错误提示:
import 'dart:io';
import 'dart:convert';

void main() async {
  RawDatagramSocket.bind(InternetAddress.anyIPv4, 9999)
      .then((RawDatagramSocket socket) {
    socket.broadcastEnabled = true;
    socket.listen(
        (RawSocketEvent event) {
          if (event == RawSocketEvent.read) {
            Datagram? datagram = socket.receive();
            if (datagram != null) {
              String message = utf8.decode(datagram.data);
              print(
                  'Received: $message from ${datagram.address.address}:${datagram.port}');
            }
          }
        },
        onError: (e, s) {
          print('Error: $e');
        },
        cancelOnError: false,
        onDone: () {
          print('Done');
        });
    socket.send(utf8.encode("hello"), InternetAddress('127.0.0.1'), 9999);
    socket.send(utf8.encode("hello"), InternetAddress('127.0.0.1'), 9999);
    socket.send(utf8.encode("hello"), InternetAddress('127.0.0.1'), 12345);
    socket.send(utf8.encode("hello"), InternetAddress('127.0.0.1'), 9999);
  });
}

执行输出:

Received: hello from 127.0.0.1:9999
Received: hello from 127.0.0.1:9999

最后一次向9999发送的数据无法被接收,监听已停止。

  1. 若直接向无服务端口发送,会直接抛出异常导致程序终止:
import 'dart:io';
import 'dart:convert';

void main() async {
  RawDatagramSocket.bind(InternetAddress.anyIPv4, 9999)
      .then((RawDatagramSocket socket) {
    socket.broadcastEnabled = true;
    socket.listen(
        (RawSocketEvent event) {
          if (event == RawSocketEvent.read) {
            Datagram? datagram = socket.receive();
            if (datagram != null) {
              String message = utf8.decode(datagram.data);
              print(
                  'Received: $message from ${datagram.address.address}:${datagram.port}');
            }
          }
        },
        onError: (e, s) {
          print('Error: $e');
        },
        cancelOnError: false,
        onDone: () {
          print('Done');
        });
    // socket.send(utf8.encode("hello"), InternetAddress('127.0.0.1'), 9999);
    // socket.send(utf8.encode("hello"), InternetAddress('127.0.0.1'), 9999);
    socket.send(utf8.encode("hello"), InternetAddress('127.0.0.1'), 12345);
    socket.send(utf8.encode("hello"), InternetAddress('127.0.0.1'), 9999);
  });
}

执行输出:

Error: SocketException: No service is operating at the destination network endpoint on the remote system
 (OS Error: No service is operating at the destination network endpoint on the remote system。
, errno = 1234), address = 0.0.0.0, port = 9999

问题原因

这是Windows平台特有的UDP错误反馈机制导致的:当向无监听的UDP端口发送数据时,目标系统会返回ICMP「端口不可达」错误,该错误会关联到发送数据的RawDatagramSocket。如果这个socket同时是监听socket,错误会导致监听逻辑终止,甚至直接抛出异常终止程序。

解决方案

核心思路是分离监听与发送的socket:主socket仅负责监听UDP数据,发送操作使用临时创建的socket完成。这样即使发送到无效端口引发异常,也只会影响临时socket,不会中断主监听逻辑。

优化后的实现代码

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

// 发送UDP数据的工具方法,使用临时socket
Future<void> sendUdpMessage(String message, String ip, int port) async {
  try {
    // 创建临时socket,绑定任意可用端口(Windows允许多个UDP socket绑定同端口)
    final tempSocket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0);
    tempSocket.send(utf8.encode(message), InternetAddress(ip), port);
    tempSocket.close();
  } catch (e) {
    // 捕获并忽略发送异常,不影响主监听
    print('发送失败:$e');
  }
}

void main() async {
  // 主socket仅负责监听
  final mainSocket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 9999);
  mainSocket.broadcastEnabled = true;
  
  mainSocket.listen(
    (RawSocketEvent event) {
      if (event == RawSocketEvent.read) {
        final datagram = mainSocket.receive();
        if (datagram != null) {
          final message = utf8.decode(datagram.data);
          print('Received: $message from ${datagram.address.address}:${datagram.port}');
        }
      }
    },
    onError: (e, s) {
      print('监听错误:$e');
    },
    cancelOnError: false,
    onDone: () {
      print('监听结束');
    }
  );

  // 执行发送操作,使用临时socket
  await sendUdpMessage("hello", '127.0.0.1', 9999);
  await sendUdpMessage("hello", '127.0.0.1', 9999);
  await sendUdpMessage("hello", '127.0.0.1', 12345); // 向无效端口发送,仅临时socket受影响
  await sendUdpMessage("hello", '127.0.0.1', 9999); // 主监听仍能接收该数据
}

代码说明

  1. 分离职责:主mainSocket只处理监听逻辑,不参与发送操作,避免发送错误影响监听。
  2. 临时发送socket:sendUdpMessage方法中创建临时socket完成发送,发送后立即关闭。即使发送到无效端口抛出异常,也会被try-catch捕获,不会影响主程序。
  3. 异常捕获:在发送方法内捕获异常,可以选择打印日志或静默忽略,不中断主监听流程。

效果验证

执行上述代码后,即使向无服务端口12345发送数据,主监听仍能正常接收后续发送到9999的数据,输出如下:

Received: hello from 127.0.0.1:9999
Received: hello from 127.0.0.1:9999
发送失败:SocketException: No service is operating at the destination network endpoint on the remote system (OS Error: No service is operating at the destination network endpoint on the remote system。, errno = 1234), address = 0.0.0.0, port = 0
Received: hello from 127.0.0.1:9999

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 08:04:53