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

C# UdpClient监听器批量发送UDP报文时无法接收全部数据包如何解决

UDP大量发送时丢包问题解决方案

问题根因

你遇到的丢包核心来自两方面:一是UDP协议本身不保证可靠交付,二是现有收发两端的逻辑都存在性能瓶颈,瞬间高并发流量会打满内核套接字缓冲区,直接触发内核层面的丢包。


接收端优化

当前接收端的瓶颈在于:接收和慢IO操作(打印日志)耦合,且默认接收缓冲区过小,导致包积压后被丢弃。调整逻辑如下:

  • 调大UdpClient的接收缓冲区,默认值通常只有几十KB,大流量下很容易被打满,建议调整到8M~32M区间
  • 解耦接收和处理逻辑,收到包后先写入高性能队列/通道,后台单独线程处理打印、计数等慢操作,保证接收线程能以最快速度调用ReceiveAsync,避免缓冲区积压

优化后接收端代码示例:

// UdpMessageReceiver 优化版
using System.Threading.Channels;
using System.Net.Sockets;
using System.Text;

class Program
{
    static async Task Main(string[] args)
    {
        const int Port = 5001;
        // 初始化无界通道存储待处理的UDP消息
        var channel = Channel.CreateUnbounded<UdpReceiveResult>();
        // 启动后台处理任务
        _ = ProcessMessagesAsync(channel.Reader);
        // 启动监听
        await UdpListener(Port, channel.Writer);
    }

    public static async Task UdpListener(int port, ChannelWriter<UdpReceiveResult> writer)
    {
        var client = new UdpClient(port, AddressFamily.InterNetwork);
        // 调大接收缓冲区为16M,可根据包大小调整
        client.Client.ReceiveBufferSize = 1024 * 1024 * 16;
        while (true)
        {
            var receivedResult = await client.ReceiveAsync();
            // 直接写入通道,不执行任何慢操作
            await writer.WriteAsync(receivedResult);
        }
    }

    public static async Task ProcessMessagesAsync(ChannelReader<UdpReceiveResult> reader)
    {
        var receivedCount = 0;
        await foreach (var result in reader.ReadAllAsync())
        {
            var resultString = Encoding.ASCII.GetString(result.Buffer);
            receivedCount++;
            Console.WriteLine("Message: " + resultString);
            Console.WriteLine("Current Message Total: " + receivedCount);
        }
    }
}

发送端优化

当前发送端的问题是:瞬间全量并发发送,且每个包都新建UdpClient实例,不仅浪费资源,还会产生突发流量直接打满接收端缓冲区。调整逻辑如下:

  • 复用同一个UdpClient实例,避免频繁创建套接字、端口的开销
  • 控制发送并发量,不要一次性把所有包塞到网络栈,通过信号量控制同时发送的包数量,给接收端留足够的处理时间
  • 不要用Parallel.ForEach处理IO密集型异步任务,该API是为CPU密集型场景设计的,直接用普通循环+异步并发控制即可

优化后发送端代码示例:

// 发送端优化版
using System.Net.Sockets;
using System.Threading;

class Runner
{
    public static string Host = "127.0.0.1";
    public static int Port = 5001;
    // 控制最大并发发送数,可根据实际接收能力调整,当前设为100
    private static readonly SemaphoreSlim _sendSemaphore = new SemaphoreSlim(100);

    public static async Task SendAllMessagesAsync(List<Message> messages)
    {
        // 复用同一个UdpClient实例
        using var client = new UdpClient();
        client.Connect(Host, Port);
        var tasks = new List<Task>();
        foreach (var message in messages)
        {
            await _sendSemaphore.WaitAsync();
            tasks.Add(SendUdpMessageAsync(client, message));
        }
        await Task.WhenAll(tasks);
    }

    private static async Task SendUdpMessageAsync(UdpClient client, Message message)
    {
        try
        {
            await client.SendAsync(message._packet, message._packet.Length);
        }
        finally
        {
            _sendSemaphore.Release();
        }
    }
}

补充说明

如果调整后还是存在丢包,可以继续调大接收缓冲区,或者降低发送端的并发数。如果业务要求100%可靠交付,UDP本身无法实现,需要自行在应用层实现序号校验、应答、重传逻辑,或者直接改用TCP协议。

内容的提问来源于stack exchange,提问作者Ean Price

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 19:27:04