UDP数据传输比TCP慢

发布于 2025-01-05 11:45:23 字数 1967 浏览 0 评论 0原文

我目前正在 C#/.Net4 中编写一个原型应用程序,我需要传输未知量的数据。数据从文本文件中读入,然后序列化为字节数组。 现在我需要实现两种传输方法:UDP 和 TCP。两种方式的传输都工作正常,但我在 UDP 方面遇到了一些困难。我假设使用 UDP 的传输必须比使用 TCP 快得多,但实际上我的测试证明 UDP 传输比使用 TCP 慢大约 7 到 8 倍。 我用12兆字节的文件测试了传输,TCP传输大约需要1秒,而UDP传输大约需要7秒。 在应用程序中,我使用简单的套接字来传输数据。由于 UDP 只允许每条消息最大为 65535kb,因此我将文件的序列化字节数组分成几个部分,其中每个部分都有 socker SendBufferSize 的大小,然后使用 Socket.Send() 方法调用传输每个部分。

这是发送者部分的代码。

while (startOffset < data.Length)
{
    if ((startOffset + payloadSize) > data.Length)
    {
        payloadSize = data.Length - startOffset;
    }
    byte[] subMessageBytes = new byte[payloadSize + 16];
    byte[] messagePrefix = new UdpMessagePrefix(data.Length, payloadSize, messageCount, messageId).ToByteArray();
    Buffer.BlockCopy(messagePrefix, 0, subMessageBytes, 0, 16);
    Buffer.BlockCopy(data, startOffset, subMessageBytes, messageOffset, payloadSize);
    messageId++;
    startOffset += payloadSize;
    udpClient.Send(subMessageBytes, subMessageBytes.Length);
    messages.Add(subMessageBytes);
}

此代码只是将要发送的下一部分复制到字节数组中,然后调用套接字上的 send 方法。我的第一个猜测是,字节数组的分割/复制会降低性能,但我隔离并测试了分割代码,分割只花了几毫秒,所以这不会导致问题。

int receivedMessageCount = 1;
Dictionary<int, byte[]> receivedMessages = new Dictionary<int, byte[]>();
while (receivedMessageCount != totalMessageCount)
{
    byte[] data = udpClient.Receive(ref remoteIpEndPoint);
    UdpMessagePrefix p = UdpMessagePrefix.FromByteArray(data);
    receivedMessages.Add(p.MessageId, data);
    //Console.WriteLine("Received packet: " + receivedMessageCount + " (ID: " + p.MessageId + ")");
    receivedMessageCount++;
    //Console.WriteLine("ReceivedMessageCount: " + receivedMessageCount);
}
Console.WriteLine("Done...");
return receivedMessages;

这是我接收 UDP 消息的服务器端代码。每条消息都有一些字节作为前缀,其中存储消息总数和大小。所以我只是循环调用 socket.Receive ,直到收到前缀中指定的消息量。

我在这里的假设是,我可能实现的 UDP 传输代码不够“高效”...也许你们中的一个人已经在代码片段中发现了问题,或者对我有任何其他建议或提示,为什么我的 UDP 传输比TCP。

提前致谢!

I'm currently writing a prototype application in C#/.Net4 where i need to transfer an unknown amount of data. The data is read in from a text file and then serialized into a byte array.
Now i need to implement both transmission methods, UDP and TCP. The transmission in both ways does work fine but i have some struggleing with UDP. I assumend that the transmission using UDP have to be much faster than using TCP but in fact my tests proved that the UDP transmission is about 7 to 8 times slower than using TCP.
I tested the transmission with a 12 megabyte file and the TCP transmission took about 1 second whereas the UDP transmission took about 7 seconds.
In the application i use simple sockets to transmit the data. Since UDP does only allow a maximum of 65535kb per message, i splitted the serialized the byte array of the file into several parts where each part has the size of the socker SendBufferSize and then i transfer each part using Socket.Send() method call.

Here is the code for the Sender part.

while (startOffset < data.Length)
{
    if ((startOffset + payloadSize) > data.Length)
    {
        payloadSize = data.Length - startOffset;
    }
    byte[] subMessageBytes = new byte[payloadSize + 16];
    byte[] messagePrefix = new UdpMessagePrefix(data.Length, payloadSize, messageCount, messageId).ToByteArray();
    Buffer.BlockCopy(messagePrefix, 0, subMessageBytes, 0, 16);
    Buffer.BlockCopy(data, startOffset, subMessageBytes, messageOffset, payloadSize);
    messageId++;
    startOffset += payloadSize;
    udpClient.Send(subMessageBytes, subMessageBytes.Length);
    messages.Add(subMessageBytes);
}

This code does simply copy the next part to be send into an byte array and then call the send method on the socket. My first guess was, that the splitting/copying of the byte arrays was slowing down the performance, but i isolated and tested the splitting code and the splitting took only a few milliseconds, so that was not causing the problem.

int receivedMessageCount = 1;
Dictionary<int, byte[]> receivedMessages = new Dictionary<int, byte[]>();
while (receivedMessageCount != totalMessageCount)
{
    byte[] data = udpClient.Receive(ref remoteIpEndPoint);
    UdpMessagePrefix p = UdpMessagePrefix.FromByteArray(data);
    receivedMessages.Add(p.MessageId, data);
    //Console.WriteLine("Received packet: " + receivedMessageCount + " (ID: " + p.MessageId + ")");
    receivedMessageCount++;
    //Console.WriteLine("ReceivedMessageCount: " + receivedMessageCount);
}
Console.WriteLine("Done...");
return receivedMessages;

This is the server side code where i receive the UDP messages. Each message has some bytes as a prefix where the total number of messages is stored and the size. So i simply call socket.Receive in a loop until i received the amount of messages which were specified in the prefix.

My assumption here is that i may have implemented the UDP transmission code not "efficiently" enough... Maybe one of you guys already sees a problem in the code snippets or have any other suggestion or hint for me why my UDP transmission is slower than TCP.

thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

野の 2025-01-12 11:45:23

虽然 UDP 数据报大小可达 64K,但实际线帧通常为 1500 字节(普通以太网 MTU)。它还必须适合至少 20 字节的 IP 标头和 8 字节的 UDP 标头,从而为您留下 1472 字节的可用负载。

您所看到的是操作系统网络堆栈碎片的结果在发送方发送 UDP 数据报,然后在接收方重新组装它们。这需要时间,因此也需要你的结果。

另一方面,TCP 会进行自己的打包并尝试查找 路径 MTU,因此它更在这种情况下是有效的。

将数据块限制为 1472 字节并再次测量。

While UDP datagram size can be up to 64K, the actual wire frames are usually 1500 bytes (normal ethernet MTU). That also has to fit an IP header of minimum 20 bytes and a UDP header of 8 bytes, leaving you with 1472 bytes of usable payload.

What you are seeing is the result of your OS network stack fragmenting the UDP datagrams on the sender side and then re-assembling them on the receiver side. That takes time, thus your results.

TCP, on the other hand, does its own packetization and tries to find path MTU, so it's more efficient in this case.

Limit your data chunks to 1472 bytes and measure again.

韬韬不绝 2025-01-12 11:45:23

我认为你应该测量测试期间的 CPU 使用率和网络吞吐量。

如果 CPU 被固定,这就是您的问题:打开分析器。

如果网络(电缆)被固定,则这是另一类问题。我不知道该怎么办;-)

如果两者都没有固定,请运行分析器并查看大部分挂钟时间都花在哪里。一定还有一些等待。

如果您没有分析器,只需在调试器中按 10 次中断,然后查看最常停止的位置。

编辑:我对您的测量的回应:我们知道 99% 的执行时间都花在接收数据上。但我们还不知道 CPU 是否繁忙。查看任务管理器并查看哪个进程正忙。

我的猜测是它是系统进程。这是 Windows 内核,也可能是其中的 UDP 组件。

这可能与数据包碎片有关。 IP 数据包有一定的最大大小,例如 1472 字节。您的 UDP 数据包正在接收机器上被分段并重新组装。我很惊讶它占用了如此多的 CPU 时间。

尝试发送总大小为 1000 和 1472 的数据包(两者都尝试!)并报告结果。

I think you should measure CPU usage and network throughput for the duration of the test.

If the CPU is pegged, this is your problem: Turn on a profiler.

If the network (cable) is pegged this is a different class of problems. I wouldn't know what to do about it ;-)

If neither is pegged, run a profiler and see where most wall-clock time is spent. There must be some waiting going on.

If you don't have a profiler just hit break 10 times in the debugger and see where it stops most often.

Edit: My response to your measurement: We know that 99% of all execution time is spent in receiving data. But we don't know yet if the CPU is busy. Look into task-manager and look which process is busy.

My guess is it is the System process. This is the windows kernel and probably the UDP component of it.

This might have to do with packet fragmentation. IP packets have a certain maximum size like 1472 bytes. Your UDP packets are being fragmented and reassembled on the receiving machine. I am surprised that is taking so much CPU time.

Try sending packets of total size of 1000 and 1472 (try both!) and report the results.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文