UDP通信用 UdpClient
还是 Socket
?
日常开发中优先用
UdpClient,它封装了底层
Socket的细节,写法简洁、不易出错;只有需要精细控制(如设置
IP_PKTINFO、绑定到任意接口、复用端口等)时才直接操作
Socket。
注意:
UdpClient默认不支持异步接收多个包的并发处理(单次
Receive阻塞),若需高吞吐或长连接模型,得配合线程/任务或改用
Socket.ReceiveFromAsync。
UdpClient构造后自动创建并绑定
Socket,调用
Close()或
Dispose()才释放资源 直接用
Socket时,必须显式调用
Bind(),且协议类型要设为
SocketType.Dgram+
ProtocolType.Udp
UdpClient的
Client属性可拿到底层
Socket实例,用于做高级配置(如
SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true))
发送 UDP 包:UdpClient.Send
的关键参数
发送前不用手动指定目标 IP 端口——
UdpClient支持两种模式:一种是构造时绑定本地端口(用于接收),发送时用
Send(byte[], int, IPEndPoint)显式传目标地址;另一种是用
Connect(IPEndPoint)后直接调用
Send(byte[], int),此时目标地址被“固定”,后续所有
Send都发往该地址。
容易踩的坑:
Connect()后不能再接收其他地址的数据(底层
Socket被限制为“已连接”状态),且
Receive()会抛
SocketException(错误码 10057)。 目标
IPEndPoint必须含有效 IPv4/IPv6 地址和非零端口,
IPAddress.Any或
IPAddress.IPv6Any是非法目标地址 发送缓冲区大小受系统 UDP 缓冲区限制(通常 64KB 左右),超长数据会被静默截断,不报错也不抛异常 无连接特性意味着发送成功 ≠ 对方收到,也无重传、确认机制
接收 UDP 包:如何避免 Receive
阻塞主线程?
UdpClient.Receive是同步阻塞调用,直接放在 UI 线程或主循环里会导致卡死。正确做法是用单独线程、
Task.Run或基于
SocketAsyncEventArgs的异步模型。
最简可行方案是启动一个后台任务持续接收:
var client = new UdpClient(8080);
_ = Task.Run(async () =>
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
var result = await client.ReceiveAsync();
Console.WriteLine($"Received {result.Buffer.Length} bytes from {result.RemoteEndPoint}");
}
catch (ObjectDisposedException) { break; }
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.Interrupted) { break; }
}
});
注意:
UdpClient不提供原生
ReceiveAsync(.NET 6+ 才有),旧版本必须用
BeginReceive/
EndReceive或转到底层
Socket调用
ReceiveFromAsync。
防火墙与端口绑定常见失败原因
运行时提示“通常每个套接字地址(协议/网络地址/端口)只允许使用一次”(错误码 10048),大概率是端口被占用或未设
ReuseAddress;提示“由于目标计算机积极拒绝,无法连接”(10061),通常是目标机器没开监听,或 Windows 防火墙拦截了入站 UDP 流量。 绑定
0.0.0.0:8080表示监听本机所有 IPv4 接口,但 Windows 默认阻止外部访问,需在防火墙中放行该端口(入站规则) 用
IPAddress.Loopback(即
127.0.0.1)只能收本机发的包,跨机器通信必须用实际网卡 IP 或
IPAddress.Any多个进程不能同时
Bind到同一端口,除非都设置了
SocketOptionName.ReuseAddress且至少一个启用了
SO_EXCLUSIVEADDRUSE(Windows)或
SO_REUSEPORT(Linux/macOS)
UDP 没有连接状态,所以抓包看到 SYN/RST 等 TCP 标志位一定不是 UDP 流量;调试时优先用
netstat -uanp(Linux)或
Get-NetUDPEndpoint(PowerShell)确认端口是否真在监听。
