这段时间接触到cocos2d-x,拜读了csdn上很多大大的文章,尤其是小满的专栏,感觉获益不少,觉得像他们那样,边学习,边总结经验,并写出来学习过程与大家分享,我觉得是一件很值得学习的事,所以也打算把自己学习的东西和经验与大家分享,有不足之处或者错误的,还希望请大家能海涵并提出来,共同讨论,共同进步。好了,废话到此。
Cocos2dx 为我们封装了在cocos2dx中http的网络框架,其文件在cocos2dx引擎包的cocos2d-2.1rc0-x-2.1.2\extensions\network文件下的 HttpClient、HttpRequest 、HttpResponse。但是真正的底层,用的还是cURL库。。。
进行一次http交互,需要涉及的有三个类,HttpRequest用来描述一个请求。HttpResponse用来描述对应请求的响应。HttpClient是一个单例模式的类,它的职责就是负责将收到的HttpRequest对象push到发送队列中,并发送一个信号量驱动工作线程工作,工作线程再将收到的数据封装成一个HttpResponse对象push接收队列,并启用调度来派送数据。具体的后面有说道。
1.首先创建一个类,继承自cocos2d-x中的任何一个类都可以(有共同父类CCObject),并实现一个SEL_CallFuncND类型成员函数,用来做收到数据后的回调函数,函数原型为void fun(CCNode*, void*)。
2.当我们需要一次http交互的时候,我们需要new 一个CCHttpRequest对象,并设置url和请求方式(get还是post,本文只说一下get的原理,post区别不大,可以自己看),并将上面说函数设置为收到数据后的回调函数。
3.使用CCHttpClient::getInstance()单例对象,将前一步骤的CCHttpRequest对象作为参数,调用send()方法。
4.在回调函数中,将第二个参数转换成CCHttpResponse *类型,就可以通过CCHttpResponse类的方法来获取返回状态和数据等能容了。
我们先来看看具体的该怎么用,以自带的HttpClientTest.cpp为例。HttpClientTest.cpp:
//get请求
void HttpClientTest::onMenuGetTestClicked(cocos2d::CCObject *sender)
{
// test 1
{
CCHttpRequest* request = new CCHttpRequest();//创建request对象,这里new出来的对象不能使用autorelease(),原因后述
request->setUrl("http://just-make-this-request-failed.com");//设置url
request->setRequestType(CCHttpRequest::kHttpGet);//设置请求方式
request->setResponseCallback(this, callfuncND_selector(HttpClientTest::onHttpRequestCompleted));//这是回调对象和回调函数
request->setTag("GET test1");//设置用户标识,可以通过response获取
CCHttpClient::getInstance()->send(request);//使用CCHttpClient共享实例来发送request
request->release();//调用release()
}
// waiting
m_labelStatusCode->setString("waiting...");
}
//这里就是我们要处理接收到数据的回调函数了,sender为CCHttpClient实例指针,data为接收到的response指针
void HttpClientTest::onHttpRequestCompleted(cocos2d::CCNode *sender, void *data)
{
CCHttpResponse *response = (CCHttpResponse*)data;
if (!response)
{
return;
}
// 获取对应request的字符串标识
if (0 != strlen(response->getHttpRequest()->getTag()))
{
CCLog("%s completed", response->getHttpRequest()->getTag());
}
//获取返回代码,比如200、404等
int statusCode = response->getResponseCode();
char statusString[64] = {};
sprintf(statusString, "HTTP Status Code: %d, tag = %s", statusCode, response->getHttpRequest()->getTag());
m_labelStatusCode->setString(statusString);
CCLog("response code: %d", statusCode);
if (!response->isSucceed())
{
CCLog("response failed");
CCLog("error buffer: %s", response->getErrorBuffer());//可以调用getErrorBuffer()来获取错误原因
return;
}
// dump data
std::vector *buffer = response->getResponseData();//用来获取接收到的数据
printf("Http Test, dump data: ");
for (unsigned int i = 0; i < buffer->size(); i++)
{
printf("%c", (*buffer)[i]);
}
printf("\n");
}
基本上一个http交互就是这个样子了,下面我们深入的看一下CCHttpClient是怎么工作的,先来看一张图,画的不好或者不足之处,请勿拍砖
其实就是当我们第一次CCHttpClient::getInstance()时,CCHttpClient会将自己的成员函数dispathResponseCallbacks()挂载至CCScheduler(可以理解成一个调度者,它会定时调用所有挂载至上面的函数),并将它初始设置为停止调度。在当我们第一次调用send()发送数据时,CCHttpClient会创建一个工作线程(之后再调用send()就不会创建线程了),然后再将传递过来的CCHttpRequest对象push到发送队列s_requestQueue,并发送一个信号给工作线程,驱使其工作。工作线程首先从发送队列中取得一个CCHttpRequest对象,并new 一个CCHttpResponse对象,将参数设置给cURL,cURL会在获取到数据的填充response,工作线程将填充后的response再放到接收队列s_responseQueue中去,同时,启用调度。下一次CCScheduler就会CCHttpClient::dispatchResponseCallbacks()了,在该函数中,它会调用我们在第二步中设置给request的回调函数,并将response传递过去。基本过程就是这样。下面来详解相关的源文件。HttpRequest.h,其实这个文件没什么好说的,都有注释
class CCHttpRequest : public CCObject
{
public:
/** 请求类型枚举,可以通过setReqeustType(param) 设置*/
typedef enum
{
kHttpGet,
kHttpPost,
kHttpUnkown,
} HttpRequestType;
/** Constructor
Because HttpRequest object will be used between UI thead and network thread,
requestObj->autorelease() is forbidden to avoid crashes in CCAutoreleasePool
new/retain/release still works, which means you need to release it manually
Please refer to HttpRequestTest.cpp to find its usage
这里是有注释的,因为要跨线程,所以就不能用autorelease()
我们在使用HttpRequest的时候,需要自己new,然后再release下就可以了
当我们把HttpRequest传递给CCHttpClient的时候,CCHttpClient已经帮我们retain了
工作线程中,需要使用CCHttpRequest对象new一个CCHttpResponse,CCHttprequest会retain一次,所以工作线程也会release一次
具体的后文有
*/
CCHttpRequest()
{
_requestType = kHttpUnkown;
_url.clear();
_requestData.clear();
_tag.clear();
_pTarget = NULL;
_pSelector = NULL;
_pUserData = NULL;
};
virtual ~CCHttpRequest()
{
if (_pTarget)
{
_pTarget->release();
}
};
/** 重载autorelease函数,禁止调用 */
CCObject* autorelease(void)
{
CCAssert(false, "HttpResponse is used between network thread and ui thread \
therefore, autorelease is forbidden here");
return NULL;
}
// setter/getters for properties
/** 设置请求类型
目前支持kHttpGet 和 kHttpPost
*/
inline void setRequestType(HttpRequestType type)
{
_requestType = type;
};
/** 返回请求类型 */
inline HttpRequestType getRequestType()
{
return _requestType;
};
/** 设置请求url
*/
inline void setUrl(const char* url)
{
_url = url;
};
/** 获取请求url */
inline const char* getUrl()
{
return _url.c_str();
};
/** 这个设置用于post方式的data数据
*/
inline void setRequestData(const char* buffer, unsigned int len)
{
_requestData.assign(buffer, buffer + len);
};
/** Get the request data pointer back */
inline char* getRequestData()
{
return &(_requestData.front());
}
/** Get the size of request data back */
inline int getRequestDataSize()
{
return _requestData.size();
}
/** 为每个请求设置一个字符串标示,可以通过HttpResponse->getHttpRequest->getTag()获取,因为HttpResponse会将对应的HttpRequest封装在里面
*/
inline void setTag(const char* tag)
{
_tag = tag;
};
/** Get the string tag back to identify the request.
The best practice is to use it in your MyClass::onMyHttpRequestCompleted(sender, HttpResponse*) callback
*/
inline const char* getTag()
{
return _tag.c_str();
};
/** Option field. You can attach a customed data in each request, and get it back in response callback.
But you need to new/delete the data pointer manully
*/
inline void setUserData(void* pUserData)
{
_pUserData = pUserData;
};
/** Get the pre-setted custom data pointer back.
Don't forget to delete it. HttpClient/HttpResponse/HttpRequest will do nothing with this pointer
*/
inline void* getUserData()
{
return _pUserData;
};
/** 通过这个函数设置我们的数据处理回调函数
*/
inline void setResponseCallback(CCObject* pTarget, SEL_CallFuncND pSelector)
{
_pTarget = pTarget;
_pSelector = pSelector;
if (_pTarget)
{
_pTarget->retain();
}
}
/** Get the target of callback selector funtion, mainly used by CCHttpClient */
inline CCObject* getTarget()
{
return _pTarget;
}
/** Get the selector function pointer, mainly used by CCHttpClient */
inline SEL_CallFuncND getSelector()
{
return _pSelector;
}
/** Set any custom headers **/
inline void setHeaders(std::vector pHeaders)
{
_headers=pHeaders;
}
/** Get custom headers **/
inline std::vector getHeaders()
{
return _headers;
}
protected:
// properties
HttpRequestType _requestType; /// 请求方式
std::string _url; /// 请求url
std::vector _requestData; /// 用于 POST
std::string _tag; /// 用户自定义标识,可以用来在response回调中区分request
CCObject* _pTarget; /// 回调对象
SEL_CallFuncND _pSelector; /// 回调函数例如 MyLayer::onHttpResponse(CCObject *sender, void *data)
void* _pUserData; /// 用户自定义数据,和_tag用法一样,只不过是用途不一样
std::vector _headers; /// custom http headers
};
class CCHttpResponse : public CCObject
{
public:
/** Constructor, it's used by CCHttpClient internal, users don't need to create HttpResponse manually
@param request the corresponding HttpRequest which leads to this response
*/
CCHttpResponse(CCHttpRequest* request)
{
_pHttpRequest = request;
if (_pHttpRequest)
{
_pHttpRequest->retain();
}
_succeed = false;
_responseData.clear();
_errorBuffer.clear();
}
/** Destructor, it will be called in CCHttpClient internal,
users don't need to desturct HttpResponse object manully
*/
virtual ~CCHttpResponse()
{
if (_pHttpRequest)
{
_pHttpRequest->release();
}
}
/** Override autorelease method to prevent developers from calling it */
CCObject* autorelease(void)
{
CCAssert(false, "HttpResponse is used between network thread and ui thread \
therefore, autorelease is forbidden here");
return NULL;
}
// getters, will be called by users
/** Get the corresponding HttpRequest object which leads to this response
There's no paired setter for it, coz it's already setted in class constructor
*/
inline CCHttpRequest* getHttpRequest()
{
return _pHttpRequest;
}
/** To see if the http reqeust is returned successfully,
Althrough users can judge if (http return code = 200), we want an easier way
If this getter returns false, you can call getResponseCode and getErrorBuffer to find more details
*/
inline bool isSucceed()
{
return _succeed;
};
/** Get the http response raw data */
inline std::vector* getResponseData()
{
return &_responseData;
}
/** Get the http response errorCode
* I know that you want to see http 200 :)
*/
inline int getResponseCode()
{
return _responseCode;
}
/** Get the rror buffer which will tell you more about the reason why http request failed
*/
inline const char* getErrorBuffer()
{
return _errorBuffer.c_str();
}
// setters, will be called by CCHttpClient
// users should avoid invoking these methods
/** Set if the http request is returned successfully,
Althrough users can judge if (http code == 200), we want a easier way
This setter is mainly used in CCHttpClient, users mustn't set it directly
*/
inline void setSucceed(bool value)
{
_succeed = value;
};
/** Set the http response raw buffer, is used by CCHttpClient
*/
inline void setResponseData(std::vector* data)
{
_responseData = *data;
}
/** Set the http response errorCode
*/
inline void setResponseCode(int value)
{
_responseCode = value;
}
/** Set the error buffer which will tell you more the reason why http request failed
*/
inline void setErrorBuffer(const char* value)
{
_errorBuffer.clear();
_errorBuffer.assign(value);
};
protected:
bool initWithRequest(CCHttpRequest* request);
// properties
//这里要留意下,每个response中都会包含对应的request,所以能在数据处理回调函数中,获取我们在设置request的所有参数,比如像tag,userdata
CCHttpRequest* _pHttpRequest; /// the corresponding HttpRequest pointer who leads to this response
bool _succeed; /// to indecate if the http reqeust is successful simply
std::vector _responseData; /// the returned raw data. You can also dump it as a string
int _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.h
//CCHttpClient是一个单例模式的类,整个程序共享一个实例对象
class CCHttpClient : public CCObject
{
public:
/** 获取共享的单例对象 **/
static CCHttpClient *getInstance();
/** Relase the shared instance **/
static void destroyInstance();
/**
* Add a get request to task queue
* @param request a CCHttpRequest object, which includes url, response callback etc.
please make sure request->_requestData is clear before calling "send" here.
* @return NULL
*/
void send(CCHttpRequest* request);
/**
* Change the connect timeout
* @param timeout
* @return NULL
*/
inline void setTimeoutForConnect(int value) {_timeoutForConnect = value;};
/**
* Get connect timeout
* @return int
*
*/
inline int getTimeoutForConnect() {return _timeoutForConnect;}
/**
* Change the download timeout
* @param value
* @return NULL
*/
inline void setTimeoutForRead(int value) {_timeoutForRead = value;};
/**
* Get download timeout
* @return int
*/
inline int getTimeoutForRead() {return _timeoutForRead;};
private:
CCHttpClient();
virtual ~CCHttpClient();
bool init(void);
/**
* Init pthread mutex, semaphore, and create new thread for http requests
* @return bool
*/
bool lazyInitThreadSemphore();
/** Poll function called from main thread to dispatch callbacks when http requests finished **/
void dispatchResponseCallbacks(float delta);
private:
int _timeoutForConnect;//连接超时时间
int _timeoutForRead;//接收数据超时时间
// std::string reqId;
};
HttpClient.cpp
static pthread_t s_networkThread;//工作线程句柄
static pthread_mutex_t s_requestQueueMutex;//请求队列互斥变量
static pthread_mutex_t s_responseQueueMutex;//接收队列互斥变量
static sem_t * s_pSem = NULL;//用来驱动线程工作的信号量
static unsigned long s_asyncRequestCount = 0;//当前需要处理的request个数
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
#define CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 1
#else
#define CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 0
#endif
#if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE
#define CC_ASYNC_HTTPREQUEST_SEMAPHORE "ccHttpAsync"
#else
static sem_t s_sem;
#endif
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
typedef int int32_t;
#endif
static bool need_quit = false; //退出标识
static CCArray* s_requestQueue = NULL; //请求队列(下面都说request队列)
static CCArray* s_responseQueue = NULL; //接收队列(下面都说response队列)
static CCHttpClient *s_pHttpClient = NULL; // 全局单例CCHttpClient对象
static char s_errorBuffer[CURL_ERROR_SIZE];//错误提示buffer
typedef size_t (*write_callback)(void *ptr, size_t size, size_t nmemb, void *stream);//这个是用于cURL收到数据的回调函数
// 这个便是当cURL接收到数据回调的函数,也就是在这里对response进行填充,这里的声明方式和fwrite()函数一样
size_t writeData(void *ptr, size_t size, size_t nmemb, void *stream)
{
//ptr指向接受到的数据,sizes为字节数
//这里传过来的stream中保存了CCHttpResponse::_responseData
std::vector *recvBuffer = (std::vector*)stream;
size_t sizes = size * nmemb;
// add data to the end of recvBuffer
// 将接受到的数据写到response中去
recvBuffer->insert(recvBuffer->end(), (char*)ptr, (char*)ptr+sizes);
return sizes;
}
// Prototypes
bool configureCURL(CURL *handle);
int processGetTask(CCHttpRequest *request, write_callback callback, void *stream, int32_t *errorCode);
int processPostTask(CCHttpRequest *request, write_callback callback, void *stream, int32_t *errorCode);
// int processDownloadTask(HttpRequest *task, write_callback callback, void *stream, int32_t *errorCode);
// 工作线程
static void* networkThread(void *data)
{
CCHttpRequest *request = NULL;
while (true)
{
// 等待主线程发送信号,就是调用send()函数
int semWaitRet = sem_wait(s_pSem);
if (semWaitRet < 0) {
CCLog("HttpRequest async thread semaphore error: %s\n", strerror(errno));
break;
}
//退出
if (need_quit)
{
break;
}
// step 1: send http request if the requestQueue isn't empty
request = NULL;
pthread_mutex_lock(&s_requestQueueMutex); //给request队列上锁
if (0 != s_requestQueue->count())
{
request = dynamic_cast(s_requestQueue->objectAtIndex(0));//取得第一个request
s_requestQueue->removeObjectAtIndex(0); //将其移除队列
// 这里的request的引用次数为1,因为只有在send()函数中retain了一次
}
pthread_mutex_unlock(&s_requestQueueMutex);//request队列解锁
if (NULL == request)
{
continue;
}
// 同步调用cURL库
// 使用request来创建一个response
CCHttpResponse *response = new CCHttpResponse(request);
// 在CCHttpTtpResponse构造中,会将request再retain一次
request->release();
// 这里,只有response中有request的一次引用计数
int responseCode = -1;
int retValue = 0;
// 根据请求类型设置cURL参数
switch (request->getRequestType())
{
case CCHttpRequest::kHttpGet: // HTTP GET
retValue = processGetTask(request,
writeData,
response->getResponseData(),
&responseCode);
break;
case CCHttpRequest::kHttpPost: // HTTP POST
retValue = processPostTask(request,
writeData,
response->getResponseData(),
&responseCode);
break;
default:
CCAssert(true, "CCHttpClient: unkown request type, only GET and POSt are supported");
break;
}
// 设置返回代码
response->setResponseCode(responseCode);
if (retValue != 0)
{
response->setSucceed(false);
response->setErrorBuffer(s_errorBuffer);
}
else
{
response->setSucceed(true);
}
// 将response加入队列
pthread_mutex_lock(&s_responseQueueMutex);//给response加锁
s_responseQueue->addObject(response);
pthread_mutex_unlock(&s_responseQueueMutex);//解锁
// 启动CCScheduler调度
CCDirector::sharedDirector()->getScheduler()->resumeTarget(CCHttpClient::getInstance());
}
// 线程退出,清理request队列
pthread_mutex_lock(&s_requestQueueMutex);
s_requestQueue->removeAllObjects();
pthread_mutex_unlock(&s_requestQueueMutex);
s_asyncRequestCount -= s_requestQueue->count();
if (s_pSem != NULL) {
#if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE
sem_unlink(CC_ASYNC_HTTPREQUEST_SEMAPHORE);
sem_close(s_pSem);
#else
sem_destroy(s_pSem);
#endif
s_pSem = NULL;
//释放互斥变量
pthread_mutex_destroy(&s_requestQueueMutex);
pthread_mutex_destroy(&s_responseQueueMutex);
s_requestQueue->release();
s_responseQueue->release();
}
pthread_exit(NULL);
return 0;
}
//设置cURL超时属性
bool configureCURL(CURL *handle)
{
if (!handle) {
return false;
}
int32_t code;
//设置错误信息缓冲
code = curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, s_errorBuffer);
if (code != CURLE_OK) {
return false;
}
//设置超时时间
code = curl_easy_setopt(handle, CURLOPT_TIMEOUT, CCHttpClient::getInstance()->getTimeoutForRead());
if (code != CURLE_OK) {
return false;
}
code = curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, CCHttpClient::getInstance()->getTimeoutForConnect());
if (code != CURLE_OK) {
return false;
}
return true;
}
//处理get方式请求
//stream传递过来的是response->getResponseData()
//关于cURL的东西这里就不多说了
int processGetTask(CCHttpRequest *request, write_callback callback, void *stream, int *responseCode)
{
CURLcode code = CURL_LAST;
//初始化cURL
CURL *curl = curl_easy_init();
do {
if (!configureCURL(curl)) //配置cURL
{
break;
}
/* handle custom header data */
/* create curl linked list */
struct curl_slist *cHeaders=NULL;
/* get custom header data (if set) */
std::vector headers=request->getHeaders();
if(!headers.empty())
{
for(std::vector::iterator it=headers.begin();it!=headers.end();it++)
{
/* append custom headers one by one */
cHeaders=curl_slist_append(cHeaders,it->c_str());
}
/* set custom headers for curl */
code = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, cHeaders);
if (code != CURLE_OK) {
break;
}
}
code = curl_easy_setopt(curl, CURLOPT_URL, request->getUrl());
if (code != CURLE_OK)
{
break;
}
code = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, request->getUrl());
if (code != CURLE_OK)
{
break;
}
code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);
if (code != CURLE_OK)
{
break;
}
//这里将response->_responseData设置为cURL回调函数中的stream参数
code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, stream);
if (code != CURLE_OK)
{
break;
}
code = curl_easy_perform(curl);
if (code != CURLE_OK)
{
break;
}
/* free the linked list for header data */
curl_slist_free_all(cHeaders);
code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, responseCode);
if (code != CURLE_OK || *responseCode != 200)
{
code = CURLE_HTTP_RETURNED_ERROR;
}
} while (0);
if (curl) {
curl_easy_cleanup(curl);
}
return (code == CURLE_OK ? 0 : 1);
}
//这个就不说了,其实都一样的,cURL承担了所有工作
int processPostTask(CCHttpRequest *request, write_callback callback, void *stream, int32_t *responseCode)
{
CURLcode code = CURL_LAST;
CURL *curl = curl_easy_init();
do {
if (!configureCURL(curl)) {
break;
}
/* handle custom header data */
/* create curl linked list */
struct curl_slist *cHeaders=NULL;
/* get custom header data (if set) */
std::vector headers=request->getHeaders();
if(!headers.empty())
{
for(std::vector::iterator it=headers.begin();it!=headers.end();it++)
{
/* append custom headers one by one */
cHeaders=curl_slist_append(cHeaders,it->c_str());
}
/* set custom headers for curl */
code = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, cHeaders);
if (code != CURLE_OK) {
break;
}
}
code = curl_easy_setopt(curl, CURLOPT_URL, request->getUrl());
if (code != CURLE_OK) {
break;
}
code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);
if (code != CURLE_OK) {
break;
}
code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, stream);
if (code != CURLE_OK) {
break;
}
code = curl_easy_setopt(curl, CURLOPT_POST, 1);
if (code != CURLE_OK) {
break;
}
code = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request->getRequestData());
if (code != CURLE_OK) {
break;
}
code = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, request->getRequestDataSize());
if (code != CURLE_OK) {
break;
}
code = curl_easy_perform(curl);
if (code != CURLE_OK) {
break;
}
/* free the linked list for header data */
curl_slist_free_all(cHeaders);
code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, responseCode);
if (code != CURLE_OK || *responseCode != 200) {
code = CURLE_HTTP_RETURNED_ERROR;
}
} while (0);
if (curl) {
curl_easy_cleanup(curl);
}
return (code == CURLE_OK ? 0 : 1);
}
// 返回共享实例
CCHttpClient* CCHttpClient::getInstance()
{
if (s_pHttpClient == NULL) {
s_pHttpClient = new CCHttpClient();
}
return s_pHttpClient;
}
void CCHttpClient::destroyInstance()
{
CCAssert(s_pHttpClient, "");
//将CCHttpClient::dispatchResponseCallbacks()函数从CCShecduler中取消挂载
CCDirector::sharedDirector()->getScheduler()->unscheduleSelector(schedule_selector(CCHttpClient::dispatchResponseCallbacks), s_pHttpClient);
s_pHttpClient->release();
}
CCHttpClient::CCHttpClient()
: _timeoutForConnect(30)
, _timeoutForRead(60)
{
//将成员函数dispatchTesponseCallbacks()挂载至CCSheduler
CCDirector::sharedDirector()->getScheduler()->scheduleSelector(
schedule_selector(CCHttpClient::dispatchResponseCallbacks), this, 0, false);
//初始化为停止调度,由工作线程接收到了数据之后启用调度
CCDirector::sharedDirector()->getScheduler()->pauseTarget(this);
}
CCHttpClient::~CCHttpClient()
{
need_quit = true;
if (s_pSem != NULL) {
sem_post(s_pSem);
}
s_pHttpClient = NULL;
}
//只有在第一次调用send()时调用,去初始化队列、创建线程、初始化互斥变量等
bool CCHttpClient::lazyInitThreadSemphore()
{
if (s_pSem != NULL) {
return true;
} else {
#if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE
s_pSem = sem_open(CC_ASYNC_HTTPREQUEST_SEMAPHORE, O_CREAT, 0644, 0);
if (s_pSem == SEM_FAILED) {
CCLog("Open HttpRequest Semaphore failed");
s_pSem = NULL;
return false;
}
#else
int semRet = sem_init(&s_sem, 0, 0);
if (semRet < 0) {
CCLog("Init HttpRequest Semaphore failed");
return false;
}
s_pSem = &s_sem;
#endif
s_requestQueue = new CCArray();
s_requestQueue->init();
s_responseQueue = new CCArray();
s_responseQueue->init();
pthread_mutex_init(&s_requestQueueMutex, NULL);
pthread_mutex_init(&s_responseQueueMutex, NULL);
pthread_create(&s_networkThread, NULL, networkThread, NULL);
pthread_detach(s_networkThread);
need_quit = false;
}
return true;
}
//Add a get task to queue
void CCHttpClient::send(CCHttpRequest* request)
{
//第一次调用的时候初始化
if (false == lazyInitThreadSemphore())
{
return;
}
if (!request)
{
return;
}
//将当前需要处理的request个数++
++s_asyncRequestCount;
//在这里对request进行第一次retain,
request->retain();
//这里request的引用次数为1
pthread_mutex_lock(&s_requestQueueMutex);//request队列加锁
s_requestQueue->addObject(request);//push到request队列
pthread_mutex_unlock(&s_requestQueueMutex);//解锁
// 发送信号唤醒工作线程
sem_post(s_pSem);
}
// 将response队列数据分发
void CCHttpClient::dispatchResponseCallbacks(float delta)
{
// CCLog("CCHttpClient::dispatchResponseCallbacks is running");
CCHttpResponse* response = NULL;
pthread_mutex_lock(&s_responseQueueMutex);//给response队列上锁
if (s_responseQueue->count())
{
response = dynamic_cast(s_responseQueue->objectAtIndex(0));//取出response
s_responseQueue->removeObjectAtIndex(0);//将其从response队列移除
}
pthread_mutex_unlock(&s_responseQueueMutex);//解锁
if (response)
{
--s_asyncRequestCount;
CCHttpRequest *request = response->getHttpRequest();
CCObject *pTarget = request->getTarget();//获取request回调函数的对象
SEL_CallFuncND pSelector = request->getSelector();//获取回调函数
if (pTarget && pSelector)
{
(pTarget->*pSelector)((CCNode *)this, response);//调用回调函数,并把本单例对象和response传递给我们设置在request中的回调函数
}
response->release();
}
if (0 == s_asyncRequestCount) //如果没有没有请求,停止调度
{
CCDirector::sharedDirector()->getScheduler()->pauseTarget(this);
}
}
花了大半天时间,终于写的差不多了,其实我当初是想看看cocos2d-x是怎样封装socket这一块的,结果是这样,用的cURL库。。。
这篇文章是我的处女做,哪里有不好的地方大家提出来共同进步,欢迎交流
本人今年刚毕业,接触cocos2d-x也才两个月(写的不好不要扔砖就好),前一个多月一直在搞粒子系统这块,这几天把改造后的粒子系统工具开发完了,时间稍微多点,就看看其他的模块了,看完了收获也不少,由于经常逛csdn,拜读大神的文章,所以就想着咱也来发表一遍学习心得吧,这样既可以提高自己,也可以和大家交流交流心得,更重要的是我发现写博客可以提升学习的兴趣
好了,就到这里了,以后有时间再继续