cocos2d-x学习篇之网络(http)篇

转自: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:

[cpp]  view plain copy
  1. //get请求  
  2. void HttpClientTest::onMenuGetTestClicked(cocos2d::CCObject *sender)  
  3. {      
  4.     // test 1  
  5.     {  
  6.         CCHttpRequest* request = new CCHttpRequest();//创建request对象,这里new出来的对象不能使用autorelease(),原因后述  
  7.         request->setUrl("http://just-make-this-request-failed.com");//设置url  
  8.         request->setRequestType(CCHttpRequest::kHttpGet);//设置请求方式  
  9.         request->setResponseCallback(this, callfuncND_selector(HttpClientTest::onHttpRequestCompleted));//这是回调对象和回调函数  
  10.         request->setTag("GET test1");//设置用户标识,可以通过response获取  
  11.         CCHttpClient::getInstance()->send(request);//使用CCHttpClient共享实例来发送request  
  12.         request->release();//调用release()  
  13.     }  
  14.   
  15.     // waiting  
  16.     m_labelStatusCode->setString("waiting...");  
  17.    
  18. }  
  19. //这里就是我们要处理接收到数据的回调函数了,sender为CCHttpClient实例指针,data为接收到的response指针  
  20. void HttpClientTest::onHttpRequestCompleted(cocos2d::CCNode *sender, void *data)  
  21. {  
  22.     CCHttpResponse *response = (CCHttpResponse*)data;  
  23.   
  24.     if (!response)  
  25.     {  
  26.         return;  
  27.     }  
  28.       
  29.     // 获取对应request的字符串标识  
  30.     if (0 != strlen(response->getHttpRequest()->getTag()))   
  31.     {  
  32.         CCLog("%s completed", response->getHttpRequest()->getTag());  
  33.     }  
  34.     //获取返回代码,比如200、404等  
  35.     int statusCode = response->getResponseCode();  
  36.     char statusString[64] = {};  
  37.     sprintf(statusString, "HTTP Status Code: %d, tag = %s", statusCode, response->getHttpRequest()->getTag());  
  38.     m_labelStatusCode->setString(statusString);  
  39.     CCLog("response code: %d", statusCode);  
  40.       
  41.     if (!response->isSucceed())   
  42.     {  
  43.         CCLog("response failed");  
  44.         CCLog("error buffer: %s", response->getErrorBuffer());//可以调用getErrorBuffer()来获取错误原因  
  45.         return;  
  46.     }  
  47.       
  48.     // dump data  
  49.     std::vector<char> *buffer = response->getResponseData();//用来获取接收到的数据  
  50.     printf("Http Test, dump data: ");  
  51.     for (unsigned int i = 0; i < buffer->size(); i++)  
  52.     {  
  53.         printf("%c", (*buffer)[i]);  
  54.     }  
  55.     printf("\n");  
  56. }  


基本上一个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,其实这个文件没什么好说的,都有注释

[cpp]  view plain copy
  1. class CCHttpRequest : public CCObject  
  2. {  
  3. public:  
  4.     /** 请求类型枚举,可以通过setReqeustType(param) 设置*/  
  5.     typedef enum  
  6.     {  
  7.         kHttpGet,  
  8.         kHttpPost,  
  9.         kHttpUnkown,  
  10.     } HttpRequestType;  
  11.       
  12.     /** Constructor  
  13.         Because HttpRequest object will be used between UI thead and network thread, 
  14.         requestObj->autorelease() is forbidden to avoid crashes in CCAutoreleasePool 
  15.         new/retain/release still works, which means you need to release it manually 
  16.         Please refer to HttpRequestTest.cpp to find its usage 
  17.     这里是有注释的,因为要跨线程,所以就不能用autorelease() 
  18.     我们在使用HttpRequest的时候,需要自己new,然后再release下就可以了 
  19.     当我们把HttpRequest传递给CCHttpClient的时候,CCHttpClient已经帮我们retain了 
  20.     工作线程中,需要使用CCHttpRequest对象new一个CCHttpResponse,CCHttprequest会retain一次,所以工作线程也会release一次 
  21.     具体的后文有 
  22.      */  
  23.     CCHttpRequest()  
  24.     {  
  25.         _requestType = kHttpUnkown;  
  26.         _url.clear();  
  27.         _requestData.clear();  
  28.         _tag.clear();  
  29.         _pTarget = NULL;  
  30.         _pSelector = NULL;  
  31.         _pUserData = NULL;  
  32.     };  
  33.       
  34.     virtual ~CCHttpRequest()  
  35.     {  
  36.         if (_pTarget)  
  37.         {  
  38.             _pTarget->release();  
  39.         }  
  40.     };  
  41.       
  42.     /** 重载autorelease函数,禁止调用 */  
  43.     CCObject* autorelease(void)  
  44.     {  
  45.         CCAssert(false, "HttpResponse is used between network thread and ui thread \  
  46.                  therefore, autorelease is forbidden here");  
  47.         return NULL;  
  48.     }  
  49.               
  50.     // setter/getters for properties  
  51.        
  52.     /** 设置请求类型 
  53.         目前支持kHttpGet 和 kHttpPost 
  54.      */  
  55.     inline void setRequestType(HttpRequestType type)  
  56.     {  
  57.         _requestType = type;  
  58.     };  
  59.     /** 返回请求类型 */  
  60.     inline HttpRequestType getRequestType()  
  61.     {  
  62.         return _requestType;  
  63.     };  
  64.       
  65.     /** 设置请求url 
  66.      */  
  67.     inline void setUrl(const char* url)  
  68.     {  
  69.         _url = url;  
  70.     };  
  71.     /** 获取请求url */  
  72.     inline const char* getUrl()  
  73.     {  
  74.         return _url.c_str();  
  75.     };  
  76.       
  77.     /** 这个设置用于post方式的data数据 
  78.      */  
  79.     inline void setRequestData(const char* buffer, unsigned int len)  
  80.     {  
  81.         _requestData.assign(buffer, buffer + len);  
  82.     };  
  83.     /** Get the request data pointer back */  
  84.     inline char* getRequestData()  
  85.     {  
  86.         return &(_requestData.front());  
  87.     }  
  88.     /** Get the size of request data back */  
  89.     inline int getRequestDataSize()  
  90.     {  
  91.         return _requestData.size();  
  92.     }  
  93.       
  94.     /** 为每个请求设置一个字符串标示,可以通过HttpResponse->getHttpRequest->getTag()获取,因为HttpResponse会将对应的HttpRequest封装在里面 
  95.      */  
  96.     inline void setTag(const char* tag)  
  97.     {  
  98.         _tag = tag;  
  99.     };  
  100.     /** Get the string tag back to identify the request.  
  101.         The best practice is to use it in your MyClass::onMyHttpRequestCompleted(sender, HttpResponse*) callback 
  102.      */  
  103.     inline const char* getTag()  
  104.     {  
  105.         return _tag.c_str();  
  106.     };  
  107.       
  108.     /** Option field. You can attach a customed data in each request, and get it back in response callback. 
  109.         But you need to new/delete the data pointer manully 
  110.      */  
  111.     inline void setUserData(void* pUserData)  
  112.     {  
  113.         _pUserData = pUserData;  
  114.     };  
  115.     /** Get the pre-setted custom data pointer back. 
  116.         Don't forget to delete it. HttpClient/HttpResponse/HttpRequest will do nothing with this pointer 
  117.      */  
  118.     inline void* getUserData()  
  119.     {  
  120.         return _pUserData;  
  121.     };  
  122.       
  123.     /** 通过这个函数设置我们的数据处理回调函数 
  124.      */  
  125.     inline void setResponseCallback(CCObject* pTarget, SEL_CallFuncND pSelector)  
  126.     {  
  127.         _pTarget = pTarget;  
  128.         _pSelector = pSelector;  
  129.           
  130.         if (_pTarget)  
  131.         {  
  132.             _pTarget->retain();  
  133.         }  
  134.     }      
  135.     /** Get the target of callback selector funtion, mainly used by CCHttpClient */  
  136.     inline CCObject* getTarget()  
  137.     {  
  138.         return _pTarget;  
  139.     }  
  140.     /** Get the selector function pointer, mainly used by CCHttpClient */  
  141.     inline SEL_CallFuncND getSelector()  
  142.     {  
  143.         return _pSelector;  
  144.     }  
  145.   
  146.     /** Set any custom headers **/  
  147.     inline void setHeaders(std::vector<std::string> pHeaders)  
  148.     {  
  149.         _headers=pHeaders;  
  150.     }  
  151.      
  152.     /** Get custom headers **/  
  153.     inline std::vector<std::string> getHeaders()  
  154.     {  
  155.         return _headers;  
  156.     }  
  157.   
  158.   
  159. protected:  
  160.     // properties  
  161.     HttpRequestType             _requestType;    /// 请求方式  
  162.     std::string                 _url;            /// 请求url  
  163.     std::vector<char>           _requestData;    /// 用于 POST  
  164.     std::string                 _tag;            /// 用户自定义标识,可以用来在response回调中区分request  
  165.     CCObject*          _pTarget;        /// 回调对象  
  166.     SEL_CallFuncND     _pSelector;      /// 回调函数例如 MyLayer::onHttpResponse(CCObject *sender, void *data)  
  167.     void*                       _pUserData;      /// 用户自定义数据,和_tag用法一样,只不过是用途不一样  
  168.     std::vector<std::string>    _headers;           /// custom http headers  
  169. };  

HttpResponse.h,这个文件和HttpRequest差不多,没什么好说的

[cpp]  view plain copy
  1. class CCHttpResponse : public CCObject  
  2. {  
  3. public:  
  4.     /** Constructor, it's used by CCHttpClient internal, users don't need to create HttpResponse manually 
  5.      @param request the corresponding HttpRequest which leads to this response  
  6.      */  
  7.     CCHttpResponse(CCHttpRequest* request)  
  8.     {  
  9.         _pHttpRequest = request;  
  10.         if (_pHttpRequest)  
  11.         {  
  12.             _pHttpRequest->retain();  
  13.         }  
  14.           
  15.         _succeed = false;  
  16.         _responseData.clear();  
  17.         _errorBuffer.clear();  
  18.     }  
  19.       
  20.     /** Destructor, it will be called in CCHttpClient internal, 
  21.      users don't need to desturct HttpResponse object manully  
  22.      */  
  23.     virtual ~CCHttpResponse()  
  24.     {  
  25.         if (_pHttpRequest)  
  26.         {  
  27.             _pHttpRequest->release();  
  28.         }  
  29.     }  
  30.       
  31.     /** Override autorelease method to prevent developers from calling it */  
  32.     CCObject* autorelease(void)  
  33.     {  
  34.         CCAssert(false, "HttpResponse is used between network thread and ui thread \  
  35.                         therefore, autorelease is forbidden here");  
  36.         return NULL;  
  37.     }  
  38.       
  39.     // getters, will be called by users  
  40.       
  41.     /** Get the corresponding HttpRequest object which leads to this response  
  42.         There's no paired setter for it, coz it's already setted in class constructor 
  43.      */  
  44.     inline CCHttpRequest* getHttpRequest()  
  45.     {  
  46.         return _pHttpRequest;  
  47.     }  
  48.           
  49.     /** To see if the http reqeust is returned successfully, 
  50.         Althrough users can judge if (http return code = 200), we want an easier way 
  51.         If this getter returns false, you can call getResponseCode and getErrorBuffer to find more details 
  52.      */  
  53.     inline bool isSucceed()  
  54.     {  
  55.         return _succeed;  
  56.     };  
  57.       
  58.     /** Get the http response raw data */  
  59.     inline std::vector<char>* getResponseData()  
  60.     {  
  61.         return &_responseData;  
  62.     }  
  63.   
  64.     /** Get the http response errorCode 
  65.      *  I know that you want to see http 200 :) 
  66.      */  
  67.     inline int getResponseCode()  
  68.     {  
  69.         return _responseCode;  
  70.     }  
  71.   
  72.     /** Get the rror buffer which will tell you more about the reason why http request failed 
  73.      */  
  74.     inline const char* getErrorBuffer()  
  75.     {  
  76.         return _errorBuffer.c_str();  
  77.     }  
  78.       
  79.     // setters, will be called by CCHttpClient  
  80.     // users should avoid invoking these methods  
  81.       
  82.       
  83.     /** Set if the http request is returned successfully, 
  84.      Althrough users can judge if (http code == 200), we want a easier way 
  85.      This setter is mainly used in CCHttpClient, users mustn't set it directly 
  86.      */  
  87.     inline void setSucceed(bool value)  
  88.     {  
  89.         _succeed = value;  
  90.     };  
  91.       
  92.       
  93.     /** Set the http response raw buffer, is used by CCHttpClient       
  94.      */  
  95.     inline void setResponseData(std::vector<char>* data)  
  96.     {  
  97.         _responseData = *data;  
  98.     }  
  99.       
  100.       
  101.     /** Set the http response errorCode 
  102.      */  
  103.     inline void setResponseCode(int value)  
  104.     {  
  105.         _responseCode = value;  
  106.     }  
  107.       
  108.       
  109.     /** Set the error buffer which will tell you more the reason why http request failed 
  110.      */  
  111.     inline void setErrorBuffer(const char* value)  
  112.     {  
  113.         _errorBuffer.clear();  
  114.         _errorBuffer.assign(value);  
  115.     };  
  116.       
  117. protected:  
  118.     bool initWithRequest(CCHttpRequest* request);  
  119.       
  120.     // properties  
  121.     //这里要留意下,每个response中都会包含对应的request,所以能在数据处理回调函数中,获取我们在设置request的所有参数,比如像tag,userdata  
  122.     CCHttpRequest*        _pHttpRequest;  /// the corresponding HttpRequest pointer who leads to this response   
  123.     bool                _succeed;       /// to indecate if the http reqeust is successful simply  
  124.     std::vector<char>   _responseData;  /// the returned raw data. You can also dump it as a string  
  125.     int                 _responseCode;    /// the status code returned from libcurl, e.g. 200, 404  
  126.     std::string         _errorBuffer;   /// if _responseCode != 200, please read _errorBuffer to find the reason   
  127.       
  128. };  

说白了,CCHttpRequest和CCHttpResponse只不过是发送队列中的数据类型,和接收队列中的数据类型,是线程之间传递的参数,下面来说说CCHttpClient

HttpClient.h

[cpp]  view plain copy
  1. //CCHttpClient是一个单例模式的类,整个程序共享一个实例对象  
  2. class CCHttpClient : public CCObject  
  3. {  
  4. public:  
  5.     /** 获取共享的单例对象 **/  
  6.     static CCHttpClient *getInstance();  
  7.       
  8.     /** Relase the shared instance **/  
  9.     static void destroyInstance();  
  10.           
  11.     /** 
  12.      * Add a get request to task queue 
  13.      * @param request a CCHttpRequest object, which includes url, response callback etc. 
  14.                       please make sure request->_requestData is clear before calling "send" here. 
  15.      * @return NULL 
  16.      */  
  17.     void send(CCHttpRequest* request);  
  18.     
  19.       
  20.     /** 
  21.      * Change the connect timeout 
  22.      * @param timeout  
  23.      * @return NULL 
  24.      */  
  25.     inline void setTimeoutForConnect(int value) {_timeoutForConnect = value;};  
  26.       
  27.     /** 
  28.      * Get connect timeout 
  29.      * @return int 
  30.      * 
  31.      */  
  32.     inline int getTimeoutForConnect() {return _timeoutForConnect;}  
  33.       
  34.       
  35.     /** 
  36.      * Change the download timeout 
  37.      * @param value 
  38.      * @return NULL 
  39.      */  
  40.     inline void setTimeoutForRead(int value) {_timeoutForRead = value;};  
  41.       
  42.   
  43.     /** 
  44.      * Get download timeout 
  45.      * @return int 
  46.      */  
  47.     inline int getTimeoutForRead() {return _timeoutForRead;};  
  48.           
  49. private:  
  50.     CCHttpClient();  
  51.     virtual ~CCHttpClient();  
  52.     bool init(void);  
  53.       
  54.     /** 
  55.      * Init pthread mutex, semaphore, and create new thread for http requests 
  56.      * @return bool 
  57.      */  
  58.     bool lazyInitThreadSemphore();  
  59.     /** Poll function called from main thread to dispatch callbacks when http requests finished **/  
  60.     void dispatchResponseCallbacks(float delta);  
  61.       
  62. private:  
  63.     int _timeoutForConnect;//连接超时时间  
  64.     int _timeoutForRead;//接收数据超时时间  
  65.       
  66.     // std::string reqId;  
  67. };  


HttpClient.cpp

[cpp]  view plain copy
  1. static pthread_t        s_networkThread;//工作线程句柄  
  2. static pthread_mutex_t  s_requestQueueMutex;//请求队列互斥变量  
  3. static pthread_mutex_t  s_responseQueueMutex;//接收队列互斥变量  
  4. static sem_t *          s_pSem = NULL;//用来驱动线程工作的信号量  
  5. static unsigned long    s_asyncRequestCount = 0;//当前需要处理的request个数  
  6.   
  7. #if CC_TARGET_PLATFORM == CC_PLATFORM_IOS  
  8. #define CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 1  
  9. #else  
  10. #define CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 0  
  11. #endif  
  12.   
  13. #if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE  
  14. #define CC_ASYNC_HTTPREQUEST_SEMAPHORE "ccHttpAsync"  
  15. #else  
  16. static sem_t s_sem;  
  17. #endif  
  18.   
  19. #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)  
  20. typedef int int32_t;  
  21. #endif  
  22.   
  23. static bool need_quit = false;      //退出标识  
  24.   
  25. static CCArray* s_requestQueue = NULL;  //请求队列(下面都说request队列)  
  26. static CCArray* s_responseQueue = NULL; //接收队列(下面都说response队列)  
  27.   
  28. static CCHttpClient *s_pHttpClient = NULL; // 全局单例CCHttpClient对象  
  29.   
  30. static char s_errorBuffer[CURL_ERROR_SIZE];//错误提示buffer  
  31.   
  32. typedef size_t (*write_callback)(void *ptr, size_t size, size_t nmemb, void *stream);//这个是用于cURL收到数据的回调函数  
  33.   
  34.   
  35. // 这个便是当cURL接收到数据回调的函数,也就是在这里对response进行填充,这里的声明方式和fwrite()函数一样  
  36. size_t writeData(void *ptr, size_t size, size_t nmemb, void *stream)  
  37. {  
  38.     //ptr指向接受到的数据,sizes为字节数  
  39.     //这里传过来的stream中保存了CCHttpResponse::_responseData  
  40.     std::vector<char> *recvBuffer = (std::vector<char>*)stream;  
  41.     size_t sizes = size * nmemb;  
  42.       
  43.     // add data to the end of recvBuffer  
  44.     // 将接受到的数据写到response中去  
  45.     recvBuffer->insert(recvBuffer->end(), (char*)ptr, (char*)ptr+sizes);  
  46.       
  47.     return sizes;  
  48. }  
  49.   
  50. // Prototypes  
  51. bool configureCURL(CURL *handle);  
  52. int processGetTask(CCHttpRequest *request, write_callback callback, void *stream, int32_t *errorCode);  
  53. int processPostTask(CCHttpRequest *request, write_callback callback, void *stream, int32_t *errorCode);  
  54. // int processDownloadTask(HttpRequest *task, write_callback callback, void *stream, int32_t *errorCode);  
  55.   
  56.   
  57. // 工作线程  
  58. static void* networkThread(void *data)  
  59. {      
  60.     CCHttpRequest *request = NULL;  
  61.       
  62.     while (true)   
  63.     {  
  64.         // 等待主线程发送信号,就是调用send()函数  
  65.         int semWaitRet = sem_wait(s_pSem);  
  66.         if (semWaitRet < 0) {  
  67.             CCLog("HttpRequest async thread semaphore error: %s\n", strerror(errno));  
  68.             break;  
  69.         }  
  70.         //退出  
  71.         if (need_quit)  
  72.         {  
  73.             break;  
  74.         }  
  75.           
  76.         // step 1: send http request if the requestQueue isn't empty  
  77.         request = NULL;  
  78.           
  79.         pthread_mutex_lock(&s_requestQueueMutex); //给request队列上锁  
  80.         if (0 != s_requestQueue->count())  
  81.         {  
  82.             request = dynamic_cast<CCHttpRequest*>(s_requestQueue->objectAtIndex(0));//取得第一个request  
  83.             s_requestQueue->removeObjectAtIndex(0);  //将其移除队列  
  84.             // 这里的request的引用次数为1,因为只有在send()函数中retain了一次  
  85.         }  
  86.         pthread_mutex_unlock(&s_requestQueueMutex);//request队列解锁  
  87.           
  88.         if (NULL == request)  
  89.         {  
  90.             continue;  
  91.         }  
  92.           
  93.         // 同步调用cURL库  
  94.           
  95.         // 使用request来创建一个response  
  96.         CCHttpResponse *response = new CCHttpResponse(request);  
  97.           
  98.         // 在CCHttpTtpResponse构造中,会将request再retain一次  
  99.         request->release();  
  100.         // 这里,只有response中有request的一次引用计数  
  101.           
  102.         int responseCode = -1;  
  103.         int retValue = 0;  
  104.   
  105.         // 根据请求类型设置cURL参数  
  106.         switch (request->getRequestType())  
  107.         {  
  108.             case CCHttpRequest::kHttpGet: // HTTP GET  
  109.                 retValue = processGetTask(request,   
  110.                                           writeData,   
  111.                                           response->getResponseData(),   
  112.                                           &responseCode);  
  113.                 break;  
  114.               
  115.             case CCHttpRequest::kHttpPost: // HTTP POST  
  116.                 retValue = processPostTask(request,   
  117.                                            writeData,   
  118.                                            response->getResponseData(),   
  119.                                            &responseCode);  
  120.                 break;  
  121.               
  122.             default:  
  123.                 CCAssert(true"CCHttpClient: unkown request type, only GET and POSt are supported");  
  124.                 break;  
  125.         }  
  126.                   
  127.         // 设置返回代码  
  128.         response->setResponseCode(responseCode);  
  129.           
  130.         if (retValue != 0)   
  131.         {  
  132.             response->setSucceed(false);  
  133.             response->setErrorBuffer(s_errorBuffer);  
  134.         }  
  135.         else  
  136.         {  
  137.             response->setSucceed(true);  
  138.         }  
  139.   
  140.           
  141.         // 将response加入队列  
  142.         pthread_mutex_lock(&s_responseQueueMutex);//给response加锁  
  143.         s_responseQueue->addObject(response);  
  144.         pthread_mutex_unlock(&s_responseQueueMutex);//解锁  
  145.           
  146.         // 启动CCScheduler调度  
  147.         CCDirector::sharedDirector()->getScheduler()->resumeTarget(CCHttpClient::getInstance());  
  148.     }  
  149.       
  150.     // 线程退出,清理request队列  
  151.     pthread_mutex_lock(&s_requestQueueMutex);  
  152.     s_requestQueue->removeAllObjects();  
  153.     pthread_mutex_unlock(&s_requestQueueMutex);  
  154.     s_asyncRequestCount -= s_requestQueue->count();  
  155.       
  156.     if (s_pSem != NULL) {  
  157. #if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE  
  158.         sem_unlink(CC_ASYNC_HTTPREQUEST_SEMAPHORE);  
  159.         sem_close(s_pSem);  
  160. #else  
  161.         sem_destroy(s_pSem);  
  162. #endif  
  163.           
  164.         s_pSem = NULL;  
  165.         //释放互斥变量  
  166.         pthread_mutex_destroy(&s_requestQueueMutex);  
  167.         pthread_mutex_destroy(&s_responseQueueMutex);  
  168.           
  169.         s_requestQueue->release();  
  170.         s_responseQueue->release();  
  171.     }  
  172.   
  173.     pthread_exit(NULL);  
  174.       
  175.     return 0;  
  176. }  
  177.   
  178. //设置cURL超时属性  
  179. bool configureCURL(CURL *handle)  
  180. {  
  181.     if (!handle) {  
  182.         return false;  
  183.     }  
  184.       
  185.     int32_t code;  
  186.     //设置错误信息缓冲  
  187.     code = curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, s_errorBuffer);  
  188.     if (code != CURLE_OK) {  
  189.         return false;  
  190.     }  
  191.     //设置超时时间  
  192.     code = curl_easy_setopt(handle, CURLOPT_TIMEOUT, CCHttpClient::getInstance()->getTimeoutForRead());  
  193.     if (code != CURLE_OK) {  
  194.         return false;  
  195.     }  
  196.     code = curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, CCHttpClient::getInstance()->getTimeoutForConnect());  
  197.     if (code != CURLE_OK) {  
  198.         return false;  
  199.     }  
  200.       
  201.     return true;  
  202. }  
  203.   
  204. //处理get方式请求  
  205. //stream传递过来的是response->getResponseData()  
  206. //关于cURL的东西这里就不多说了  
  207. int processGetTask(CCHttpRequest *request, write_callback callback, void *stream, int *responseCode)  
  208. {  
  209.     CURLcode code = CURL_LAST;  
  210.     //初始化cURL  
  211.     CURL *curl = curl_easy_init();  
  212.       
  213.     do {  
  214.         if (!configureCURL(curl)) //配置cURL  
  215.         {  
  216.             break;  
  217.         }  
  218.           
  219.         /* handle custom header data */  
  220.         /* create curl linked list */  
  221.         struct curl_slist *cHeaders=NULL;  
  222.         /* get custom header data (if set) */  
  223.         std::vector<std::string> headers=request->getHeaders();  
  224.             if(!headers.empty())  
  225.             {                 
  226.                     for(std::vector<std::string>::iterator it=headers.begin();it!=headers.end();it++)  
  227.                     {  
  228.               /* append custom headers one by one */  
  229.                         cHeaders=curl_slist_append(cHeaders,it->c_str());  
  230.                     }  
  231.            /* set custom headers for curl */  
  232.                     code = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, cHeaders);  
  233.                     if (code != CURLE_OK) {  
  234.                         break;  
  235.                     }  
  236.             }  
  237.                 
  238.         code = curl_easy_setopt(curl, CURLOPT_URL, request->getUrl());  
  239.         if (code != CURLE_OK)   
  240.         {  
  241.             break;  
  242.         }  
  243.           
  244.         code = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, request->getUrl());  
  245.         if (code != CURLE_OK)  
  246.         {  
  247.             break;  
  248.         }  
  249.   
  250.         code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);  
  251.         if (code != CURLE_OK)   
  252.         {  
  253.             break;  
  254.         }  
  255.         //这里将response->_responseData设置为cURL回调函数中的stream参数  
  256.         code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, stream);  
  257.         if (code != CURLE_OK)   
  258.         {  
  259.             break;  
  260.         }  
  261.           
  262.         code = curl_easy_perform(curl);  
  263.         if (code != CURLE_OK)   
  264.         {  
  265.             break;  
  266.         }  
  267.           
  268.         /* free the linked list for header data */  
  269.         curl_slist_free_all(cHeaders);  
  270.   
  271.         code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, responseCode);   
  272.         if (code != CURLE_OK || *responseCode != 200)   
  273.         {  
  274.             code = CURLE_HTTP_RETURNED_ERROR;  
  275.         }  
  276.     } while (0);  
  277.       
  278.     if (curl) {  
  279.         curl_easy_cleanup(curl);  
  280.     }  
  281.       
  282.     return (code == CURLE_OK ? 0 : 1);  
  283. }  
  284.   
  285. //这个就不说了,其实都一样的,cURL承担了所有工作  
  286. int processPostTask(CCHttpRequest *request, write_callback callback, void *stream, int32_t *responseCode)  
  287. {  
  288.     CURLcode code = CURL_LAST;  
  289.     CURL *curl = curl_easy_init();  
  290.       
  291.     do {  
  292.         if (!configureCURL(curl)) {  
  293.             break;  
  294.         }  
  295.           
  296.         /* handle custom header data */  
  297.         /* create curl linked list */  
  298.         struct curl_slist *cHeaders=NULL;  
  299.         /* get custom header data (if set) */  
  300.             std::vector<std::string> headers=request->getHeaders();  
  301.             if(!headers.empty())  
  302.             {                 
  303.                     for(std::vector<std::string>::iterator it=headers.begin();it!=headers.end();it++)  
  304.                     {  
  305.               /* append custom headers one by one */  
  306.                         cHeaders=curl_slist_append(cHeaders,it->c_str());  
  307.                     }  
  308.            /* set custom headers for curl */  
  309.                     code = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, cHeaders);  
  310.                     if (code != CURLE_OK) {  
  311.                         break;  
  312.                     }  
  313.             }  
  314.                 
  315.         code = curl_easy_setopt(curl, CURLOPT_URL, request->getUrl());  
  316.         if (code != CURLE_OK) {  
  317.             break;  
  318.         }  
  319.         code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);  
  320.         if (code != CURLE_OK) {  
  321.             break;  
  322.         }  
  323.         code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, stream);  
  324.         if (code != CURLE_OK) {  
  325.             break;  
  326.         }  
  327.         code = curl_easy_setopt(curl, CURLOPT_POST, 1);  
  328.         if (code != CURLE_OK) {  
  329.             break;  
  330.         }  
  331.         code = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request->getRequestData());  
  332.         if (code != CURLE_OK) {  
  333.             break;  
  334.         }  
  335.         code = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, request->getRequestDataSize());  
  336.         if (code != CURLE_OK) {  
  337.             break;  
  338.         }  
  339.         code = curl_easy_perform(curl);  
  340.         if (code != CURLE_OK) {  
  341.             break;  
  342.         }  
  343.           
  344.         /* free the linked list for header data */  
  345.         curl_slist_free_all(cHeaders);  
  346.   
  347.         code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, responseCode);   
  348.         if (code != CURLE_OK || *responseCode != 200) {  
  349.             code = CURLE_HTTP_RETURNED_ERROR;  
  350.         }  
  351.     } while (0);  
  352.     if (curl) {  
  353.         curl_easy_cleanup(curl);  
  354.     }  
  355.       
  356.     return (code == CURLE_OK ? 0 : 1);      
  357. }  
  358.   
  359. // 返回共享实例  
  360. CCHttpClient* CCHttpClient::getInstance()  
  361. {  
  362.     if (s_pHttpClient == NULL) {  
  363.         s_pHttpClient = new CCHttpClient();  
  364.     }  
  365.       
  366.     return s_pHttpClient;  
  367. }  
  368.   
  369. void CCHttpClient::destroyInstance()  
  370. {  
  371.     CCAssert(s_pHttpClient, "");  
  372.     //将CCHttpClient::dispatchResponseCallbacks()函数从CCShecduler中取消挂载  
  373.     CCDirector::sharedDirector()->getScheduler()->unscheduleSelector(schedule_selector(CCHttpClient::dispatchResponseCallbacks), s_pHttpClient);  
  374.     s_pHttpClient->release();  
  375. }  
  376.   
  377. CCHttpClient::CCHttpClient()  
  378. : _timeoutForConnect(30)  
  379. , _timeoutForRead(60)  
  380. {  
  381.     //将成员函数dispatchTesponseCallbacks()挂载至CCSheduler  
  382.     CCDirector::sharedDirector()->getScheduler()->scheduleSelector(  
  383.                     schedule_selector(CCHttpClient::dispatchResponseCallbacks), this, 0, false);  
  384.     //初始化为停止调度,由工作线程接收到了数据之后启用调度  
  385.     CCDirector::sharedDirector()->getScheduler()->pauseTarget(this);  
  386. }  
  387.   
  388. CCHttpClient::~CCHttpClient()  
  389. {  
  390.     need_quit = true;  
  391.       
  392.     if (s_pSem != NULL) {  
  393.         sem_post(s_pSem);  
  394.     }  
  395.       
  396.     s_pHttpClient = NULL;  
  397. }  
  398.   
  399. //只有在第一次调用send()时调用,去初始化队列、创建线程、初始化互斥变量等  
  400. bool CCHttpClient::lazyInitThreadSemphore()  
  401. {  
  402.     if (s_pSem != NULL) {  
  403.         return true;  
  404.     } else {  
  405. #if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE  
  406.         s_pSem = sem_open(CC_ASYNC_HTTPREQUEST_SEMAPHORE, O_CREAT, 0644, 0);  
  407.         if (s_pSem == SEM_FAILED) {  
  408.             CCLog("Open HttpRequest Semaphore failed");  
  409.             s_pSem = NULL;  
  410.             return false;  
  411.         }  
  412. #else  
  413.         int semRet = sem_init(&s_sem, 0, 0);  
  414.         if (semRet < 0) {  
  415.             CCLog("Init HttpRequest Semaphore failed");  
  416.             return false;  
  417.         }  
  418.           
  419.         s_pSem = &s_sem;  
  420. #endif  
  421.           
  422.         s_requestQueue = new CCArray();  
  423.         s_requestQueue->init();  
  424.           
  425.         s_responseQueue = new CCArray();  
  426.         s_responseQueue->init();  
  427.           
  428.         pthread_mutex_init(&s_requestQueueMutex, NULL);  
  429.         pthread_mutex_init(&s_responseQueueMutex, NULL);  
  430.           
  431.         pthread_create(&s_networkThread, NULL, networkThread, NULL);  
  432.         pthread_detach(s_networkThread);  
  433.           
  434.         need_quit = false;  
  435.     }  
  436.       
  437.     return true;  
  438. }  
  439.   
  440. //Add a get task to queue  
  441. void CCHttpClient::send(CCHttpRequest* request)  
  442. {      
  443.     //第一次调用的时候初始化  
  444.     if (false == lazyInitThreadSemphore())   
  445.     {  
  446.         return;  
  447.     }  
  448.       
  449.     if (!request)  
  450.     {  
  451.         return;  
  452.     }  
  453.     //将当前需要处理的request个数++  
  454.     ++s_asyncRequestCount;  
  455.     //在这里对request进行第一次retain,  
  456.     request->retain();  
  457.     //这里request的引用次数为1  
  458.     pthread_mutex_lock(&s_requestQueueMutex);//request队列加锁  
  459.     s_requestQueue->addObject(request);//push到request队列  
  460.     pthread_mutex_unlock(&s_requestQueueMutex);//解锁  
  461.       
  462.     // 发送信号唤醒工作线程  
  463.     sem_post(s_pSem);  
  464. }  
  465.   
  466. // 将response队列数据分发  
  467. void CCHttpClient::dispatchResponseCallbacks(float delta)  
  468. {  
  469.     // CCLog("CCHttpClient::dispatchResponseCallbacks is running");  
  470.       
  471.     CCHttpResponse* response = NULL;  
  472.       
  473.     pthread_mutex_lock(&s_responseQueueMutex);//给response队列上锁  
  474.     if (s_responseQueue->count())  
  475.     {  
  476.         response = dynamic_cast<CCHttpResponse*>(s_responseQueue->objectAtIndex(0));//取出response  
  477.         s_responseQueue->removeObjectAtIndex(0);//将其从response队列移除  
  478.     }  
  479.     pthread_mutex_unlock(&s_responseQueueMutex);//解锁  
  480.       
  481.     if (response)  
  482.     {  
  483.         --s_asyncRequestCount;  
  484.           
  485.         CCHttpRequest *request = response->getHttpRequest();  
  486.         CCObject *pTarget = request->getTarget();//获取request回调函数的对象  
  487.         SEL_CallFuncND pSelector = request->getSelector();//获取回调函数  
  488.   
  489.         if (pTarget && pSelector)   
  490.         {  
  491.             (pTarget->*pSelector)((CCNode *)this, response);//调用回调函数,并把本单例对象和response传递给我们设置在request中的回调函数  
  492.         }  
  493.           
  494.         response->release();  
  495.     }  
  496.       
  497.     if (0 == s_asyncRequestCount) //如果没有没有请求,停止调度  
  498.     {  
  499.         CCDirector::sharedDirector()->getScheduler()->pauseTarget(this);  
  500.     }  
  501.       
  502. }  


      花了大半天时间,终于写的差不多了,其实我当初是想看看cocos2d-x是怎样封装socket这一块的,结果是这样,用的cURL库。。。
      这篇文章是我的处女做,哪里有不好的地方大家提出来共同进步,欢迎交流

      本人今年刚毕业,接触cocos2d-x也才两个月(写的不好不要扔砖就好),前一个多月一直在搞粒子系统这块,这几天把改造后的粒子系统工具开发完了,时间稍微多点,就看看其他的模块了,看完了收获也不少,由于经常逛csdn,拜读大神的文章,所以就想着咱也来发表一遍学习心得吧,这样既可以提高自己,也可以和大家交流交流心得,更重要的是我发现写博客可以提升学习的兴趣

      好了,就到这里了,以后有时间再继续


  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值