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

C#中如何使用UdpClient类接收多个UDP数据包?

问题原因与解决方法

核心问题

  1. 服务器接收逻辑不足:你仅调用了一次client.Receive(ref endPoint),而UDP是无连接协议,每次Receive只能读取一个数据包,因此只能拿到第一个分片。
  2. 客户端分片不完整:用buffer.Length / buffMax计算循环次数是整数除法,若文件字节数不是buffMax的整数倍,最后一部分数据会被直接丢弃,无法发送。
  3. UDP无可靠性保障:UDP不保证数据包的送达顺序、不丢包、不重复,你的代码没有任何校验与控制逻辑,即便服务器循环接收,也可能因丢包、乱序导致文件损坏。

修正方案

1. 修复客户端分片发送逻辑

补全最后一部分数据的发送,同时给每个数据包添加头部信息(分片序号、总分片数),让服务器能识别接收进度:

private void WorkOnBuffer(byte[] buffer)
{
    int buffMax = 64000;
    // 计算总分片数,用Math.Ceiling确保最后一片被包含
    int totalSegments = (int)Math.Ceiling((double)buffer.Length / buffMax);
    
    for (int i = 0; i < totalSegments; i++)
    {
        // 计算当前分片的实际长度:最后一片可能小于buffMax
        int currentLength = i == totalSegments - 1 ? buffer.Length - i * buffMax : buffMax;
        byte[] subBuffer = new byte[currentLength];
        Array.Copy(buffer, i * buffMax, subBuffer, 0, currentLength);
        
        // 构造带头部的数据包:前4字节存分片序号,后4字节存总分片数
        byte[] sendBuffer = new byte[8 + currentLength];
        BitConverter.GetBytes(i).CopyTo(sendBuffer, 0);
        BitConverter.GetBytes(totalSegments).CopyTo(sendBuffer, 4);
        subBuffer.CopyTo(sendBuffer, 8);
        
        client.Send(sendBuffer, sendBuffer.Length);
        // 可选:添加微小延迟,避免发送过快导致服务器接收压力
        Thread.Sleep(10);
    }
}

2. 服务器端循环接收并重组数据

服务器需要循环调用Receive,根据头部信息判断是否接收完所有分片,再重组完整数据:

// 存储已接收的分片,key为分片序号,value为分片数据
Dictionary<int, byte[]> receivedSegments = new Dictionary<int, byte[]>();
int totalSegments = -1;

while (true)
{
    byte[] receivedBuffer = client.Receive(ref endPoint);
    if (receivedBuffer.Length < 8) continue; // 跳过无效数据包
    
    // 解析头部信息
    int segmentIndex = BitConverter.ToInt32(receivedBuffer, 0);
    totalSegments = BitConverter.ToInt32(receivedBuffer, 4);
    byte[] segmentData = new byte[receivedBuffer.Length - 8];
    Array.Copy(receivedBuffer, 8, segmentData, 0, segmentData.Length);
    
    // 存储分片,避免重复接收
    if (!receivedSegments.ContainsKey(segmentIndex))
    {
        receivedSegments.Add(segmentIndex, segmentData);
    }
    
    // 判断是否所有分片都已接收
    if (receivedSegments.Count == totalSegments)
    {
        break;
    }
}

// 重组完整文件字节数组
int totalLength = 0;
foreach (var seg in receivedSegments.Values)
{
    totalLength += seg.Length;
}
byte[] finalBuffer = new byte[totalLength];
int offset = 0;
for (int i = 0; i < totalSegments; i++)
{
    byte[] seg = receivedSegments[i];
    seg.CopyTo(finalBuffer, offset);
    offset += seg.Length;
}

// 此处可将finalBuffer写入文件或进行其他处理

额外注意事项

  • 丢包处理:上述代码未处理丢包,实际场景中需添加超时重传机制——服务器若超时未收全分片,向客户端发送重传请求;客户端需保留分片数据直到确认服务器接收完成。
  • 顺序问题:通过分片序号重组数据,即便数据包乱序到达,也能正确拼接。
  • 数据包大小限制:UDP数据包最大长度一般不超过65507字节,buffMax=64000的设置合理,加上头部也不会超限。

内容的提问来源于stack exchange,提问作者Gabriele Troviso

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 22:12:45