Несколько асинхронных запросов WinHTTP

Мне нужно получить данные примерно с 6000 страниц веб-сайта. Проведя небольшое исследование, я решил попробовать WinHTTP. Мне удалось заставить это работать, однако я делал все синхронно, поэтому для завершения потребовалось некоторое время. Сейчас я пытаюсь использовать WinHTTP асинхронно, но столкнулся с препятствием. Я искал несколько руководств и примеров, но смог найти только документацию MSDN, которая кажется слишком сложной для того, чем я занимаюсь. Как уже упоминалось, я не смог найти много ресурсов, поэтому я решил попробовать:

std::string theSource = "";
char * httpBuffer;
DWORD dwSize = 1;
DWORD dwRecv = 1;

HINTERNET hOpen = 
           WinHttpOpen
           (
               L"Example Agent", 
               WINHTTP_ACCESS_TYPE_NO_PROXY, 
               NULL, 
               NULL, 
               WINHTTP_FLAG_ASYNC
           );

WINHTTP_STATUS_CALLBACK theCallback = 
           WinHttpSetStatusCallback
           (
               hOpen, 
               (WINHTTP_STATUS_CALLBACK) HttpCallback,
               WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS,
               NULL
           );

HINTERNET hConnect = 
           WinHttpConnect
           (
               hOpen, 
               L"example.org",
               INTERNET_DEFAULT_HTTPS_PORT, 
               0
           );

HINTERNET hRequest = NULL;

BOOL allComplete = false;

int theRequest = 1;


while (!allComplete)
{
    if (theRequest == 1)
    {
        hRequest = WinHttpOpenRequest
                   (
                       hConnect,
                       L"GET", 
                       L"example.html",
                       0,
                       WINHTTP_NO_REFERER, 
                       WINHTTP_DEFAULT_ACCEPT_TYPES, 
                       WINHTTP_FLAG_SECURE
                   );


        WinHttpSendRequest
        (
            hRequest, 
            WINHTTP_NO_ADDITIONAL_HEADERS, 
            0, 
            WINHTTP_NO_REQUEST_DATA, 
            0, 
            0, 
            0
        );
    }

    else if (theRequest == 2)
    {
        WinHttpReceiveResponse(hRequest, NULL);
    }

    else if (theRequest == 3)
    {
        WinHttpQueryHeaders
        (
            hRequest, 
            WINHTTP_QUERY_RAW_HEADERS_CRLF, 
            WINHTTP_HEADER_NAME_BY_INDEX, 
            NULL, 
            &dwSize, 
            WINHTTP_NO_HEADER_INDEX
        );

        WCHAR * headerBuffer = new WCHAR[dwSize/sizeof(WCHAR)];

        WinHttpQueryHeaders
        (
            hRequest, 
            WINHTTP_QUERY_RAW_HEADERS_CRLF, 
            WINHTTP_HEADER_NAME_BY_INDEX, 
            headerBuffer, 
            &dwSize, 
            WINHTTP_NO_HEADER_INDEX
        );

        delete [] headerBuffer;

        dwSize = 1;

        while (dwSize > 0)
        {
            if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
            {
                break;
            }

            httpBuffer = new char[dwSize + 1];

            ZeroMemory(httpBuffer, dwSize + 1);

            if (!WinHttpReadData(hRequest, httpBuffer, dwSize, &dwRecv))
            {
                std::cout << "WinHttpReadData() - Error Code: " << GetLastError() << "\n";
            }

        else
        {
            theSource = theSource + httpBuffer;
        }

        delete [] httpBuffer;

        // Parse the source for the data I'm looking for.

        break;

    }
}

Ниже моя функция обратного вызова:

void CALLBACK HttpCallback(HINTERNET hInternet, DWORD * dwContext, DWORD dwInternetStatus, void * lpvStatusInfo, DWORD dwStatusInfoLength)
{
    switch (dwInternetStatus)
    {
        default:
            std::cout << dwInternetStatus << "\n";
            break;

        case WINHTTP_CALLBACK_STATUS_HANDLE_CREATED:
            std::cout << "Handle created.\n";
            theRequest = 1;
            break;

        case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:
            std::cout << "Request sent.\n";
            theRequest = 2;
            break;

        case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:
            std::cout << "Response received.\n";
            theRequest = 3;
            break;

    }
}

Примечание. Я предоставил только этот раздел своего кода, поскольку он относится к моему вопросу/проблеме. Прошу прощения, если объявление переменной отсутствует.

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

В общем, вот краткое изложение моей проблемы: мне нужно сделать около 6000 GET-запросов, используя WinHTTP асинхронно. Я не совсем уверен, как это сделать, потому что я новичок в WinHTTP, поэтому я ищу самый простой (или, возможно, эффективный) способ работы с несколькими асинхронными запросами.


person Jamal Winters    schedule 18.05.2012    source источник
comment
Вы видели эту статью MSDN: Асинхронный WinHTTP? И, возможно, этот однопоточный, многопроцессорный и простой для понимания Perl API HTTP::Async может предоставить вам некоторое вдохновение о том, как действовать дальше.   -  person Lumi    schedule 19.05.2012


Ответы (1)


Вы должны повторить то, что вы делаете в while (!allComplete) { ... }, и таким образом снимать больше запросов. Вы можете повторно использовать hConnect, но вам нужно делать WinHttpOpenRequest для каждого запроса ресурсов.

person Roman R.    schedule 19.05.2012
comment
Не могли бы вы уточнить или предоставить пример кода? Последнее было бы наиболее полезным для меня, если это возможно. Я не вижу, как я могу включить дополнительные запросы в свой цикл и по-прежнему отслеживать их надлежащим образом. Я не смогу зависеть от значений theRequest для запроса заголовков или чтения данных, поскольку каждый запрос будет изменять это значение с помощью функции обратного вызова в любой момент времени на основе текущего кода. - person Jamal Winters; 19.05.2012
comment
theRequest не может быть глобальным в этом сценарии, он будет специфичен для конкретного hRequest. Поэтому вам понадобится своего рода набор структур со значениями theRequest + hRequest. - person Roman R.; 19.05.2012