C#中如何使用UdpClient类接收多个UDP数据包?
问题原因与解决方法
核心问题
- 服务器接收逻辑不足:你仅调用了一次
client.Receive(ref endPoint),而UDP是无连接协议,每次Receive只能读取一个数据包,因此只能拿到第一个分片。 - 客户端分片不完整:用
buffer.Length / buffMax计算循环次数是整数除法,若文件字节数不是buffMax的整数倍,最后一部分数据会被直接丢弃,无法发送。 - 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
相关产品推荐
相关产品推荐

