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:
- //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"
- #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: %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 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<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庫。。。
相關文章
- 前端面試之路五(網路HTTP篇)前端面試HTTP
- HTTP協議學習---(三)進階篇HTTP協議
- Java學習之反射篇Java反射
- 網路篇:協天子令諸侯[-Http-]HTTP
- 計算機網路資料篇(一)——HTTP計算機網路HTTP
- Android筆記-網路篇:HTTP & HTTPSAndroid筆記HTTP
- iOS開發之網路篇iOS
- HTTP Client 學習筆記 (一) 初遇篇HTTPclient筆記
- 快速Android開發系列網路篇之Android-Async-HttpAndroidHTTP
- 【深度學習篇】--神經網路中的卷積神經網路深度學習神經網路卷積
- 網路基礎之網路協議篇協議
- 測試開發之網路篇-網路路由路由
- 網路篇 - http協議從入門到精通HTTP協議
- http協議學習系列(協議詳解篇)HTTP協議
- Linux效能分析之網路篇Linux
- 網路管理之ICMP協議篇協議
- swoft 學習筆記之 response 篇筆記
- Flutter Canvas學習之繪圖篇FlutterCanvas繪圖
- less學習之Bootstrap(按鈕篇)boot
- JAVA基礎學習篇之反射Java反射
- Kotlin學習之起點篇Kotlin
- MySQL學習基礎之起航篇MySql
- 《計算機網路學習指南》——學習網路,這一篇足以讓你心滿意足計算機網路
- 一篇學習HTTP狀態碼的神文HTTP
- 網路安全之iptables實驗篇一
- 網路、HTTP相關學習總結HTTP
- vc 學習點滴之終結篇
- MySQL 學習之索引篇和查詢MySql索引
- sass學習篇
- Avazu:全球網際網路產業研究之巴西篇產業
- 全球網際網路產業研究之巴西篇(二)產業
- 【備忘】Java菜鳥到大牛學習路線之實戰篇Java
- 測試開發之網路篇-IP地址
- Linux之《荒島餘生》(五)網路篇Linux
- 私有云落地解決方案之網路篇-網路架構架構
- Flutter學習篇(五)——路由剖析終篇Flutter路由
- 畫江湖之 Go 學習篇 【訊號量】Go
- 畫江湖之 Go 學習篇 [訊號量]Go