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

发表于2016-05-24
评论1 1.4k浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏程序行业精英群

711501594
  这段时间接触到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,拜读大神的文章,所以就想着咱也来发表一遍学习心得吧,这样既可以提高自己,也可以和大家交流交流心得,更重要的是我发现写博客可以提升学习的兴趣
  好了,就到这里了,以后有时间再继续

原文链接

著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

游戏学院公众号二维码
腾讯游戏学院
微信公众号

提供更专业的游戏知识学习平台