WinHTTP 会话概述

Microsoft Windows HTTP Services (WinHTTP) 公开一组 C/C++ 函数,使应用程序能够访问 Web 上的 HTTP 资源。 本主题概述了如何使用这些函数与 HTTP 服务器进行交互。

使用 WinHTTP API 访问 Web

下图显示了与 HTTP 服务器交互时 WinHTTP 函数的调用顺序。 底纹框表示生成 HINTERNET 句柄的函数,而普通框表示使用这些句柄的函数。

用于创建句柄的函数

初始化 WinHTTP

在与服务器交互之前,必须通过调用 WinHttpOpen 初始化 WinHTTPWinHttpOpen 创建会话上下文来维护有关 HTTP 会话的详细信息,并返回会话句柄。 使用此句柄, WinHttpConnect 函数随后能够指定目标 HTTP 或安全超文本传输协议 (HTTPS) 服务器。

注意

调用 WinHttpConnect 不会导致与 HTTP 服务器的实际连接,直到对特定资源发出请求。

 

打开请求

WinHttpOpenRequest 函数打开特定资源的 HTTP 请求,并返回可由其他 HTTP 函数使用的 HINTERNET 句柄。 WinHttpOpenRequest 在调用时不会将请求发送到服务器。 WinHttpSendRequest 函数实际上通过网络建立连接并发送请求。

以下示例演示使用默认选项对 WinHttpOpenRequest 的示例调用。

HINTERNET hRequest = WinHttpOpenRequest( hConnect, L"GET", NULL, NULL, NULL, NULL, 0);

添加请求标头

WinHttpAddRequestHeaders 函数允许应用程序将其他自由格式的请求标头追加到 HTTP 请求句柄。 它适用于需要精确控制发送到 HTTP 服务器的请求的复杂应用程序。

WinHttpAddRequestHeaders 函数需要由 WinHttpOpenRequest 创建的 HTTP 请求句柄、包含标头、标头长度和任何修饰符的字符串。

以下修饰符可用于 WinHttpAddRequestHeaders

修饰符 说明
WINHTTP_ADDREQ_FLAG_ADD 如果标头不存在,则添加该标头。 与 WINHTTP_ADDREQ_FLAG_REPLACE 一起使用。
WINHTTP_ADDREQ_FLAG_ADD_IF_NEW 仅当标头尚不存在时才添加标头;否则,将返回错误。
WINHTTP_ADDREQ_FLAG_COALESCE 合并同名的标头。
WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA 使用逗号合并同名的标头。 例如,使用此标志添加“Accept: text/*”后跟“Accept: audio/*”,形成单个标头“Accept: text/*, audio/*”,从而合并找到的第一个标头。 由调用应用程序来确保与合并/单独标头相关的一致方案。
WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON 使用分号合并同名的标头。
WINHTTP_ADDREQ_FLAG_REPLACE 替换或删除标头。 如果标头值为空且找到标头,则会将其删除。 如果标头值不为空,则替换标头值。

 

发送请求

WinHttpSendRequest 函数与服务器建立连接,并将请求发送到指定站点。 此函数需要 WinHttpOpenRequest 创建的 HINTERNET 句柄。 WinHttpSendRequest 还可以发送其他标头或可选信息。 可选信息通常用于将信息写入服务器的操作,例如 PUT 和 POST。

WinHttpSendRequest 函数发送请求后,应用程序可以使用 HINTERNET 句柄上的 WinHttpReadDataWinHttpQueryDataAvailable 函数下载服务器的资源。

将数据发布到服务器

若要将数据发布到服务器,调用 WinHttpOpenRequest 中的 HTTP 谓词必须是 POST 或 PUT。 调用 WinHttpSendRequest 时, dwTotalLength 参数应设置为数据的大小(以字节为单位)。 然后使用 WinHttpWriteData 将数据发布到服务器。

或者,将 WinHttpSendRequestlpOptional 参数设置为包含要发布到服务器的数据的缓冲区的地址。 使用此方法时,必须将 WinHttpSendRequestdwOptionalLengthdwTotalLength 参数设置为要发布的数据的大小。 以这种方式调用 WinHttpSendRequest 无需调用 WinHttpWriteData

获取有关请求的信息

WinHttpQueryHeaders 函数允许应用程序检索有关 HTTP 请求的信息。 函数需要 WinHttpOpenRequest 创建的 HINTERNET 句柄、信息级别值和缓冲区长度。 WinHttpQueryHeaders 还接受存储信息的缓冲区和从零开始的标头索引,该索引枚举多个具有相同名称的标头。

使用在“查询信息标志”页上找到的任何信息级别值和修饰符来控制在 WinHttpQueryHeaderslpvBuffer 参数中存储信息的格式。

从 Web 下载资源

使用 WinHttpOpenRequest 函数打开请求,使用 WinHttpSendRequest 将其发送到服务器,并准备请求句柄以使用 WinHttpReceiveResponse 接收响应后,应用程序可以使用 WinHttpReadDataWinHttpQueryDataAvailable 函数从 HTTP 服务器下载资源。

以下示例代码演示如何下载具有安全事务语义的资源。 示例代码 (API) 初始化 WinHTTP 应用程序编程接口,选择目标 HTTPS 服务器,然后打开并发送对此安全资源的请求。 WinHttpQueryDataAvailable 与请求句柄一起使用,以确定可供下载的数据量,然后使用 WinHttpReadData 读取该数据。 此过程将重复,直到检索并显示整个文档。

  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( ) );

      // Allocate space for the buffer.
      pszOutBuffer = new char[dwSize+1];
      if( !pszOutBuffer )
      {
        printf( "Out of memory\n" );
        dwSize=0;
      }
      else
      {
        // 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;
      }
    } while( dwSize > 0 );
  }


  // Report any errors.
  if( !bResults )
    printf( "Error %d has occurred.\n", GetLastError( ) );

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