Передача данных UDP медленнее, чем TCP - PullRequest
1 голос
/ 16 февраля 2012

В настоящее время я пишу прототип приложения на C # /. Net4, где мне нужно передать неизвестное количество данных. Данные считываются из текстового файла и затем сериализуются в байтовый массив. Теперь мне нужно реализовать оба способа передачи, UDP и TCP. Передача в обоих направлениях работает нормально, но у меня есть некоторые проблемы с UDP. Я предполагаю, что передача с использованием UDP должна быть намного быстрее, чем с использованием TCP, но на самом деле мои тесты показали, что передача UDP примерно в 7-8 раз медленнее, чем с использованием TCP. Я протестировал передачу с 12-мегабайтным файлом, и передача TCP заняла около 1 секунды, тогда как передача UDP заняла около 7 секунд. В приложении я использую простые сокеты для передачи данных. Поскольку протокол UDP допускает максимум 65535 КБ на сообщение, я разделил сериализованный байтовый массив файла на несколько частей, каждая из которых имеет размер 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.

спасибо заранее!

Ответы [ 2 ]

3 голосов
/ 16 февраля 2012

Хотя размер дейтаграммы UDP может составлять до 64 КБ, фактические размеры проводных кадров обычно составляют 1500 байт (обычный Ethernet MTU ).Это также должно соответствовать IP-заголовку размером не менее 20 байтов и UDP-заголовку размером 8 байтов, чтобы вы могли использовать полезную нагрузку в 1472 байта. фрагментация дейтаграмм UDP на стороне отправителя и затем повторная сборка их на стороне получателя.Это требует времени, поэтому ваши результаты.

TCP, с другой стороны, выполняет свою собственную пакетировку и пытается найти путь MTU , так что в этом случае он более эффективен.

Ограничьте ваши порции данных до 1472 байта и измерьте снова.

0 голосов
/ 16 февраля 2012

Я думаю, что вы должны измерять загрузку ЦП и пропускную способность сети на время теста.

Если ЦП привязан, это ваша проблема: Включите профилировщик.

Еслисеть (кабель) привязана, это другой класс проблем.Я бы не знал, что с этим делать; -)

Если ни один из них не привязан, запустите профилировщик и посмотрите, на что тратится большинство настенных часов.Должно быть какое-то ожидание.

Если у вас нет профилировщика, просто нажмите 10 раз на break в отладчике и посмотрите, где он останавливается чаще всего.

Редактировать: Мой ответ на ваш запросИзмерение: мы знаем, что 99% всего времени выполнения тратится на получение данных.Но мы пока не знаем, занят ли процессор.Посмотрите в диспетчере задач и посмотрите , какой процесс занят.

Я предполагаю, что это системный процесс.Это ядро ​​Windows и, вероятно, его компонент UDP.

Возможно, это связано с фрагментацией пакетов.IP-пакеты имеют определенный максимальный размер, например 1472 байта.Ваши UDP-пакеты фрагментируются и собираются на принимающей машине.Я удивлен, что так много времени занимает процессорное время.

Попробуйте отправить пакеты с общим размером 1000 и 1472 (попробуйте оба!) И сообщите результаты.

...