16【cocos2d-x 源码分析】:HttpClient 的详细分析

对应源码位置:cocos2d-x-3.3\cocos\network\Http*

HttpRequest的实现

typedef std::function<void(HttpClient* client, HttpResponse* response)> ccHttpRequestCallback;
//(cocos2d::Ref::*SEL_HttpResponse) 代表cocos2d::Ref类的这种成员函数
typedef void (cocos2d::Ref::*SEL_HttpResponse)(HttpClient* client, HttpResponse* response);
//一个类型转换而已
#define httpresponse_selector(_SELECTOR) (cocos2d::network::SEL_HttpResponse)(&_SELECTOR)
class CC_DLL HttpRequest : public Ref
{
public:
 	 //请求方式
    enum class Type
    {
        GET,
        POST,
        PUT,
        DELETE,
        UNKNOWN,
    };
    
    /** Constructor
        Because HttpRequest object will be used between UI thead and network thread,
        requestObj->autorelease() is forbidden to avoid crashes in AutoreleasePool
        new/retain/release still works, which means you need to release it manually
        Please refer to HttpRequestTest.cpp to find its usage
     */
     //注意上面一=的话 autorelease不再能用了  由于多线程的问题
    HttpRequest()
    {
        _requestType = Type::UNKNOWN;
        _url.clear();
        _requestData.clear();
        _tag.clear();
        _pTarget = nullptr;
        _pSelector = nullptr;
        _pCallback = nullptr;
        _pUserData = nullptr;
    };
    
    /** Destructor */
    virtual ~HttpRequest()
    {
        if (_pTarget)
        {
            _pTarget->release();
        }
    };
   //专门告诉你 不能用了
    Ref* autorelease(void)
    {
        CCASSERT(false, "HttpResponse is used between network thread and ui thread \
                 therefore, autorelease is forbidden here");
        return NULL;
    }      
    inline void setRequestType(Type type)
    {
        _requestType = type;
    };
    inline Type getRequestType()
    {
        return _requestType;
    };
    inline void setUrl(const char* url)
    {
        _url = url;
    };
    inline const char* getUrl()
    {
        return _url.c_str();
    };
    inline void setRequestData(const char* buffer, size_t len)
    {
        _requestData.assign(buffer, buffer + len);
    };
    inline char* getRequestData()
    {
        if(_requestData.size() != 0)
            return &(_requestData.front());
        return nullptr;
    }
    inline ssize_t getRequestDataSize()
    {
        return _requestData.size();
    }
    inline void setTag(const char* tag)
    {
        _tag = tag;
    };
    inline const char* getTag()
    {
        return _tag.c_str();
    };
    inline void setUserData(void* pUserData)
    {
        _pUserData = pUserData;
    };
    inline void* getUserData()
    {
        return _pUserData;
    };
    //设置回调
    inline void setResponseCallback(const ccHttpRequestCallback& callback)
    {
        _pCallback = callback;
    }
    inline Ref* getTarget()
    {
        return _pTarget;
    }

    /* This sub class is just for migration SEL_CallFuncND to SEL_HttpResponse, 
       someday this way will be removed */
    class _prxy
    {
    public:
        _prxy( SEL_HttpResponse cb ) :_cb(cb) {}
        ~_prxy(){};
        operator SEL_HttpResponse() const { return _cb; }
        CC_DEPRECATED_ATTRIBUTE operator SEL_CallFuncND()   const { return (SEL_CallFuncND) _cb; }
    protected:
        SEL_HttpResponse _cb;
    };
    inline const ccHttpRequestCallback& getCallback()
    {
        return _pCallback;
    }
    //首部信息
    inline void setHeaders(std::vector<std::string> pHeaders)
   	{
   		_headers=pHeaders;
   	}
   	inline std::vector<std::string> getHeaders()
   	{
   		return _headers;
   	}
    
protected:
    Type                        _requestType;    /// kHttpRequestGet, kHttpRequestPost or other enums
    std::string                 _url;            /// target url that this request is sent to
    std::vector<char>           _requestData;    /// used for POST
    std::string                 _tag;            /// user defined tag, to identify different requests in response callback
    //用这两个  才能实现回调
    Ref*                        _pTarget;        /// callback target of pSelector function
    SEL_HttpResponse            _pSelector;      /// callback function, e.g. MyLayer::onHttpResponse(HttpClient *sender, HttpResponse * response)
    //用仿函数 更好实现
    ccHttpRequestCallback       _pCallback;      /// C++11 style callbacks
    void*                       _pUserData;      /// You can add your customed data here 
    //自定义的头
    std::vector<std::string>    _headers;		      /// custom http headers
};

HttpResponse的实现

class CC_DLL HttpResponse : public cocos2d::Ref
{
public:
    HttpResponse(HttpRequest* request)
    {
        _pHttpRequest = request;
        if (_pHttpRequest)
        {
            _pHttpRequest->retain();
        }
        
        _succeed = false;
        _responseData.clear();
        _errorBuffer.clear();
    }
    
    /** Destructor, it will be called in HttpClient internal,
     users don't need to desturct HttpResponse object manully 
     */
    virtual ~HttpResponse()
    {
        if (_pHttpRequest)
        {
            _pHttpRequest->release();
        }
    }
    //不能用
    cocos2d::Ref* autorelease(void)
    {
        CCASSERT(false, "HttpResponse is used between network thread and ui thread \
                        therefore, autorelease is forbidden here");
        return NULL;
    }
    inline HttpRequest* getHttpRequest()
    {
        return _pHttpRequest;
    }
    //看是否成功 
    inline bool isSucceed()
    {
        return _succeed;
    };
   	//获取返回的数据
    inline std::vector<char>* getResponseData()
    {
        return &_responseData;
    }
    //返回的报文头
    inline std::vector<char>* getResponseHeader()
    {
        return &_responseHeader;
    }
	//返回码 200 404
    inline long getResponseCode()
    {
        return _responseCode;
    }
	//错误原因
    inline const char* getErrorBuffer()
    {
        return _errorBuffer.c_str();
    }
    inline void setSucceed(bool value)
    {
        _succeed = value;
    };
    inline void setResponseData(std::vector<char>* data)
    {
        _responseData = *data;
    }
    inline void setResponseHeader(std::vector<char>* data)
    {
        _responseHeader = *data;
    }
    inline void setResponseCode(long value)
    {
        _responseCode = value;
    }
    inline void setErrorBuffer(const char* value)
    {
        _errorBuffer.clear();
        _errorBuffer.assign(value);
    };
    
protected:
    bool initWithRequest(HttpRequest* request);
    // 属性
    HttpRequest*        _pHttpRequest;  /// the corresponding HttpRequest pointer who leads to this response 
    bool                _succeed;       /// to indecate if the http reqeust is successful simply
    std::vector<char>   _responseData;  /// the returned raw data. You can also dump it as a string
    std::vector<char>   _responseHeader;  /// the returned raw header data. You can also dump it as a string
    long                _responseCode;    /// the status code returned from libcurl, e.g. 200, 404
    std::string         _errorBuffer;   /// if _responseCode != 200, please read _errorBuffer to find the reason     
};

HttpClient的实现

class CC_DLL HttpClient
{
public:
   //单例模式
    static HttpClient *getInstance();
    //销毁
    static void destroyInstance();
	//允许cookie
    void enableCookies(const char* cookieFile);
    //传入SSL verification 否则就代表不支持
    void setSSLVerification(const std::string& caFile);
  	//发送请求
    void send(HttpRequest* request);
	//立即发送
    void sendImmediate(HttpRequest* request);
  	//超时时间
    inline void setTimeoutForConnect(int value) {_timeoutForConnect = value;};
    inline int getTimeoutForConnect() {return _timeoutForConnect;}
    //下载超时时间
    inline void setTimeoutForRead(int value) {_timeoutForRead = value;};
    inline int getTimeoutForRead() {return _timeoutForRead;};
        
private:
    HttpClient();
    virtual ~HttpClient();
    bool init(void);
    //初始化 信号量 互斥量
    bool lazyInitThreadSemphore();
    //网络线程的执行函数
    void networkThread();
    void networkThreadAlone(HttpRequest* request);
   	//回调
    void dispatchResponseCallbacks();    
private:
    int _timeoutForConnect;
    int _timeoutForRead;
};

详细实现

//一些配置
//两个队列的 互斥量
static std::mutex       s_requestQueueMutex;
static std::mutex       s_responseQueueMutex;
//条件变量
static std::condition_variable_any s_SleepCondition;
//两个请求队列
static Vector<HttpRequest*>*  s_requestQueue = nullptr;
static Vector<HttpResponse*>* s_responseQueue = nullptr;
//单例的 HTTPClient
static HttpClient *s_pHttpClient = nullptr; // pointer to singleton
//错误缓冲
static char s_errorBuffer[CURL_ERROR_SIZE] = {0};
//write_callback
typedef size_t (*write_callback)(void *ptr, size_t size, size_t nmemb, void *stream);
//cookie文件名
static std::string s_cookieFilename = "";
//SSL证书 位置
static std::string s_sslCaFilename = "";
//一个准备的请求对象 哨兵请求 用来当HttpClient 析构时 通知 工作线程退出
static HttpRequest *s_requestSentinel = new HttpRequest;

// libcurl 用来收集response data 的回调
static size_t writeData(void *ptr, size_t size, size_t nmemb, void *stream)
{
    std::vector<char> *recvBuffer = (std::vector<char>*)stream;
    size_t sizes = size * nmemb;
    //写入到stream中
    recvBuffer->insert(recvBuffer->end(), (char*)ptr, (char*)ptr+sizes);
    return sizes;
}

// libcurl 用来收集header data 的回调
static size_t writeHeaderData(void *ptr, size_t size, size_t nmemb, void *stream)
{
    std::vector<char> *recvBuffer = (std::vector<char>*)stream;
    size_t sizes = size * nmemb;
   	//同理
    recvBuffer->insert(recvBuffer->end(), (char*)ptr, (char*)ptr+sizes);
    return sizes;
}

两个重要的工作线程

// Worker thread
void HttpClient::networkThread()
{    
    auto scheduler = Director::getInstance()->getScheduler();
    
    while (true) 
    {
        HttpRequest *request;
        // step 1: send http request if the requestQueue isn't empty
        {
        	//获取请求队列的锁
            std::lock_guard<std::mutex> lock(s_requestQueueMutex);
            //如果为空 就要sleep
            while (s_requestQueue->empty()) {
                s_SleepCondition.wait(s_requestQueueMutex);
            }
            //否则  取出第一个需求
            request = s_requestQueue->at(0);
            s_requestQueue->erase(0);
        }
		//是哨兵 就结束
        if (request == s_requestSentinel) {
            break;
        }

        // step 2: libcurl sync access
        
        // Create a HttpResponse object, the default setting is http access failed
        //开始 包装response对象
        HttpResponse *response = new (std::nothrow) HttpResponse(request);
        //开始处理
        processResponse(response, s_errorBuffer);
        //处理完成了 放到response队列里面去
        // add response packet into queue
        s_responseQueueMutex.lock();
        s_responseQueue->pushBack(response);
        s_responseQueueMutex.unlock();
        //放到主线程去执行 回调
        //每次处理一个 通知一次回调 注意不是每帧调用 或定时调用 只是这一次
        if (nullptr != s_pHttpClient) {
            scheduler->performFunctionInCocosThread(CC_CALLBACK_0(HttpClient::dispatchResponseCallbacks, this));
        }
    }
   //说明 全部处理完了 清理一下
    s_requestQueueMutex.lock();
    s_requestQueue->clear();
    s_requestQueueMutex.unlock();
	//不为空  就删除队列
    if (s_requestQueue != nullptr) {
        delete s_requestQueue;
        s_requestQueue = nullptr;
        delete s_responseQueue;
        s_responseQueue = nullptr;
    }
    
}
//单独处理 一个请求
void HttpClient::networkThreadAlone(HttpRequest* request)
{
    // Create a HttpResponse object, the default setting is http access failed
    HttpResponse *response = new (std::nothrow) HttpResponse(request);
    char errorBuffer[CURL_ERROR_SIZE] = { 0 };
    //处理完了 之后
    processResponse(response, errorBuffer);
	//注册回调
    auto scheduler = Director::getInstance()->getScheduler();
    //单独注册
    scheduler->performFunctionInCocosThread([response, request]{
        const ccHttpRequestCallback& callback = request->getCallback();
        Ref* pTarget = request->getTarget();
        SEL_HttpResponse pSelector = request->getSelector();
		//这几种回调 看有哪一个
        if (callback != nullptr)
        {
            callback(s_pHttpClient, response);
        }
        else if (pTarget && pSelector)
        {
            (pTarget->*pSelector)(s_pHttpClient, response);
        }
        //手动释放
        response->release();
        // do not release in other thread
        request->release();
    });
}

可以看到对待每一个request,都对其执行 processResponse

//传入 Request封装成的Response 并传入errorBuffer
// Process Response
static void processResponse(HttpResponse* response, char* errorBuffer)
{
    auto request = response->getHttpRequest();
    long responseCode = -1;
    int retValue = 0;
    // Process the request -> get response packet
    //根据类型进行处理 分别处理
    switch (request->getRequestType())
    {
    case HttpRequest::Type::GET: // HTTP GET
        retValue = processGetTask(request,
            writeData, 
            response->getResponseData(), 
            &responseCode,
            writeHeaderData,
            response->getResponseHeader(),
            errorBuffer);
        break;

    case HttpRequest::Type::POST: // HTTP POST
        retValue = processPostTask(request,
            writeData, 
            response->getResponseData(), 
            &responseCode,
            writeHeaderData,
            response->getResponseHeader(),
            errorBuffer);
        break;

    case HttpRequest::Type::PUT:
        retValue = processPutTask(request,
            writeData,
            response->getResponseData(),
            &responseCode,
            writeHeaderData,
            response->getResponseHeader(),
            errorBuffer);
        break;

    case HttpRequest::Type::DELETE:
        retValue = processDeleteTask(request,
            writeData,
            response->getResponseData(),
            &responseCode,
            writeHeaderData,
            response->getResponseHeader(),
            errorBuffer);
        break;

    default:
        CCASSERT(true, "CCHttpClient: unkown request type, only GET and POSt are supported");
        break;
    }

    // write data to HttpResponse
    response->setResponseCode(responseCode);

    if (retValue != 0) 
    {
        response->setSucceed(false);
        response->setErrorBuffer(errorBuffer);
    }
    else
    {
        response->setSucceed(true);
    }
}
//Process Get Request
static int processGetTask(HttpRequest *request, write_callback callback, void *stream, long *responseCode, write_callback headerCallback, void *headerStream, char *errorBuffer)
{
	//整个 网络功能 都是对curl库的封装
    CURLRaii curl;
    bool ok = curl.init(request, callback, stream, headerCallback, headerStream, errorBuffer)
            && curl.setOption(CURLOPT_FOLLOWLOCATION, true)
            && curl.perform(responseCode);
    return ok ? 0 : 1;
}

//Process POST Request
static int processPostTask(HttpRequest *request, write_callback callback, void *stream, long *responseCode, write_callback headerCallback, void *headerStream, char *errorBuffer)
{
    CURLRaii curl;
    bool ok = curl.init(request, callback, stream, headerCallback, headerStream, errorBuffer)
            && curl.setOption(CURLOPT_POST, 1)
            && curl.setOption(CURLOPT_POSTFIELDS, request->getRequestData())
            && curl.setOption(CURLOPT_POSTFIELDSIZE, request->getRequestDataSize())
            && curl.perform(responseCode);
    return ok ? 0 : 1;
}

//Process PUT Request
static int processPutTask(HttpRequest *request, write_callback callback, void *stream, long *responseCode, write_callback headerCallback, void *headerStream, char *errorBuffer)
{
    CURLRaii curl;
    bool ok = curl.init(request, callback, stream, headerCallback, headerStream, errorBuffer)
            && curl.setOption(CURLOPT_CUSTOMREQUEST, "PUT")
            && curl.setOption(CURLOPT_POSTFIELDS, request->getRequestData())
            && curl.setOption(CURLOPT_POSTFIELDSIZE, request->getRequestDataSize())
            && curl.perform(responseCode);
    return ok ? 0 : 1;
}

//Process DELETE Request
static int processDeleteTask(HttpRequest *request, write_callback callback, void *stream, long *responseCode, write_callback headerCallback, void *headerStream, char *errorBuffer)
{
    CURLRaii curl;
    bool ok = curl.init(request, callback, stream, headerCallback, headerStream, errorBuffer)
            && curl.setOption(CURLOPT_CUSTOMREQUEST, "DELETE")
            && curl.setOption(CURLOPT_FOLLOWLOCATION, true)
            && curl.perform(responseCode);
    return ok ? 0 : 1;
}

CURLRaii的实现

//主要是实现 一些参数的 简单传递
//以及使用RAII思想 将初始化与清理放在 构造析构之中
class CURLRaii
{
    /// Instance of CURL
    CURL *_curl;
    /// Keeps custom header data
    curl_slist *_headers;
public:
    CURLRaii()
        : _curl(curl_easy_init())
        , _headers(nullptr)
    {
    }
    ~CURLRaii()
    {
        if (_curl)
            curl_easy_cleanup(_curl);
        /* free the linked list for header data */
        if (_headers)
            curl_slist_free_all(_headers);
    }

    template <class T>
    bool setOption(CURLoption option, T data)
    {
        return CURLE_OK == curl_easy_setopt(_curl, option, data);
    }

    /**
     * @brief Inits CURL instance for common usage
     * @param request Null not allowed
     * @param callback Response write callback
     * @param stream Response write stream
     */
    bool init(HttpRequest *request, write_callback callback, void *stream, write_callback headerCallback, void *headerStream, char *errorBuffer)
    {
        if (!_curl)
            return false;
        if (!configureCURL(_curl, errorBuffer))
            return false;

        /* get custom header data (if set) */
        std::vector<std::string> headers=request->getHeaders();
        if(!headers.empty())
        {
            /* append custom headers one by one */
            for (std::vector<std::string>::iterator it = headers.begin(); it != headers.end(); ++it)
                _headers = curl_slist_append(_headers,it->c_str());
            /* set custom headers for curl */
            if (!setOption(CURLOPT_HTTPHEADER, _headers))
                return false;
        }
        if (!s_cookieFilename.empty()) {
            if (!setOption(CURLOPT_COOKIEFILE, s_cookieFilename.c_str())) {
                return false;
            }
            if (!setOption(CURLOPT_COOKIEJAR, s_cookieFilename.c_str())) {
                return false;
            }
        }

        return setOption(CURLOPT_URL, request->getUrl())
                && setOption(CURLOPT_WRITEFUNCTION, callback)
                && setOption(CURLOPT_WRITEDATA, stream)
                && setOption(CURLOPT_HEADERFUNCTION, headerCallback)
                && setOption(CURLOPT_HEADERDATA, headerStream);
        
    }

    /// @param responseCode Null not allowed
    bool perform(long *responseCode)
    {
        if (CURLE_OK != curl_easy_perform(_curl))
            return false;
        CURLcode code = curl_easy_getinfo(_curl, CURLINFO_RESPONSE_CODE, responseCode);
        if (code != CURLE_OK || !(*responseCode >= 200 && *responseCode < 300)) {
            CCLOGERROR("Curl curl_easy_getinfo failed: %s", curl_easy_strerror(code));
            return false;
        }
        // Get some mor data.
        
        return true;
    }
};

Send相关的实现

//分配 两个队列
bool HttpClient::lazyInitThreadSemphore()
{
    if (s_requestQueue != nullptr) {
        return true;
    } else {
        
        s_requestQueue = new (std::nothrow) Vector<HttpRequest*>();
        s_responseQueue = new (std::nothrow) Vector<HttpResponse*>();

        auto t = std::thread(CC_CALLBACK_0(HttpClient::networkThread, this));
        t.detach();
    }
    
    return true;
}
//send 为加入到发送队列
void HttpClient::send(HttpRequest* request)
{    
    if (false == lazyInitThreadSemphore()) 
    {
        return;
    }
    
    if (!request)
    {
        return;
    }
        
    request->retain();
    
    if (nullptr != s_requestQueue) {
        s_requestQueueMutex.lock();
        s_requestQueue->pushBack(request);
        s_requestQueueMutex.unlock();
        
        // 通知唤醒
        s_SleepCondition.notify_one();
    }
}
//立即发送 即为 重开一个线程 专门处理这个请求
void HttpClient::sendImmediate(HttpRequest* request)
{
    if(!request)
    {
        return;
    }

    request->retain();
    auto t = std::thread(&HttpClient::networkThreadAlone, this, request);
    t.detach();
}

Callback的实现

//每产生一个 response 都会调用到这里一次
void HttpClient::dispatchResponseCallbacks()
{
    // log("CCHttpClient::dispatchResponseCallbacks is running");
    //occurs when cocos thread fires but the network thread has already quited
    if (nullptr == s_responseQueue) {
        return;
    }
    HttpResponse* response = nullptr;
    s_responseQueueMutex.lock();

    if (!s_responseQueue->empty())
    {
        response = s_responseQueue->at(0);
        s_responseQueue->erase(0);
    }
   
    s_responseQueueMutex.unlock();
    
    if (response)
    {
        HttpRequest *request = response->getHttpRequest();
        const ccHttpRequestCallback& callback = request->getCallback();
        Ref* pTarget = request->getTarget();
        SEL_HttpResponse pSelector = request->getSelector();
        //取出来 进行回调
        if (callback != nullptr)
        {
            callback(this, response);
        }
        else if (pTarget && pSelector)
        {
            (pTarget->*pSelector)(this, response);
        }
        response->release();
        // do not release in other thread
        request->release();
    }
}

最后

下一篇,分析 多分辨率支持 的实现。

你可能感兴趣的:(cocos2d-x,c++,cocos2d-x,源码分析)