cocos2d-x学习篇之网络(http)篇
发表于2016-05-24
这段时间接触到cocos2d-x,拜读了csdn上很多大大的文章,尤其是小满的专栏,感觉获益不少,觉得像他们那样,边学习,边总结经验,并写出来学习过程与大家分享,我觉得是一件很值得学习的事,所以也打算把自己学习的东西和经验与大家分享,有不足之处或者错误的,还希望请大家能海涵并提出来,共同讨论,共同进步。好了,废话到此。
Cocos2dx 为我们封装了在cocos2dx中http的网络框架,其文件在cocos2dx引擎包的cocos2d-2.1rc0-x-2.1.2extensionsnetwork文件下的 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | //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" ); } char > |
基本上一个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,其实这个文件没什么好说的,都有注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | 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< char > _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 }; char > |
HttpResponse.h,这个文件和HttpRequest差不多,没什么好说的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | 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 }; char > char > char > |
说白了,CCHttpRequest和CCHttpResponse只不过是发送队列中的数据类型,和接收队列中的数据类型,是线程之间传递的参数,下面来说说CCHttpClient
HttpClient.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | //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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | 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< 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; } // 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: %sn" , 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 ); } } char > char > |
花了大半天时间,终于写的差不多了,其实我当初是想看看cocos2d-x是怎样封装socket这一块的,结果是这样,用的cURL库。。。
这篇文章是我的处女做,哪里有不好的地方大家提出来共同进步,欢迎交流
本人今年刚毕业,接触cocos2d-x也才两个月(写的不好不要扔砖就好),前一个多月一直在搞粒子系统这块,这几天把改造后的粒子系统工具开发完了,时间稍微多点,就看看其他的模块了,看完了收获也不少,由于经常逛csdn,拜读大神的文章,所以就想着咱也来发表一遍学习心得吧,这样既可以提高自己,也可以和大家交流交流心得,更重要的是我发现写博客可以提升学习的兴趣
好了,就到这里了,以后有时间再继续