ZetCode

Dart RawDatagramSocket

最后修改于 2025 年 4 月 4 日

Dart 中的 RawDatagramSocket 类提供了 UDP 套接字功能。它允许通过 IP 网络发送和接收数据报(UDP 数据包)。

与 TCP 套接字不同,UDP 是无连接的,不保证送达。RawDatagramSocket 是 Dart dart:io 库的一部分,用于 I/O 操作。

基本定义

RawDatagramSocket 表示一个可以绑定到端口的 UDP 套接字。它支持 IPv4 和 IPv6 地址,并提供基于事件的 I/O。

主要功能包括发送/接收数据报、组播支持和端口绑定。它对于游戏和流媒体等低延迟应用程序非常有用。

基本的 UDP 回显服务器

此示例展示了一个简单的 UDP 回显服务器,它可以将接收到的数据包发送回去。

main.dart
import 'dart:io';

void main() async {
  var socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 8888);
  print('UDP echo server listening on ${socket.address}:${socket.port}');

  socket.listen((RawSocketEvent event) {
    if (event == RawSocketEvent.read) {
      var datagram = socket.receive();
      if (datagram != null) {
        print('Received from ${datagram.address}:${datagram.port}');
        socket.send(datagram.data, datagram.address, datagram.port);
      }
    }
  });
}

我们将服务器绑定到端口 8888 上的任何 IPv4 地址,并监听传入的数据报。当数据到达时,我们打印发送者信息并将数据回显回去。

$ dart main.dart
UDP echo server listening on 0.0.0.0:8888
Received from 127.0.0.1:54321

UDP 客户端示例

此示例演示了一个向服务器发送消息的 UDP 客户端。

main.dart
import 'dart:io';

void main() async {
  var socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0);
  print('Client bound to port ${socket.port}');

  var message = 'Hello UDP';
  var data = message.codeUnits;
  var server = InternetAddress.loopbackIPv4;
  var port = 8888;

  socket.send(data, server, port);
  print('Sent "$message" to $server:$port');

  socket.listen((event) {
    if (event == RawSocketEvent.read) {
      var datagram = socket.receive();
      if (datagram != null) {
        print('Received: ${String.fromCharCodes(datagram.data)}');
      }
    }
  });
}

客户端绑定到一个随机端口(0),将消息发送到服务器,然后等待响应。在通过 UDP 发送之前,我们将字符串转换为字节。

$ dart main.dart
Client bound to port 54321
Sent "Hello UDP" to 127.0.0.1:8888
Received: Hello UDP

广播 UDP 数据包

此示例展示了如何将 UDP 广播数据包发送到网络上的所有主机。

main.dart
import 'dart:io';

void main() async {
  var socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0);
  socket.broadcastEnabled = true;

  var message = 'Broadcast message';
  var data = message.codeUnits;
  var port = 8888;

  socket.send(data, InternetAddress('255.255.255.255'), port);
  print('Broadcast sent to port $port');

  socket.close();
}

我们启用广播并将数据发送到特殊的广播地址 255.255.255.255。这将数据包发送到本地网络段上的所有主机。

$ dart main.dart
Broadcast sent to port 8888

组播 UDP 示例

此示例演示了加入组播组并接收数据包。

main.dart
import 'dart:io';

void main() async {
  var socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 8888);
  var multicast = InternetAddress('239.1.2.3');
  
  socket.joinMulticast(multicast);
  print('Joined multicast group $multicast');

  socket.listen((event) {
    if (event == RawSocketEvent.read) {
      var datagram = socket.receive();
      if (datagram != null) {
        print('Multicast from ${datagram.address}: ${String.fromCharCodes(datagram.data)}');
      }
    }
  });
}

我们加入组播组 239.1.2.3 并监听传入的数据包。组播允许在网络上进行高效的一对多通信。

$ dart main.dart
Joined multicast group 239.1.2.3
Multicast from 192.168.1.100: Test multicast message

处理错误和超时

此示例展示了 UDP 套接字的错误处理和超时配置。

main.dart
import 'dart:io';
import 'dart:async';

void main() async {
  try {
    var socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 8888)
      .timeout(Duration(seconds: 5));
    
    socket.handleError((error) {
      print('Socket error: $error');
    });

    socket.listen((event) {
      if (event == RawSocketEvent.read) {
        var datagram = socket.receive();
        print('Received data');
      }
      if (event == RawSocketEvent.closed) {
        print('Socket closed');
      }
    });

    Timer(Duration(seconds: 10), () {
      socket.close();
    });
  } on TimeoutException {
    print('Failed to bind socket within 5 seconds');
  } on SocketException catch (e) {
    print('Socket exception: ${e.message}');
  }
}

我们为套接字绑定添加超时、错误处理程序以及自动套接字关闭。这使得 UDP 通信在实际场景中更加健壮。

$ dart main.dart
Socket closed

最佳实践

来源

Dart RawDatagramSocket 文档

本教程介绍了 Dart 的 RawDatagramSocket 类,并提供了实际示例,展示了包括单播、广播和组播在内的 UDP 通信模式。

作者

我叫 Jan Bodnar,是一名充满激情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出 所有 Dart 教程