windows C++-windows C++-使用任务和 XML HTTP 请求进行连接(二)

  1. 在 HttpRequest.h 中,添加此代码:

    namespace Web
    {

    namespace Details
    {

    // 用于执行异步HTTP请求的实用程序类。
    // 此类一次只支持一个未完成的请求。
    class HttpRequest
    {
    public:
    HttpRequest();

     int GetStatusCode() const
     {
         return statusCode;
     }
    
     std::wstring const& GetReasonPhrase() const
     {
         return reasonPhrase;
     }
    
     // Whether the response has been completely received, if using ReadAsync().
     bool IsResponseComplete() const
     {
         return responseComplete;
     }
    
     // Start an HTTP GET on the specified URI.  The returned task completes once the entire response
     // has been received, and the task produces the HTTP response text.  The status code and reason
     // can be read with GetStatusCode() and GetReasonPhrase().
     concurrency::task<std::wstring> GetAsync(
         Windows::Foundation::Uri^ uri, 
         concurrency::cancellation_token cancellationToken = concurrency::cancellation_token::none());
    
     // Start an HTTP POST on the specified URI, using a string body.  The returned task produces the 
     // HTTP response text.  The status code and reason can be read with GetStatusCode() and GetReasonPhrase().
     concurrency::task<std::wstring> PostAsync(
         Windows::Foundation::Uri^ uri,
         PCWSTR contentType,
         IStream* postStream,
         uint64 postStreamSizeToSend,
         concurrency::cancellation_token cancellationToken = concurrency::cancellation_token::none());
    
     // Start an HTTP POST on the specified URI, using a stream body.  The returned task produces the
     // HTTP response text.  The status code and reason can be read with GetStatusCode() and GetReasonPhrase().
     concurrency::task<std::wstring> PostAsync(
         Windows::Foundation::Uri^ uri,
         const std::wstring& str,
         concurrency::cancellation_token cancellationToken = concurrency::cancellation_token::none());
    
     // Send a request but don't return the response.  Instead, let the caller read it with ReadAsync().
     concurrency::task<void> SendAsync(
         const std::wstring& httpMethod,
         Windows::Foundation::Uri^ uri,
         concurrency::cancellation_token cancellationToken = concurrency::cancellation_token::none());
    
     // Read a chunk of data from the HTTP response, up to a specified length or until we reach the end
     // of the response, and store the value in the provided buffer.  This is useful for large content,
     // enabling the streaming of the result.
     concurrency::task<void> ReadAsync(
         Windows::Storage::Streams::IBuffer^ readBuffer,
         unsigned int offsetInBuffer,
         unsigned int requestedBytesToRead);
    
     static void CreateMemoryStream(IStream **stream);
    

    private:
    // Start a download of the specified URI using the specified method. The returned task produces the
    // HTTP response text. The status code and reason can be read with GetStatusCode() and GetReasonPhrase().
    concurrency::taskstd::wstring DownloadAsync(
    PCWSTR httpMethod,
    PCWSTR uri,
    concurrency::cancellation_token cancellationToken,
    PCWSTR contentType,
    IStream* postStream,
    uint64 postStreamBytesToSend);

     // Referenced pointer to the callback, if using SendAsync/ReadAsync.
     Microsoft::WRL::ComPtr<Details::HttpRequestBuffersCallback> buffersCallback;
    
     int statusCode;
     std::wstring reasonPhrase;
    
     // Whether the response has been completely received, if using ReadAsync().
     bool responseComplete;
    

    };

    }

  2. 在 HttpRequest.cpp 中,先定义一个类HttpRequestStringCallback :

cpp 复制代码
#include "pch.h"
#include "HttpRequest.h"
#include <robuffer.h>
#include <shcore.h>

using namespace concurrency;
using namespace Microsoft::WRL;
using namespace Platform;
using namespace std;
using namespace Web;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;

//当只需要完整的响应时,使用IXMLHTTPRequest2Callback的实现。
//在处理收到的响应数据块时,请改用HttpRequestBuffersCallback。
class HttpRequestStringCallback 
    : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IXMLHTTPRequest2Callback, FtmBase>
{
public:
    HttpRequestStringCallback(IXMLHTTPRequest2* httpRequest, 
        cancellation_token ct = concurrency::cancellation_token::none()) :
        request(httpRequest), cancellationToken(ct)
    {
        // Register a callback function that aborts the HTTP operation when 
        // the cancellation token is canceled.
        if (cancellationToken != cancellation_token::none())
        {
            registrationToken = cancellationToken.register_callback([this]() 
            {
                if (request != nullptr) 
                {
                    request->Abort();
                }
            });
        }
    }

    // Called when the HTTP request is being redirected to a new URL.
    IFACEMETHODIMP OnRedirect(IXMLHTTPRequest2*, PCWSTR) 
    {
        return S_OK;
    }

    // Called when HTTP headers have been received and processed.
    IFACEMETHODIMP OnHeadersAvailable(IXMLHTTPRequest2*, DWORD statusCode, PCWSTR reasonPhrase)
    {
        HRESULT hr = S_OK;

        // We must not propagate exceptions back to IXHR2.
        try
        {
            this->statusCode = statusCode;
            this->reasonPhrase = reasonPhrase;
        }
        catch (std::bad_alloc&)
        {
            hr = E_OUTOFMEMORY;
        }

        return hr;
    }

    // Called when a portion of the entity body has been received.
    IFACEMETHODIMP OnDataAvailable(IXMLHTTPRequest2*, ISequentialStream*)
    {
        return S_OK;
    }
        
    // Called when the entire entity response has been received.
    IFACEMETHODIMP OnResponseReceived(IXMLHTTPRequest2*, ISequentialStream* responseStream)
    {
        wstring wstr;
        HRESULT hr = ReadUtf8StringFromSequentialStream(responseStream, wstr);

        // We must not propagate exceptions back to IXHR2.
        try
        {
            completionEvent.set(make_tuple<HRESULT, wstring>(move(hr), move(wstr)));
        }
        catch (std::bad_alloc&)
        {
            hr = E_OUTOFMEMORY;
        }

        return hr;
    }

    // Simulate the functionality of DataReader.ReadString().
    // This is needed because DataReader requires IRandomAccessStream and this
    // code has an ISequentialStream that does not have a conversion to IRandomAccessStream like IStream does.
    HRESULT ReadUtf8StringFromSequentialStream(ISequentialStream* readStream, wstring& str)
    {
        // Convert the response to Unicode wstring.
        HRESULT hr;

        // Holds the response as a Unicode string.
        wstringstream ss;

        while (true)
        {
            ULONG cb;
            char buffer[4096];

            // Read the response as a UTF-8 string.  Since UTF-8 characters are 1-4 bytes long,
            // we need to make sure we only read an integral number of characters.  So we'll
            // start with 4093 bytes.
            hr = readStream->Read(buffer, sizeof(buffer) - 3, &cb);
            if (FAILED(hr) || (cb == 0))
            {
                break; // Error or no more data to process, exit loop.
            }

            if (cb == sizeof(buffer) - 3)
            {
                ULONG subsequentBytesRead;
                unsigned int i, cl;

                // Find the first byte of the last UTF-8 character in the buffer.
                for (i = cb - 1; (i >= 0) && ((buffer[i] & 0xC0) == 0x80); i--);

                // Calculate the number of subsequent bytes in the UTF-8 character.
                if (((unsigned char)buffer[i]) < 0x80)
                {
                    cl = 1;
                }
                else if (((unsigned char)buffer[i]) < 0xE0)
                {
                    cl = 2;
                }
                else if (((unsigned char)buffer[i]) < 0xF0)
                {
                    cl = 3;
                }
                else
                {
                    cl = 4;
                }

                // Read any remaining bytes.
                if (cb < i + cl)
                {
                    hr = readStream->Read(buffer + cb, i + cl - cb, &subsequentBytesRead);
                    if (FAILED(hr))
                    {
                        break; // Error, exit loop.
                    }
                    cb += subsequentBytesRead;
                }
            }

            // First determine the size required to store the Unicode string.
            int const sizeRequired = MultiByteToWideChar(CP_UTF8, 0, buffer, cb, nullptr, 0);
            if (sizeRequired == 0)
            {
                // Invalid UTF-8.
                hr = HRESULT_FROM_WIN32(GetLastError());
                break;
            }
            unique_ptr<char16[]> wstr(new(std::nothrow) char16[sizeRequired + 1]);
            if (wstr.get() == nullptr)
            {
                hr = E_OUTOFMEMORY;
                break;
            }

            // Convert the string from UTF-8 to UTF-16LE.  This can never fail, since
            // the previous call above succeeded.
            MultiByteToWideChar(CP_UTF8, 0, buffer, cb, wstr.get(), sizeRequired);
            wstr[sizeRequired] = L'\0'; // Terminate the string.
            ss << wstr.get(); // Write the string to the stream.
        }

        str = SUCCEEDED(hr) ? ss.str() : wstring();
        return (SUCCEEDED(hr)) ? S_OK : hr; // Don't return S_FALSE.
    }
        
    // Called when an error occurs during the HTTP request.
    IFACEMETHODIMP OnError(IXMLHTTPRequest2*, HRESULT hrError) 
    {
        HRESULT hr = S_OK;

        // We must not propagate exceptions back to IXHR2.
        try
        {
            completionEvent.set(make_tuple<HRESULT, wstring>(move(hrError), wstring()));
        }
        catch (std::bad_alloc&)
        {
            hr = E_OUTOFMEMORY;
        }

        return hr;
    }

    // Retrieves the completion event for the HTTP operation.
    task_completion_event<tuple<HRESULT, wstring>> const& GetCompletionEvent() const
    {
        return completionEvent; 
    }

    int GetStatusCode() const
    {
        return statusCode;
    }

    wstring GetReasonPhrase() const
    {
        return reasonPhrase;
    }

private:
    ~HttpRequestStringCallback()
    {
        // Unregister the callback.
        if (cancellationToken != cancellation_token::none())
        {
            cancellationToken.deregister_callback(registrationToken);
        }
    }

    // Signals that the download operation was canceled.
    cancellation_token cancellationToken;

    // Used to unregister the cancellation token callback.
    cancellation_token_registration registrationToken;

    // The IXMLHTTPRequest2 that processes the HTTP request.
    ComPtr<IXMLHTTPRequest2> request;

    // Task completion event that is set when the 
    // download operation completes.
    task_completion_event<tuple<HRESULT, wstring>> completionEvent;

    int statusCode;
    wstring reasonPhrase;
};
  1. 接下来继续完成这个类的成员函数代码的添加:
cpp 复制代码
//将字节从顺序流复制到提供的缓冲区中,直到
//我们到达了一个或另一个的终点。
unsigned int Web::Details::HttpRequestBuffersCallback::ReadData(
    _Out_writes_(outputBufferSize) byte* outputBuffer,
    unsigned int outputBufferSize)
{
    // Lock the data event while doing the read, to ensure that any bytes we don't read will
    // result in the correct event getting triggered.
    concurrency::critical_section::scoped_lock lock(dataEventLock);

    ULONG bytesRead;
    CheckHResult(dataStream.Get()->Read(outputBuffer, outputBufferSize, &bytesRead));
    if (bytesRead < outputBufferSize)
    {
        // We need to reset the data event, which we can only do by creating a new one.
        dataEvent = task_completion_event<void>();
    }

    return bytesRead;
}
  1. 在代码中加入对完成任务的处理函数:
cpp 复制代码
//以异常安全的方式创建一个在数据可用时完成的任务。
task<void> Web::Details::HttpRequestBuffersCallback::CreateDataTask()
{
    concurrency::critical_section::scoped_lock lock(dataEventLock);
    return create_task(dataEvent, cancellationToken);
}

HttpRequest::HttpRequest() : responseComplete(true), statusCode(200)
{
}
相关推荐
黑不溜秋的22 分钟前
C++ 语言特性29 - 协程介绍
开发语言·c++
一丝晨光26 分钟前
C++、Ruby和JavaScript
java·开发语言·javascript·c++·python·c·ruby
天上掉下来个程小白29 分钟前
Stream流的中间方法
java·开发语言·windows
暮雪倾风35 分钟前
【WPF开发】控件介绍-Grid(网格布局)
windows·wpf
￴ㅤ￴￴ㅤ9527超级帅40 分钟前
LeetCode hot100---二叉树专题(C++语言)
c++·算法·leetcode
_GR1 小时前
每日OJ题_牛客_牛牛冲钻五_模拟_C++_Java
java·数据结构·c++·算法·动态规划
Death2001 小时前
Qt 中的 QListWidget、QTreeWidget 和 QTableWidget:简化的数据展示控件
c语言·开发语言·c++·qt·c#
六点半8881 小时前
【C++】速通涉及 “vector” 的经典OJ编程题
开发语言·c++·算法·青少年编程·推荐算法
coduck_S12004zbj2 小时前
csp-j模拟五补题报告
c++·算法·图论
Death2002 小时前
Qt 3D、QtQuick、QtQuick 3D 和 QML 的关系
c语言·c++·qt·3d·c#