cocos2d-x学习篇之网络(http)篇
来源:互联网 发布:淘宝怎样延长付款时间 编辑:程序博客网 时间:2024/05/29 17:26
这段时间接触到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<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,其实这个文件没什么好说的,都有注释
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<std::string> pHeaders) { _headers=pHeaders; } /** Get custom headers **/ inline std::vector<std::string> getHeaders() { return _headers; }protected: // properties HttpRequestType _requestType; /// 请求方式 std::string _url; /// 请求url std::vector<char> _requestData; /// 用于 POST std::string _tag; /// 用户自定义标识,可以用来在response回调中区分request CCObject* _pTarget; /// 回调对象 SEL_CallFuncND _pSelector; /// 回调函数例如 MyLayer::onHttpResponse(CCObject *sender, void *data) void* _pUserData; /// 用户自定义数据,和_tag用法一样,只不过是用途不一样 std::vector<std::string> _headers; /// custom http headers};
HttpResponse.h,这个文件和HttpRequest差不多,没什么好说的
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<char>* 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<char>* 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<char> _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 };
说白了,CCHttpRequest和CCHttpResponse只不过是发送队列中的数据类型,和接收队列中的数据类型,是线程之间传递的参数,下面来说说CCHttpClient
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"#elsestatic sem_t s_sem;#endif#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)typedef int int32_t;#endifstatic 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];//错误提示buffertypedef 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<char> *recvBuffer = (std::vector<char>*)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;}// Prototypesbool 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<CCHttpRequest*>(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<std::string> headers=request->getHeaders(); if(!headers.empty()) { for(std::vector<std::string>::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<std::string> headers=request->getHeaders(); if(!headers.empty()) { for(std::vector<std::string>::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 queuevoid 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<CCHttpResponse*>(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,拜读大神的文章,所以就想着咱也来发表一遍学习心得吧,这样既可以提高自己,也可以和大家交流交流心得,更重要的是我发现写博客可以提升学习的兴趣
好了,就到这里了,以后有时间再继续
- cocos2d-x学习篇之网络(http)篇
- cocos2d-x学习篇之网络(http)篇
- cocos2d-x学习篇之网络(http)篇
- cocos2d-x学习篇之网络(http)篇
- cocos2d-x学习篇之网络(http)篇
- cocos2d-x学习日记之网络通信篇—HttpClient
- Cocos2d-x 之 Http 网络编程
- cocos2d-x 网络请求 Http
- cocos2d-x-LuaProxy学习日志(5) -- Http网络交互
- Cocos2d-x的学习之旅(一)扫盲篇
- Cocos2d-x网络篇01:网络基本概念
- 学习cocos2d-x 之路 (1)--了解cocos2d-x
- Cocos2d-x之 Http/Https
- Cocos2d-x 3.2 Lua示例 XMLHttpRequestTest(Http网络请求)
- Cocos2d-x 3.2 Lua示例 XMLHttpRequestTest(Http网络请求)
- cocos2d-x新手学习之Helloworld(第三篇)[版本:cocos2d-x-3.1.1]
- Cocos2d-x新手学习之TestCpp项目结构分析(第四篇)[版本:cocos2d-x-3.1.1]
- cocos2D-X源码分析之从cocos2D-X学习OpenGL(1)----cocos2D-X渲染结构
- Hadoop-Hadoop应用总结(转载)
- 产品经理必看书籍推荐
- 详解vsftpd搭建ftp和ftps
- 记录一点点。。
- PHP上传文件出现文件名被截掉第一个字符的问题
- cocos2d-x学习篇之网络(http)篇
- Asterisk 11的Web RTC功能
- 让我过着无奈的人生,沉默是最好的选择
- 【Cocos2d-x游戏引擎开发笔记(21)】CCHttpRequest联网
- LA5009三分
- linux Vim的高亮颜色设置
- 将Datatable转化成json发送前台
- JavaScript Set简单实现
- public default protected private的访问权限区别