C++ WinInet InternetCloseHandle 崩溃

Posted

技术标签:

【中文标题】C++ WinInet InternetCloseHandle 崩溃【英文标题】:C++ WinInet InternetCloseHandle crash 【发布时间】:2015-09-01 18:05:40 【问题描述】:

我编写了一个使用 WinInet 库的程序。该程序每天运行约 8-12 小时。首先它连接到互联网,然后它使用 FTP 下载/上传文件。之后它开始一个循环,在不同的时间间隔内最多启动两个线程。两个线程都在向同一台服务器发送GET-requests,看起来像这样:

private void Thread()

    if(!InternetGetConnectedState(NULL, NULL))
    
        connectToInternet();
    

    // some code...

    HINTERNET httpOpenRequest = HttpOpenRequest(
        hHTTPConnection,        // InternetConnect-Handle
        L"GET",                 // HTTP-Verb
        request_target,         // FileName
        L"HTTP/1.1",            // HTTP-Version
        NULL,                   // Referer
        NULL,                   // AcceptTypes
        INTERNET_FLAG_RELOAD,   // Flags
        0                       // Context
        );

    BOOL httpsRequest = HttpSendRequest(
            httpOpenRequest,    // Handle of HttpOpenRequest
            NULL,               // Headers
            0,                  // Headers-Length
            NULL,               // Optional
            0                   // Optional-Length
            );

    InternetCloseHandle(httpOpenRequest); // App-Crash sometimes here!

我的connectToInternet-函数在这里:

int connectToInternet()

    DWORD InetTest1 = 16000;
    while (InetTest1 != 0)
    
        InetTest1 = InternetAttemptConnect(0);
        // wait 1 second for next attempt
        if (InetTest1 != 0)
        
            Sleep(1000);
        
    

    BOOL InetTest2 = FALSE;
    while (!InetTest2)
    
        InetTest2 = InternetCheckConnection(
            L"http://www.example.com",  // URL
            FLAG_ICC_FORCE_CONNECTION,  // Flags
            0                           // Reserved
            );
        // wait 1 second for next attempt
        if (!InetTest2)
        
            Sleep(1000);
        
    

    while (hInternetOpen == NULL)
    
        hInternetOpen = InternetOpen(
            L"Custom-Agent",            // Agent
            INTERNET_OPEN_TYPE_DIRECT,  // AccessType
            NULL,                       // ProxyName
            NULL,                       // ProxyBypass
            0                           // Flags
            );

        // wait 1 second for next attempt
        if (hInternetOpen == NULL)
        
            Sleep(1000);
        
    

    while (hHTTPConnection == NULL)
    
        hHTTPConnection = InternetConnect(
            hInternetOpen,              // InternetOpen-Handle
            L"www.example.com",         // ServerName
            INTERNET_DEFAULT_HTTP_PORT, // ServerPort
            NULL,                       // Username
            NULL,                       // Password
            INTERNET_SERVICE_HTTP,      // Service
            0,                          // Flags
            0                           // Context
            );

        // wait 1 second for next attempt
        if (hHTTPConnection == NULL)
        
            Sleep(1000);
        
    

    return 0;

(我在这里找到了这个函数的基本工作流程:https://msdn.microsoft.com/en-us/library/windows/desktop/aa383996(v=vs.85).aspx)


现在问题:

Q1:当两个线程之一调用InternetCloseHandle(httpOpenRequest) 时,我似乎很少遇到应用程序崩溃。我找不到这些应用崩溃的原因...您有什么想法吗?

Q2: 运行程序的笔记本电脑似乎存在互联网连接问题,因为它经常会在几秒钟到几分钟甚至几小时内失去连接。因此,如果InternetGetConnectedState(NULL, NULL)false,我首先在两个线程中调用connectToInternet()。是否需要此步骤,或者如果我不调用连接函数,它是否也可以工作?如果连接中断,全局HINTERNET-Handles 是否无效?

[EDIT] 同时,我怀疑我的程序导致了连接问题,因为我在家中的有线网络上也没有互联网访问权限!我犯了什么错误吗?

【问题讨论】:

您在滥用GetLastError()。除非首先报告实际失败(InternetOpen() 返回 NULL,InternetConnect() 返回 NULL 等),否则请勿调用它,否则您可能会看到早期 API 调用的错误代码,这些错误代码与您正在调用的 API 无关。除非明确记录,大多数 API 函数不会在输入时重置 GetLastError(),或者如果成功则更新 GetLastError() @RemyLebeau 好的,谢谢,我会针对这种误用修改我的程序代码! @RemyLebeau 现在编辑了代码。关于错误的任何想法? 您有多个线程共享hInternetOpenhHTTPConnection 句柄,通过单个HTTP 连接在多个线程中发送HTTP 请求,而无需序列化对连接的访问​​。这是灾难的秘诀。因此,要么序列化对连接的访问​​以避免竞争条件,要么为每个线程提供自己的本地连接。 @RemyLebeau 好的,谢谢!如果我给每个线程自己的连接怎么样?如果同一个线程每 5 秒启动一次,会不会有问题?我所谓的“灾难食谱”会不会是问题 2 中所示的连接行为的原因? 【参考方案1】:

看来我的问题是多个线程同时运行并试图调用InternetCloseHandle()引起的。

与此同时,我重写了我的代码。我还尝试让Remy Lebeau 的cmets 参与进来。我的代码现在看起来像以下示例:

void Thread()

    // access-control
    if (ThreadRunning == true)
    
        return 99;
    
    ThreadRunning = true;

    // variables
    HINTERNET hInternetOpen = NULL;
    HINTERNET hHTTPConnection = NULL;
    HINTERNET httpOpenRequest = NULL;
    int connectionAttempts;
    string target_string;
    wstring target_wstring;
    LPCWSTR request_target;
    BOOL httpSendRequestSuccessful;
    BOOL httpQueryInfoReceived;
    DWORD statusCode = 0;
    DWORD statusCodeLen = sizeof(statusCode);
    BOOL dataAvailable;
    DWORD numberOfBytesAvailable;
    BOOL internetReadFileSuccessful;
    char buffer[4096] =  "" ;
    DWORD numberOfBytesRead;

    // Connect to Internet
    connectionAttempts = 0;
    DWORD InetTest1 = 16000;
    while (InetTest1 != 0)
    
        InetTest1 = InternetAttemptConnect(0);
        if (InetTest1 != 0)
        
            connectionAttempts++;
            // prevent an infinite loop
            if (connectionAttempts >= 5)
            
                // reset access-control
                ThreadRunning = false;

                return 1;
            
            // wait 1 second if it fails
            Sleep(1000);
        
    

    // reset connectionAttempts
    connectionAttempts = 0;
    BOOL InetTest2 = FALSE;
    while (!InetTest2)
    
        InetTest2 = InternetCheckConnection(
            L"http://www.example.com",  // URL
            FLAG_ICC_FORCE_CONNECTION,  // Flags
            0                           // Reserved
            );
        if (InetTest2 == FALSE)
        
            connectionAttempts++;
            // prevent an infinite loop
            if (connectionAttempts >= 5)
            
                // reset access-control
                ThreadRunning = false;

                return 2;
            
            // wait 1 second if it fails
            Sleep(1000);
        
    

    // reset connectionAttempts
    connectionAttempts = 0;
    while (hInternetOpen == NULL)
    
        hInternetOpen = InternetOpen(
            L"Custom-Agent",                // Agent
            INTERNET_OPEN_TYPE_DIRECT,      // AccessType
            NULL,                           // ProxyName
            NULL,                           // ProxyBypass
            0                               // Flags
            );

        if (hInternetOpen == NULL)
        
            connectionAttempts++;
            // prevent an infinite loop
            if (connectionAttempts >= 5)
            
                // reset access-control
                ThreadRunning = false;

                return 3;
            
            // wait 1 second if it fails
            Sleep(1000);
        
    

    // reset connectionAttempts
    connectionAttempts = 0;
    while (hHTTPConnection == NULL)
    
        hHTTPConnection = InternetConnect(
            hInternetOpen,              // InternetOpen-Handle
            L"www.example.com",         // ServerName
            INTERNET_DEFAULT_HTTP_PORT, // ServerPort
            NULL,                       // Username
            NULL,                       // Password
            INTERNET_SERVICE_HTTP,      // Service
            0,                          // Flags
            0                           // Context
            );
        if (hHTTPConnection == NULL)
        
            connectionAttempts++;
            // prevent an infinite loop
            if (connectionAttempts >= 5)
            
                // reset access-control
                onlineSettingsThreadRunning = false;

                // Handle-Cleanup
                InternetCloseHandle(hInternetOpen);

                return 4;
            
            // wait 1 second if it fails
            Sleep(1000);
        
    

    // some code...

    // open HTTP-Request
    httpOpenRequest = HttpOpenRequest(
        hHTTPConnection,        // InternetConnect-Handle
        L"GET",                 // HTTP-Verb (GET or POST)
        request_target,         // FileName
        L"HTTP/1.1",            // HTTP-Version
        NULL,                   // Referer
        NULL,                   // AcceptTypes
        INTERNET_FLAG_RELOAD,   // Flags
        0                       // Context
        );
    if (httpOpenRequest == NULL)
    
        // Handle-Cleanup
        InternetCloseHandle(hHTTPConnection);
        InternetCloseHandle(hInternetOpen);

        // reset access-control
        onlineSettingsThreadRunning = false;

        return 5;
    

    // send HTTP-Request
    httpSendRequestSuccessful = HttpSendRequest(
        httpOpenRequest,    // Handle von HttpOpenRequest
        NULL,               // Headers
        0,                  // Headers-Length
        NULL,               // Optional
        0                   // Optional-Length
        );
    if (httpSendRequestSuccessful == FALSE)
    
        // Handle-CleanUp
        InternetCloseHandle(httpOpenRequest);
        InternetCloseHandle(hHTTPConnection);
        InternetCloseHandle(hInternetOpen);

        // reset access-control
        onlineSettingsThreadRunning = false;

        return 6;
    

    // read Server-Status
    httpQueryInfoReceived = HttpQueryInfo(
        httpOpenRequest,
        HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
        &statusCode,
        &statusCodeLen,
        NULL
        );
    if (httpQueryInfoReceived == FALSE || statusCode != 200)
    
        // Handle-CleanUp
        InternetCloseHandle(httpOpenRequest);
        InternetCloseHandle(hHTTPConnection);
        InternetCloseHandle(hInternetOpen);

        // reset access-control
        onlineSettingsThreadRunning = false;

        return 7;
    

    // test, how much bytes are readable
    dataAvailable = InternetQueryDataAvailable(
        httpOpenRequest,
        &numberOfBytesAvailable,
        0,
        0
        );
    if (dataAvailable == FALSE)
    
        // Handle-CleanUp
        InternetCloseHandle(httpOpenRequest);
        InternetCloseHandle(hHTTPConnection);
        InternetCloseHandle(hInternetOpen);

        // reset access-control
        onlineSettingsThreadRunning = false;

        return 8;
    

    // some code...

    // Close internet-connection
    InternetCloseHandle(httpOpenRequest);
    InternetCloseHandle(hHTTPConnection);
    InternetCloseHandle(hInternetOpen);

    // reset access-control
    ThreadRunning = false;

    return 0;

所以我的connectToInternet()-function 不再存在。它现在是需要互联网访问的每个线程的一部分。它也不再使用GetLastError(),因为这可能会产生错误的结果...

我仍在测试我的代码,但它似乎很有希望:-)

【讨论】:

"它也不再使用 GetLastError(),因为这可能会产生错误的结果" - 只有当你错误地使用它时(你最初正在这样做)。跨度> @RemyLebeau 当然我的意思是我在我的第一个代码中使用该函数,而不是一般!

以上是关于C++ WinInet InternetCloseHandle 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

C++ wininet,连接weblogin,如何设置cookies?

C++ 使用 WinINet 上传到 FTP 服务器

C++ WinInet 和回调不起作用

WinInet C++ ftp 模板?

C++ Wininet 自定义 http 标头

C++ 下载文件 WinInet - 0kb 写入文件