这段时间接触到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请求
- voidHttpClientTest::onMenuGetTestClicked(cocos2d::CCObject*sender)
- {
- //test1
- CCHttpRequest*request=newCCHttpRequest();//创建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("GETtest1");//设置用户标识,可以通过response获取
- CCHttpClient::getInstance()->send(request);//使用CCHttpClient共享实例来发送request
- request->release();//调用release()
- }
- //waiting
- m_labelStatusCode->setString("waiting...");
- }
- //这里就是我们要处理接收到数据的回调函数了,sender为CCHttpClient实例指针,data为接收到的response指针
- voidHttpClientTest::onHttpRequestCompleted(cocos2d::CCNode*sender,void*data)
- CCHttpResponse*response=(CCHttpResponse*)data;
- if(!response)
- return;
- //获取对应request的字符串标识
- if(0!=strlen(response->getHttpRequest()->getTag()))
- CCLog("%scompleted",response->getHttpRequest()->getTag());
- //获取返回代码,比如200、404等
- intstatusCode=response->getResponseCode();
- charstatusString[64]={};
- sprintf(statusString,"HTTPStatusCode:%d,tag=%s",statusCode,response->getHttpRequest()->getTag());
- m_labelStatusCode->setString(statusString);
- CCLog("responsecode:%d",statusCode);
- if(!response->isSucceed())
- {
- CCLog("responsefailed");
- CCLog("errorbuffer:%s",response->getErrorBuffer());//可以调用getErrorBuffer()来获取错误原因
- return;
- //dumpdata
- std::vector<char>*buffer=response->getResponseData();//用来获取接收到的数据
- printf("HttpTest,dumpdata:");
- for(unsignedinti=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,其实这个文件没什么好说的,都有注释
- classCCHttpRequest:publicCCObject
- public:
- /**请求类型枚举,可以通过setReqeustType(param)设置*/
- typedefenum
- kHttpGet,
- kHttpPost,
- kHttpUnkown,248)"> }HttpRequestType;
- /**Constructor
- BecauseHttpRequestobjectwillbeusedbetweenUItheadandnetworkthread,
- requestObj->autorelease()isforbiddentoavoidcrashesinCCAutoreleasePool
- new/retain/releasestillworks,whichmeansyouneedtoreleaseitmanually
- PleaserefertoHttpRequestTest.cpptofinditsusage
- 这里是有注释的,因为要跨线程,所以就不能用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,"HttpResponseisusedbetweennetworkthreadanduithread\
- therefore,autoreleaseisforbiddenhere");
- returnNULL;
- //setter/gettersforproperties
- /**设置请求类型
- 目前支持kHttpGet和kHttpPost
- inlinevoidsetRequestType(HttpRequestTypetype)
- _requestType=type;
- /**返回请求类型*/
- inlineHttpRequestTypegetRequestType()
- return_requestType;
- };
- /**设置请求url
- voidsetUrl(constchar*url)
- _url=url;
- /**获取请求url*/
- char*getUrl()
- return_url.c_str();
- /**这个设置用于post方式的data数据
- voidsetRequestData(char*buffer,unsignedintlen)
- _requestData.assign(buffer,buffer+len);
- /**Gettherequestdatapointerback*/
- inlinechar*getRequestData()
- return&(_requestData.front());
- /**Getthesizeofrequestdataback*/
- intgetRequestDataSize()
- return_requestData.size();
- /**为每个请求设置一个字符串标示,可以通过HttpResponse->getHttpRequest->getTag()获取,因为HttpResponse会将对应的HttpRequest封装在里面
- */
- voidsetTag(char*tag)
- _tag=tag;
- /**Getthestringtagbacktoidentifytherequest.
- ThebestpracticeistouseitinyourMyClass::onMyHttpRequestCompleted(sender,HttpResponse*)callback
- char*getTag()
- return_tag.c_str();
- /**Optionfield.Youcanattachacustomeddataineachrequest,andgetitbackinresponsecallback.
- Butyouneedtonew/deletethedatapointermanully
- voidsetUserData(void*pUserData)
- _pUserData=pUserData;
- /**Getthepre-settedcustomdatapointerback.
- Don'tforgettodeleteit.HttpClient/HttpResponse/HttpRequestwilldonothingwiththispointer
- void*getUserData()
- return_pUserData;
- /**通过这个函数设置我们的数据处理回调函数
- voidsetResponseCallback(CCObject*pTarget,SEL_CallFuncNDpSelector)
- _pTarget=pTarget;
- _pSelector=pSelector;
- _pTarget->retain();
- /**Getthetargetofcallbackselectorfuntion,mainlyusedbyCCHttpClient*/
- inlineCCObject*getTarget()
- return_pTarget;
- /**Gettheselectorfunctionpointer,mainlyusedbyCCHttpClient*/
- inlineSEL_CallFuncNDgetSelector()
- return_pSelector;
- /**Setanycustomheaders**/
- voidsetHeaders(std::vector<std::string>pHeaders)
- _headers=pHeaders;
- /**Getcustomheaders**/
- inlinestd::vector<std::string>getHeaders()
- return_headers;
- protected:
- //properties
- HttpRequestType_requestType;///请求方式
- std::string_url;///请求url
- 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;///customhttpheaders
- };