Flutter中RawDatagramSocket.listen无法接收UDP包问题排查
背景
我希望两个Flutter客户端通过dart:io的RawDatagramSocket实现UDP Socket数据互传,双方已知彼此的监听IP地址和端口。
当前问题
向同一局域网内的Flutter客户端发送UDP包时,socket.listen()方法无法接收数据包;但无论是从PowerShell窗口还是Flutter客户端向PowerShell UDP监听器发送数据包,都能被正常接收并读取。
Flutter客户端代码
以下客户端监听1338端口,发送数据包后listen无输出,说明未接收成功:
class ChatCubit extends Cubit<ChatState> { ChatCubit() : super(ChatLoading()); String addressValue = '目标机器IP'; late RawDatagramSocket udpSocket; Future main() async { udpSocket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 1338); udpSocket.broadcastEnabled = true; print('############# 监听消息中 #############'); udpSocket.listen((event) { print("recv e: $event"); if (event == RawSocketEvent.read) { print(utf8.decode(udpSocket.receive()!.data)); } }, onError: (error) { print("error: $error"); }, onDone: () { print("done!"); }, cancelOnError: true); print('${udpSocket.address.address}:${udpSocket.port}'); } void sendMessage(String basicMessage) async { print('############# 发送消息中 #############'); udpSocket.send(const Utf8Codec().encode(basicMessage), InternetAddress(addressValue), 1337); } }
PowerShell发送端脚本
使用该脚本向Flutter客户端发送UDP包,客户端无接收响应:
function Send-UdpDatagram { Param ([string] $EndPoint, [int] $Port, [string] $Message) $IP = [System.Net.Dns]::GetHostAddresses($EndPoint) $Address = [System.Net.IPAddress]::Parse($IP) $EndPoints = New-Object System.Net.IPEndPoint($Address, $Port) $Socket = New-Object System.Net.Sockets.UDPClient $EncodedText = [Text.Encoding]::ASCII.GetBytes($Message) $SendMessage = $Socket.Send($EncodedText, $EncodedText.Length, $EndPoints) $Socket.Close() }
执行命令:Send-UdpDatagram -EndPoint "目标机器IP" -Port 1338 -Message "test.mymetric:0|c"
PowerShell接收端脚本
该脚本监听1338端口,无论是Flutter客户端(监听1337、发送到1338)还是PowerShell发送端发送的数据包,都能成功接收:
param( $address="Any", $port=1338) try{ $endpoint = new-object System.Net.IPEndPoint( [IPAddress]::$address, $port ) $udpclient = new-object System.Net.Sockets.UdpClient $port } catch{ throw $_ exit -1 } Write-Host "按ESC停止UDP服务器 ..." -fore yellow Write-Host "" while( $true ) { if( $host.ui.RawUi.KeyAvailable ) { $key = $host.ui.RawUI.ReadKey( "NoEcho,IncludeKeyUp,IncludeKeyDown" ) if( $key.VirtualKeyCode -eq 27 ) { break } } if( $udpclient.Available ) { $content = $udpclient.Receive( [ref]$endpoint ) Write-Host "$($endpoint.Address.IPAddressToString):$($endpoint.Port) $([Text.Encoding]::ASCII.GetString($content))" } } $udpclient.Close( )
核心疑问
为何Flutter客户端或PowerShell向PowerShell监听器发送数据正常,但向Flutter客户端发送UDP包时,其监听却无法接收?
问题排查与解决方案
1. 防火墙拦截
这是最常见的原因:PowerShell作为系统自带程序,默认已被防火墙允许接收UDP数据包;而Flutter应用(尤其是桌面或移动应用)可能未被添加到防火墙的允许列表中,导致入站UDP流量被拦截。
解决方法:
- 打开系统防火墙设置,添加Flutter应用的入站规则,允许UDP端口1338的流量通过。
- 临时关闭防火墙测试,确认是否是防火墙导致的问题。
2. 网络权限缺失
如果是移动平台(Android/iOS)的Flutter应用,需要配置相应的网络权限才能接收UDP数据包:
- Android:在
AndroidManifest.xml中添加<uses-permission android:name="android.permission.INTERNET"/> - iOS:确保
Info.plist中配置了允许网络访问的权限(UDP不受ATS限制,但需应用具备基础网络权限)
3. 事件处理逻辑遗漏
RawDatagramSocket的listen回调会触发多种事件类型,除了RawSocketEvent.read,还可能有RawSocketEvent.closed、RawSocketEvent.write等。可以先打印所有事件类型,确认是否有数据包到达的相关事件:
udpSocket.listen((event) { print("触发事件: $event"); // 打印所有事件,确认是否有read事件 if (event == RawSocketEvent.read) { var datagram = udpSocket.receive(); if (datagram != null) { print("接收数据: ${utf8.decode(datagram.data)}"); } } });
另外,注意udpSocket.receive()会移除队列中的数据包,如果有多个read事件,确保每次都正确处理。
4. 编码不匹配(导致解码失败而非接收失败)
PowerShell发送端使用ASCII编码,而Flutter用utf8.decode解码。虽然这不会导致接收不到数据包,但可能会出现解码乱码,让你误以为没接收成功。可以统一编码:
- Flutter端改用
ascii.decode(datagram.data) - 或PowerShell端改用UTF8编码:
$EncodedText = [Text.Encoding]::UTF8.GetBytes($Message)
5. 绑定地址问题
虽然代码中用了InternetAddress.anyIPv4(即0.0.0.0,监听所有网卡),但某些系统环境下可能存在多个网卡,导致数据包未到达Flutter绑定的网卡。可以尝试绑定到具体的局域网IP而非anyIPv4:
udpSocket = await RawDatagramSocket.bind(InternetAddress("你的局域网IP"), 1338);
内容的提问来源于stack exchange,提问作者Angelo-Br

