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 回显服务器,它可以将接收到的数据包发送回去。
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 客户端。
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 广播数据包发送到网络上的所有主机。
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 示例
此示例演示了加入组播组并接收数据包。
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 套接字的错误处理和超时配置。
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
最佳实践
- 错误处理:始终为套接字实现错误处理程序
- 端口选择:在客户端中使用 0 表示临时端口
- 缓冲区大小:UDP 有 MTU 限制(通常为 1500 字节)
- 组播 TTL:为组播数据包设置适当的 TTL
- 资源清理:完成后关闭套接字
来源
本教程介绍了 Dart 的 RawDatagramSocket 类,并提供了实际示例,展示了包括单播、广播和组播在内的 UDP 通信模式。
作者
列出 所有 Dart 教程。