Вычисление сокета загружает скорость

Я задаюсь вопросом, знает ли кто-либо, как вычислить скорость загрузки сокета Беркли в C++. Мой отправлять вызов не блокирует и занимает 0,001 секунды для отправки 5 мегабайтов данных, но требует времени к recv ответ (таким образом, я знаю, что это загружает).

Это - сокет TCP к серверу HTTP, и я должен асинхронно проверить, сколько байтов данных было загружено / остаются. Однако я не могу найти API-функции для этого в Winsock, таким образом, я озадачен.

Любая справка значительно ценилась бы.

Править: Я нашел решение и буду отправлять как ответ как можно скорее!

РЕДАКТИРОВАНИЕ 2: Надлежащее решение, добавленное как ответ, будет добавлен как решение через 4 часа.

5
задан Saul 1 July 2010 в 20:30
поделиться

4 ответа

Я решил свою проблему благодаря bdolan , предложив уменьшить SO_SNDBUF . Однако, чтобы использовать этот код, вы должны отметить, что ваш код использует Winsock 2 (для перекрывающихся сокетов и WSASend ). В дополнение к этому ваш дескриптор SOCKET должен быть создан аналогично:

SOCKET sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);

Обратите внимание на флаг WSA_FLAG_OVERLAPPED в качестве последнего параметра.

В этом ответе я рассмотрю этапы загрузки данных на TCP-сервер и отслеживание каждого фрагмента загрузки и его состояния завершения. Эта концепция требует разделения буфера загрузки на части (требуется минимальная модификация существующего кода) и загрузки его по частям с последующим отслеживанием каждого фрагмента.

Мой поток кода

Глобальные переменные

Ваш документ с кодом должен иметь следующие глобальные переменные:

#define UPLOAD_CHUNK_SIZE 4096

int g_nUploadChunks = 0;
int g_nChunksCompleted = 0;
WSAOVERLAPPED *g_pSendOverlapped = NULL;
int g_nBytesSent = 0;
float g_flLastUploadTimeReset = 0.0f;

Примечание: в моих тестах уменьшение UPLOAD_CHUNK_SIZE приводит к повышению точности скорости загрузки , но снижает общую скорость загрузки. Увеличение UPLOAD_CHUNK_SIZE приводит к снижению точности скорости загрузки, но увеличивает общую скорость загрузки. 4 килобайта (4096 байт) были хорошим компромиссом для файла размером ~ 500 КБ.

Функция обратного вызова

Эта функция увеличивает количество отправленных байтов и завершенных переменных фрагментов (вызывается после того, как фрагмент был полностью загружен на сервер)

void CALLBACK SendCompletionCallback(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
    g_nChunksCompleted++;
    g_nBytesSent += cbTransferred;
}

Подготовить сокет

Первоначально сокет должен быть подготовлен путем сокращения SO_SNDBUF в 0.

Примечание: В моих тестах любое значение больше 0 приведет к нежелательному поведению.

int nSndBuf = 0;
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&nSndBuf, sizeof(nSndBuf));

Создать WSAOVERLAPPED массив

Массив из WSAOVERLAPPED структур должен быть создан для хранения перекрывающегося статуса всех наших загружаемых фрагментов. Для этого я просто:

// Calculate the amount of upload chunks we will have to create.
// nDataBytes is the size of data you wish to upload
g_nUploadChunks = ceil(nDataBytes / float(UPLOAD_CHUNK_SIZE));

// Overlapped array, should be delete'd after all uploads have completed
g_pSendOverlapped = new WSAOVERLAPPED[g_nUploadChunks];
memset(g_pSendOverlapped, 0, sizeof(WSAOVERLAPPED) * g_nUploadChunks);

Загрузить данные

Все данные, которые необходимо отправить, например, для целей, хранятся в переменной с именем pszData . Затем, используя WSASend , данные отправляются блоками, определенными константой UPLOAD_CHUNK_SIZE .

WSABUF dataBuf;
DWORD dwBytesSent = 0;
int err;
int i, j;

for(i = 0, j = 0; i < nDataBytes; i += UPLOAD_CHUNK_SIZE, j++)
{
    int nTransferBytes = min(nDataBytes - i, UPLOAD_CHUNK_SIZE);

    dataBuf.buf = &pszData[i];
    dataBuf.len = nTransferBytes;

    // Now upload the data
    int rc = WSASend(sock, &dataBuf, 1, &dwBytesSent, 0, &g_pSendOverlapped[j], SendCompletionCallback);

    if ((rc == SOCKET_ERROR) && (WSA_IO_PENDING != (err = WSAGetLastError())))
    {
        fprintf(stderr, "WSASend failed: %d\n", err);
        exit(EXIT_FAILURE);
    }
}

Ожидающая игра

Теперь мы можем делать все, что захотим, пока все фрагменты загружаются.

Примечание: поток, который вызвал WSASend , должен регулярно переводиться в тревожное состояние , чтобы наш обратный вызов «передача завершена» (SendCompletionCallback) был исключен из очереди Список APC (асинхронный вызов процедур).

В моем коде я постоянно выполнял цикл, пока g_nUploadChunks == g_nChunksCompleted . Это необходимо для отображения прогресса и скорости загрузки конечным пользователем (может быть изменено для отображения расчетного времени завершения, прошедшего времени и т. Д.)

Примечание 2: этот код использует Plat_FloatTime в качестве секунды счетчик, замените его любым вторым таймером, который использует ваш код (или отрегулируйте соответствующим образом)

g_flLastUploadTimeReset = Plat_FloatTime();

// Clear the line on the screen with some default data
printf("(0 chunks of %d) Upload speed: ???? KiB/sec", g_nUploadChunks);

// Keep looping until ALL upload chunks have completed
while(g_nChunksCompleted < g_nUploadChunks)
{
    // Wait for 10ms so then we aren't repeatedly updating the screen
    SleepEx(10, TRUE);

    // Updata chunk count
    printf("\r(%d chunks of %d) ", g_nChunksCompleted, g_nUploadChunks);

    // Not enough time passed?
    if(g_flLastUploadTimeReset + 1 > Plat_FloatTime())
        continue;

    // Reset timer
    g_flLastUploadTimeReset = Plat_FloatTime();

    // Calculate how many kibibytes have been transmitted in the last second
    float flByteRate = g_nBytesSent/1024.0f;
    printf("Upload speed: %.2f KiB/sec", flByteRate);

    // Reset byte count
    g_nBytesSent = 0;
}

// Delete overlapped data (not used anymore)
delete [] g_pSendOverlapped;

// Note that the transfer has completed
Msg("\nTransfer completed successfully!\n");

Заключение

Я действительно надеюсь, что это помогло кому-то в будущем, кто хотел рассчитать скорость загрузки на своих сокетах TCP без каких-либо изменений на стороне сервера . Я понятия не имею, насколько снижается производительность SO_SNDBUF = 0 , хотя я уверен, что гуру сокетов укажет на это.

5
ответ дан 14 December 2019 в 04:29
поделиться

Вы можете получить нижнюю границу объема полученных и подтвержденных данных, вычтя значение параметра сокета SO_SNDBUF из числа байтов, записанных в сокет. . Этот буфер можно настроить с помощью setsockopt , хотя в некоторых случаях ОС может выбрать длину меньше или больше, чем вы указали, поэтому вы должны повторно проверить ее после установки.

Однако, чтобы получить более точную информацию, удаленная сторона должна информировать вас о ходе выполнения, поскольку winsock не предоставляет API для получения объема данных, ожидающих в данный момент в буфере отправки.

В качестве альтернативы вы можете реализовать свой собственный транспортный протокол на UDP, но реализация управления скоростью для такого протокола может быть довольно сложной.

2
ответ дан 14 December 2019 в 04:29
поделиться

Поскольку у вас нет контроля над удаленной стороной, и вы хотите сделать это в коде, я бы предложил сделать очень простое приближение. Предполагаю долгоживущую программу / подключение. Одноразовые загрузки будут слишком искажены ARP, поиском DNS, буферизацией сокетов, медленным запуском TCP и т. Д. И т. Д.

Имеются два счетчика - длина ожидающей очереди в байтах (OB) и количество отправленных байтов (SB):

  • увеличивает OB на количество байтов, которые будут отправляться каждый раз, когда вы ставите блок в очередь для загрузки,
  • уменьшают OB и увеличивают SB на число, возвращенное из send (2) (по модулю -1 случаев),
  • на выборке таймера как OB, так и SB - либо сохранить их, регистрировать их или вычислять среднее значение,
  • вычислять невыполненные байты в секунду / минуту или что угодно, то же самое для отправленных байтов.

Сетевой стек выполняет буферизацию, а TCP выполняет повторную передачу и управление потоком, но на самом деле это не имеет значения. Эти два счетчика покажут вам скорость, с которой ваше приложение производит данные, и скорость, с которой оно может отправить их в сеть. Это не способ узнать реальную скорость ссылки, а способ сохранить полезные индикаторы того, насколько хорошо работает приложение.

Если скорость вывода данных ниже скорости вывода сети - все в порядке. Если все наоборот, и сеть не успевает за приложением - проблема - вам нужна либо более быстрая сеть, либо более медленное приложение, либо другой дизайн.

Для разовых экспериментов просто делайте периодические снимки вывода netstat -sp tcp (или чего-то еще, что есть в Windows) и вычисляйте скорость отправки вручную.

Надеюсь, это поможет.

1
ответ дан 14 December 2019 в 04:29
поделиться

Если ваше приложение использует заголовки пакетов, такие как

0001234DT

, где 000123 - длина пакета для одного пакета, вы можете рассмотреть возможность использования MSG_PEEK + recv (), чтобы получить длину пакета, прежде чем вы его фактически прочитаете с помощью recv ().

Проблема в том, что send () НЕ делает то, что вы думаете - он буферизуется ядром.

getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &flag, &sz));
fprintf(STDOUT, "%s: listener socket send buffer = %d\n", now(), flag);
sz=sizeof(int);
ERR_CHK(getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &flag, &sz));
fprintf(STDOUT, "%s: listener socket recv buffer = %d\n", now(), flag);

Посмотрите, что они показывают для вас.

Когда вы получаете доступ к неблокирующему сокету, который имеет данные, у него обычно нет МБ данных, припаркованных в буфере, готового к получению. Большая часть того, что я испытал, это то, что сокет имеет ~ 1500 байт данных на recv. Поскольку вы, вероятно, читаете блокирующий сокет, для завершения recv () требуется некоторое время.

Размер буфера сокета, вероятно, единственный лучший показатель пропускной способности сокета. setsockopt () позволяет изменять размер буфера сокета до определенного момента. Примечание: эти буферы совместно используются сокетами во многих операционных системах, таких как Solaris. Вы можете убить производительность, слишком сильно поворачивая эти настройки.

Кроме того, я не думаю, что вы измеряете то, что, по вашему мнению, вы измеряете. Реальная эффективность send () - это мера пропускной способности на конце recv (). Не конец send (). ИМО.

0
ответ дан 14 December 2019 в 04:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: