Я использую WinHTTP API в коде C ++, похожем на тот, что внизу Эта статья. Он запускается из моей службы Windows и используется для загрузки обновлений в фоновом режиме. Код работает нормально, за исключением того, что я получил жалобы на то, что при загрузке обновления этот код использует слишком большую полосу пропускания, доступную на клиентском компьютере.
Есть ли способ сделать эти WinHTTP API, 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);
РЕДАКТИРОВАТЬ: Следуя предложениям @RemyLebeau, я создал тестовый проект C ++ (вы можете скачать его отсюда), который пытается вычислить текущую скорость загрузки, использованную вышеописанным способом, и использует «Sleep» API для регулирования самого себя. К сожалению, результаты, которые я получаю от этого, довольно неожиданны. Я сделал скриншот:
Посмотрите разницу между моим чтением и тем, что дает мне диспетчер задач. (Обратите внимание, что ничто не использовало пропускную способность в то время, когда я выполнял эти тесты.)
Я должен что-то упустить. Вопрос в том, что?
Не всегда легко регулировать «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() );
}