转自:http://blog.csdn.net/duotianshi86/article/details/10216383
这段时间接触到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:
-
- void HttpClientTest::onMenuGetTestClicked(cocos2d::CCObject *sender)
- {
-
- {
- CCHttpRequest* request = new CCHttpRequest();
- 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");
- CCHttpClient::getInstance()->send(request);
- request->release();
- }
-
-
- m_labelStatusCode->setString("waiting...");
-
- }
-
- void HttpClientTest::onHttpRequestCompleted(cocos2d::CCNode *sender, void *data)
- {
- CCHttpResponse *response = (CCHttpResponse*)data;
-
- if (!response)
- {
- return;
- }
-
-
- if (0 != strlen(response->getHttpRequest()->getTag()))
- {
- CCLog("%s completed", response->getHttpRequest()->getTag());
- }
-
- 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());
- return;
- }
-
-
- std::vector<char> *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,其实这个文件没什么好说的,都有注释
HttpResponse.h,这个文件和HttpRequest差不多,没什么好说的
说白了,CCHttpRequest和CCHttpResponse只不过是发送队列中的数据类型,和接收队列中的数据类型,是线程之间传递的参数,下面来说说CCHttpClient
HttpClient.h
-
- class CCHttpClient : public CCObject
- {
- public:
-
- static CCHttpClient *getInstance();
-
-
- static void destroyInstance();
-
-
-
-
-
-
-
- void send(CCHttpRequest* 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:
- CCHttpClient();
- virtual ~CCHttpClient();
- bool init(void);
-
-
-
-
-
- bool lazyInitThreadSemphore();
-
- void dispatchResponseCallbacks(float delta);
-
- private:
- int _timeoutForConnect;
- int _timeoutForRead;
-
-
- };
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;
-
- #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;
- static CCArray* s_responseQueue = NULL;
-
- static CCHttpClient *s_pHttpClient = NULL;
-
- static char s_errorBuffer[CURL_ERROR_SIZE];
-
- typedef size_t (*write_callback)(void *ptr, size_t size, size_t nmemb, void *stream);
-
-
-
- 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;
-
-
-
- recvBuffer->insert(recvBuffer->end(), (char*)ptr, (char*)ptr+sizes);
-
- return sizes;
- }
-
-
- 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);
-
-
-
-
- static void* networkThread(void *data)
- {
- CCHttpRequest *request = NULL;
-
- while (true)
- {
-
- int semWaitRet = sem_wait(s_pSem);
- if (semWaitRet < 0) {
- CCLog("HttpRequest async thread semaphore error: %s\n", strerror(errno));
- break;
- }
-
- if (need_quit)
- {
- break;
- }
-
-
- request = NULL;
-
- pthread_mutex_lock(&s_requestQueueMutex);
- if (0 != s_requestQueue->count())
- {
- request = dynamic_cast<CCHttpRequest*>(s_requestQueue->objectAtIndex(0));
- s_requestQueue->removeObjectAtIndex(0);
-
- }
- pthread_mutex_unlock(&s_requestQueueMutex);
-
- if (NULL == request)
- {
- continue;
- }
-
-
-
-
- CCHttpResponse *response = new CCHttpResponse(request);
-
-
- request->release();
-
-
- int responseCode = -1;
- int retValue = 0;
-
-
- switch (request->getRequestType())
- {
- case CCHttpRequest::kHttpGet:
- retValue = processGetTask(request,
- writeData,
- response->getResponseData(),
- &responseCode);
- break;
-
- case CCHttpRequest::kHttpPost:
- 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);
- }
-
-
-
- pthread_mutex_lock(&s_responseQueueMutex);
- s_responseQueue->addObject(response);
- pthread_mutex_unlock(&s_responseQueueMutex);
-
-
- CCDirector::sharedDirector()->getScheduler()->resumeTarget(CCHttpClient::getInstance());
- }
-
-
- 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;
- }
-
-
- 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;
- }
-
-
-
-
- int processGetTask(CCHttpRequest *request, write_callback callback, void *stream, int *responseCode)
- {
- CURLcode code = CURL_LAST;
-
- CURL *curl = curl_easy_init();
-
- do {
- if (!configureCURL(curl))
- {
- break;
- }
-
-
-
- struct curl_slist *cHeaders=NULL;
-
- std::vector<std::string> headers=request->getHeaders();
- if(!headers.empty())
- {
- for(std::vector<std::string>::iterator it=headers.begin();it!=headers.end();it++)
- {
-
- cHeaders=curl_slist_append(cHeaders,it->c_str());
- }
-
- 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;
- }
-
- code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, stream);
- if (code != CURLE_OK)
- {
- break;
- }
-
- code = curl_easy_perform(curl);
- if (code != CURLE_OK)
- {
- break;
- }
-
-
- 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);
- }
-
-
- 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;
- }
-
-
-
- struct curl_slist *cHeaders=NULL;
-
- std::vector<std::string> headers=request->getHeaders();
- if(!headers.empty())
- {
- for(std::vector<std::string>::iterator it=headers.begin();it!=headers.end();it++)
- {
-
- cHeaders=curl_slist_append(cHeaders,it->c_str());
- }
-
- 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;
- }
-
-
- 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, "");
-
- CCDirector::sharedDirector()->getScheduler()->unscheduleSelector(schedule_selector(CCHttpClient::dispatchResponseCallbacks), s_pHttpClient);
- s_pHttpClient->release();
- }
-
- CCHttpClient::CCHttpClient()
- : _timeoutForConnect(30)
- , _timeoutForRead(60)
- {
-
- 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;
- }
-
-
- 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;
- }
-
-
- void CCHttpClient::send(CCHttpRequest* request)
- {
-
- if (false == lazyInitThreadSemphore())
- {
- return;
- }
-
- if (!request)
- {
- return;
- }
-
- ++s_asyncRequestCount;
-
- request->retain();
-
- pthread_mutex_lock(&s_requestQueueMutex);
- s_requestQueue->addObject(request);
- pthread_mutex_unlock(&s_requestQueueMutex);
-
-
- sem_post(s_pSem);
- }
-
-
- void CCHttpClient::dispatchResponseCallbacks(float delta)
- {
-
-
- CCHttpResponse* response = NULL;
-
- pthread_mutex_lock(&s_responseQueueMutex);
- if (s_responseQueue->count())
- {
- response = dynamic_cast<CCHttpResponse*>(s_responseQueue->objectAtIndex(0));
- s_responseQueue->removeObjectAtIndex(0);
- }
- pthread_mutex_unlock(&s_responseQueueMutex);
-
- if (response)
- {
- --s_asyncRequestCount;
-
- CCHttpRequest *request = response->getHttpRequest();
- CCObject *pTarget = request->getTarget();
- SEL_CallFuncND pSelector = request->getSelector();
-
- if (pTarget && pSelector)
- {
- (pTarget->*pSelector)((CCNode *)this, response);
- }
-
- response->release();
- }
-
- if (0 == s_asyncRequestCount)
- {
- CCDirector::sharedDirector()->getScheduler()->pauseTarget(this);
- }
-
- }
花了大半天时间,终于写的差不多了,其实我当初是想看看cocos2d-x是怎样封装socket这一块的,结果是这样,用的cURL库。。。
这篇文章是我的处女做,哪里有不好的地方大家提出来共同进步,欢迎交流
本人今年刚毕业,接触cocos2d-x也才两个月(写的不好不要扔砖就好),前一个多月一直在搞粒子系统这块,这几天把改造后的粒子系统工具开发完了,时间稍微多点,就看看其他的模块了,看完了收获也不少,由于经常逛csdn,拜读大神的文章,所以就想着咱也来发表一遍学习心得吧,这样既可以提高自己,也可以和大家交流交流心得,更重要的是我发现写博客可以提升学习的兴趣
好了,就到这里了,以后有时间再继续