Методы ограничения использования полосы пропускания с помощью WinHTTP API

0

Я использую API WinHTTP в коде C++, подобном тому, который находится внизу этой статьи. Он запускается из моей службы Windows и используется для загрузки обновлений в фоновом режиме. Код работает нормально, за исключением того, что я получил жалобы на то, что при загрузке обновления этот код использует слишком большую пропускную способность, доступную на клиентском компьютере.

Есть ли способ сделать эти API WinHTTP, WinHttpQueryDataAvailable и WinHttpReadData в частности, ограничить пропускную способность, которую они используют? Скажем, до 30% доступной пропускной способности.

PS. Для удобства ссылок я собираюсь скопировать код, который я имею в виду из статьи MSDN:

DWORD dwSize = 0;
DWORD dwDownloaded = 0;
LPSTR pszOutBuffer;
BOOL  bResults = FALSE;
HINTERNET  hSession = NULL, 
           hConnect = NULL,
           hRequest = NULL;

// Use WinHttpOpen to obtain a session handle.
hSession = WinHttpOpen( L"WinHTTP Example/1.0",  
                        WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
                        WINHTTP_NO_PROXY_NAME, 
                        WINHTTP_NO_PROXY_BYPASS, 0);

// Specify an HTTP server.
if (hSession)
    hConnect = WinHttpConnect( hSession, L"www.microsoft.com",
                               INTERNET_DEFAULT_HTTPS_PORT, 0);

// Create an HTTP request handle.
if (hConnect)
    hRequest = WinHttpOpenRequest( hConnect, L"GET", NULL,
                                   NULL, WINHTTP_NO_REFERER, 
                                   WINHTTP_DEFAULT_ACCEPT_TYPES, 
                                   WINHTTP_FLAG_SECURE);

// Send a request.
if (hRequest)
    bResults = WinHttpSendRequest( hRequest,
                                   WINHTTP_NO_ADDITIONAL_HEADERS,
                                   0, WINHTTP_NO_REQUEST_DATA, 0, 
                                   0, 0);


// End the request.
if (bResults)
    bResults = WinHttpReceiveResponse( hRequest, NULL);

// Keep checking for data until there is nothing left.
if (bResults)
{
    do 
    {
        // Check for available data.
        dwSize = 0;
        if (!WinHttpQueryDataAvailable( hRequest, &dwSize)) 
        {
            printf( "Error %u in WinHttpQueryDataAvailable.\n",
                    GetLastError());
            break;
        }

        // No more available data.
        if (!dwSize)
            break;

        // Allocate space for the buffer.
        pszOutBuffer = new char[dwSize+1];
        if (!pszOutBuffer)
        {
            printf("Out of memory\n");
            break;
        }

        // Read the Data.
        ZeroMemory(pszOutBuffer, dwSize+1);

        if (!WinHttpReadData( hRequest, (LPVOID)pszOutBuffer, 
                              dwSize, &dwDownloaded))
        {                                  
            printf( "Error %u in WinHttpReadData.\n", GetLastError());
        }
        else
        {
            printf("%s", pszOutBuffer);
        }

        // Free the memory allocated to the buffer.
        delete [] pszOutBuffer;

        // This condition should never be reached since WinHttpQueryDataAvailable
        // reported that there are bits to read.
        if (!dwDownloaded)
            break;

    } while (dwSize > 0);
}
else
{
    // Report any errors.
    printf( "Error %d has occurred.\n", GetLastError() );
}

// Close any open handles.
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);

EDIT: при выполнении рекомендаций @RemyLebeau я создал тестовый проект C++ (вы можете скачать его здесь), который пытается рассчитать текущую скорость загрузки, используемую вышеописанным методом, и использовать API "Сон" для дросселирования. К сожалению, результаты, которые я получаю от этого, довольно неожиданны. Я сделал скриншот:

Изображение 174551

Посмотрите разницу между моим чтением и тем, что дает мне диспетчер задач. (Обратите внимание, что ничто не использовало пропускную способность во время выполнения этих тестов.)

Я должен что-то упустить. Вопрос в том, что?

  • 0
    Если ваш размер буфера небольшой (ish), вы можете перевести поток в спящий режим на соответствующее время, чтобы использование в течение всей загрузки равнялось ~ 30% пропускной способности.
  • 0
    @ user1793036: Ну, я понимаю теорию. Можете ли вы показать, что вы имеете в виду в коде?
Теги:
winapi
winhttp
bandwidth-throttling

1 ответ

1

Не всегда легко дросселировать "30% доступной полосы пропускания", так как вам нужно будет знать, что такое "доступная полоса пропускания" на самом деле, и это может быть нелегко определить программируемо. Я предполагаю, что вы можете синхронизировать каждую итерацию цикла, чтобы рассчитать вероятную полосу пропускания, исходя из того, как долго проходит каждый считывание. Но это очень хорошо может колебаться, поскольку пропускная способность используется для других целей, и поскольку вы уменьшаете использование полосы пропускания, ваш расчет доступной полосы пропускания будет зависеть.

То, что чаще всего (и, как правило, проще) реализовать, - это дросселировать по желаемым "байтам на (милли) секунду". Вы не можете дросселировать WinHttpReadData(), но можете дросселировать, как часто вы его называете. Просто следите за количеством байтов, которые вы читаете, и спятите ваши итерации цикла, чтобы вы не читали слишком много байтов за нужную скорость дроссельной заслонки - спать дольше, чтобы замедляться, и спать меньше, чтобы ускорить, например:

// Keep checking for data until there is nothing left.
if (bResults)
{
    char *pszOutBuffer = NULL;
    DWORD dwOutBufferSize = 0;

    do 
    {
        // Check for available data.

        // RL: personally, I would not bother with WinHttpQueryDataAvailable()
        // at all.  Just allocate a fixed-size buffer and let WinHttpReadData()
        // tell you when there is no more data to read...

        dwSize = 0;
        if (!WinHttpQueryDataAvailable( hRequest, &dwSize)) 
        {
            printf( "Error %u in WinHttpQueryDataAvailable.\n", GetLastError());
            break;
        }

        // No more available data.
        if (!dwSize)
            break;

        // (re)Allocate space for the buffer.
        if (dwSize > dwOutBufferSize)
        {
            delete [] pszOutBuffer;
            pszOutBuffer = NULL;
            dwOutBufferSize = 0;

            pszOutBuffer = new char[dwSize];
            if (!pszOutBuffer)
            {
                printf("Out of memory\n");
                break;
            }

            dwOutBufferSize = dwSize;
        }

        // Read the Data.
        DWORD dwStart = GetTickCount();
        if (!WinHttpReadData(hRequest, pszOutBuffer, dwSize, &dwDownloaded))
        {                                  
            printf("Error %u in WinHttpReadData.\n", GetLastError());
            break;
        }
        DWORD dwEnd = GetTickCount();
        DWORD dwDownloadTime = (dwEnd >= dwStart) ? (dwEnd - dwStart) : ((MAXDWORD-dwStart)+dwEnd);
        if (dwDownloadTime == 0) dwDownloadTime = 1;

        printf("%.*s", dwDownloaded, pszOutBuffer);

        // throttle by bits/sec
        //
        // todo: use a waitable object to sleep on, or use smaller
        // sleeps more often, if you need to abort a transfer in
        // progress during long sleeps...

        __int64 BitsPerSec = (__int64(dwDownloaded) * 8) * 1000) / dwDownloadTime;
        if (BitsPerSec > DesiredBitsPerSec)
            Sleep( ((BitsPerSec - DesiredBitsPerSec) * 1000) / DesiredBitsPerSec );
    }
    while (true);

    // Free the memory allocated to the buffer.
    delete [] pszOutBuffer;
}
else
{
    // Report any errors.
    printf( "Error %d has occurred.\n", GetLastError() );
}
  • 0
    Благодарю. Итак, вы используете 1000 бит для преобразования в kbs а не 1024, верно?
  • 0
    А также, в вашем коде вы используете все, что WinHttpReadData возвращает в параметре dwDownloaded . Но что, если, скажем, сервер возвращает 1 ГБ за один "большой глоток"? Может ли быть необходимость разбить его на 1000 / 8 байтовые куски буфера?
Показать ещё 7 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню