2014-11-11 4 views
-1

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

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

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

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++ (можно download it from here), который пытается рассчитать текущую скорость загрузки, используемую вышеописанным методом, и использовать API «Сон» для дросселирования. К сожалению, результаты, которые я получаю от этого, довольно неожиданны. Я сделал скриншот:

enter image description here

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

Мне что-то не хватает. Вопрос в том, что?

+0

Если ваш размер буфера невелик (ish), вы можете спать поток в течение соответствующего времени, чтобы использование в течение продолжительности загрузки соответствовало полосе пропускания ~ 30%. – user1793036

+0

@ user1793036: Ну, я понимаю теорию. Можете ли вы показать, что вы имеете в виду в коде? – c00000fd

ответ

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, верно? – c00000fd

+0

А также в вашем коде вы используете то, что 'WinHttpReadData' возвращает в параметре' dwDownloaded'. Но что сказать, сервер возвращает 1 ГБ в один «большой глоток»? Может ли возникнуть необходимость разбить его на «1000/8» байтовые куски буфера? – c00000fd

+0

И еще одна вещь, которую я только что заметил. Когда вы вычисляете свой 'SleepInterval', вы не затягиваете время, затрачиваемое на загрузку байтов' dwDownloaded', чтобы получить пропускную способность 'kbs'. Поэтому я не выполняю эту часть ... – c00000fd