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();
}
}
下一篇,分析 多分辨率支持 的实现。