cocos2d-x 10.1版本 http 链接

cocos2d-x 10.1版本 http 链接

这是根据 cocos2d-x-2.0 修改的。

 

 1 /********************************************************************
 2  *  Copyright(C) 2012 Ambition( All rights reserved. )
 3  *    Created:    2012/09/18   11:21
 4  *    File base:    HSBaseHttp.h
 5  *    Author:        Ambition
 6  *    
 7  *    Purpose:    
 8 *********************************************************************/
 9 #ifndef __HSBaseHttp_H__
10 #define __HSBaseHttp_H__
11 #pragma once
12 #include "cocos2d.h"
13 #include "curl.h"
14 #include <queue>
15 #include <pthread.h>
16 #include <semaphore.h>
17 #include <errno.h>
18 #include "HSHttpRequest.h"
19 #include "HSHttpResponse.h"
20 using namespace cocos2d;
21 
22 #if WIN32
23 #pragma comment(lib,"libcurl_imp.lib")
24 #pragma comment(lib,"pthreadVCE2.lib")
25 #endif
26 
27 
28 class HSBaseHttp : public SelectorProtocol,public CCObject
29 {
30 
31 public:
32     HSBaseHttp();
33     virtual ~HSBaseHttp();
34 
35 private:
36     int iTimeoutForConnect;        //链接超时时间
37     int iTimeoutForRead;        //读取超时时间
38 public:
39     //得到一个实例
40     static HSBaseHttp* GetInstance();
41     //销毁实例
42     static void DestroyInstance();
43     //发送请求
44     void Send(HSHttpRequest* request);
45     void SetTimeoutForConnect(int value);
46     
47     int GetTimeoutForConnect();
48  
49     void SetTimeoutForRead(int value);
50 
51     int GetTimeoutForRead();
52     
53 
54 
55 private:
56     //销毁回调
57     void DispatchResponseCallbacks(float delta);
58     //线程信号初始化
59     bool LazyInitThreadSemphore();
60 };
61 
62 
63 #endif // __HSBaseHttp_H__

 

 1 #include "HSBaseHttp.h"  2  3  4 //static member  5 static HSBaseHttp* s_pBaseHttp = NULL; // pointer to singleton  6  7 //线程锁  8 static pthread_t s_networkThread;  9 static pthread_mutex_t s_requestQueueMutex;  10 static pthread_mutex_t s_responseQueueMutex;  11 static sem_t * s_pSem = NULL;  12 static unsigned long s_asyncRequestCount = 0;  13  14 static bool need_quit = false;  15 static CCArray* s_requestQueue = NULL; //请求队列  16 static CCArray* s_responseQueue = NULL; //响应队列  17  18 #if CC_TARGET_PLATFORM == CC_PLATFORM_IOS  19 #define CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 1  20 #else  21 #define CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 0  22 #endif  23  24 #if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE  25 #define CC_ASYNC_HTTPREQUEST_SEMAPHORE "ccHttpAsync"  26 #else  27 static sem_t s_sem;  28 #endif  29  30 #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)  31 typedef int int32_t;  32 #endif  33  34 static char s_errorBuffer[CURL_ERROR_SIZE];  35  36 typedef size_t (*write_callback)(void *ptr, size_t size, size_t nmemb, void *stream);  37  38 //  39 // C 函数实现  40 //  41  42 size_t writeData(void *ptr, size_t size, size_t nmemb, void *stream)  43 {  44 std::vector<char> *recvBuffer = (std::vector<char>*)stream;  45 size_t sizes = size * nmemb;  46  47 recvBuffer->clear();  48 recvBuffer->assign((char*)ptr, (char*)ptr + sizes);  49 CCLog("...%s ... ",(char*)ptr);  50  51 return sizes;  52 }  53  54 bool ConfigureCURL(CURL *handle)  55 {  56 if (!handle) {  57 return false;  58  }  59  60  int32_t code;  61 code = curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, s_errorBuffer);  62 if (code != CURLE_OK) {  63 return false;  64  }  65 //设置超时时间 防止一直等待  66 code = curl_easy_setopt(handle, CURLOPT_TIMEOUT, HSBaseHttp::GetInstance()->GetTimeoutForRead());  67 if (code != CURLE_OK) {  68 return false;  69  }  70 code = curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, HSBaseHttp::GetInstance()->GetTimeoutForConnect());  71 if (code != CURLE_OK) {  72 return false;  73  }  74  75 return true;  76 }  77  78 //Post 模式设置  79 int ProcessPostTask(HSHttpRequest *request, write_callback callback, void *stream, int32_t *responseCode)  80 {  81 CURLcode code = CURL_LAST;  82 CURL *curl = curl_easy_init();  83  84 do {  85 if (!ConfigureCURL(curl)) {  86 break;  87  }  88  89 code = curl_easy_setopt(curl, CURLOPT_URL, request->GetUrl());  90 if (code != CURLE_OK) {  91 break;  92  }  93 code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);  94 if (code != CURLE_OK) {  95 break;  96  }  97 code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, stream);  98 if (code != CURLE_OK) {  99 break; 100  } 101 code = curl_easy_setopt(curl, CURLOPT_POST, 1); 102 if (code != CURLE_OK) { 103 break; 104  } 105 code = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request->GetRequestData()); 106 if (code != CURLE_OK) { 107 break; 108  } 109 code = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, request->GetRequestDataSize()); 110 if (code != CURLE_OK) { 111 break; 112  } 113 code = curl_easy_perform(curl); 114 if (code != CURLE_OK) { 115 break; 116  } 117 118 code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, responseCode); 119 if (code != CURLE_OK || *responseCode != 200) { 120 code = CURLE_HTTP_RETURNED_ERROR; 121  } 122 } while (0); 123 if (curl) { 124  curl_easy_cleanup(curl); 125  } 126 127 return (code == CURLE_OK ? 0 : 1); 128 } 129 130 //Get模式设置 131 int ProcessGetTask(HSHttpRequest *request, write_callback callback, void *stream, int *responseCode) 132 { 133 CURLcode code = CURL_LAST; 134 CURL *curl = curl_easy_init(); 135 136 do { 137 if (!ConfigureCURL(curl)) 138  { 139 break; 140  } 141 142 code = curl_easy_setopt(curl, CURLOPT_URL, request->GetUrl()); 143 if (code != CURLE_OK) 144  { 145 break; 146  } 147 148 code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback); 149 if (code != CURLE_OK) 150  { 151 break; 152  } 153 154 code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, stream); 155 if (code != CURLE_OK) 156  { 157 break; 158  } 159 160 code = curl_easy_perform(curl); 161 if (code != CURLE_OK) 162  { 163 break; 164  } 165 166 code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, responseCode); 167 if (code != CURLE_OK || *responseCode != 200) 168  { 169 code = CURLE_HTTP_RETURNED_ERROR; 170  } 171 } while (0); 172 173 if (curl) { 174  curl_easy_cleanup(curl); 175  } 176 177 return (code == CURLE_OK ? 0 : 1); 178 } 179 180 181 static void* NetworkThread(void *data) 182 { 183 HSHttpRequest *request = NULL; 184 185 while (true) 186  { 187 // Wait for http request tasks from main thread 188 int semWaitRet = sem_wait(s_pSem); 189 if (semWaitRet < 0) { 190 CCLog("HSBaseHttp: HttpRequest async thread semaphore error: %s\n", strerror(errno)); 191 break; 192  } 193 194 if (need_quit) 195  { 196 break; 197  } 198 199 // step 1: send http request if the requestQueue isn't empty 200 request = NULL; 201 202 pthread_mutex_lock(&s_requestQueueMutex); //Get request task from queue 203 if (0 != s_requestQueue->count()) 204  { 205 request = dynamic_cast<HSHttpRequest*>(s_requestQueue->objectAtIndex(0)); 206 s_requestQueue->removeObjectAtIndex(0); 207 // request's refcount = 1 here 208  } 209 pthread_mutex_unlock(&s_requestQueueMutex); 210 211 if (NULL == request) 212  { 213 continue; 214  } 215 216 // step 2: libcurl sync access 217 218 // Create a HttpResponse object, the default setting is http access failed 219 HSHttpResponse *response = new HSHttpResponse(request); 220 221 // request's refcount = 2 here, it's retained by HttpRespose constructor 222 request->release(); 223 // ok, refcount = 1 now, only HttpResponse hold it. 224 225 int responseCode = -1; 226 int retValue = 0; 227 228 // Process the request -> get response packet 229 switch (request->GetRequestType()) 230  { 231 case HSHttpRequest::HTTP_MODE_GET: // HTTP GET 232 retValue = ProcessGetTask(request, writeData, response->GetResponseData(), &responseCode); 233 break; 234 235 case HSHttpRequest::HTTP_MODE_POST: // HTTP POST 236 retValue = ProcessPostTask(request, writeData, response->GetResponseData(), &responseCode); 237 break; 238 239 default: 240 CCAssert(true, "CCHttpClient: unkown request type, only GET and POSt are supported"); 241 break; 242  } 243 244 // write data to HttpResponse 245 response->SetResponseCode(responseCode); 246 247 if (retValue != 0) 248  { 249 response->SetSucceed(false); 250 response->SetErrorBuffer(s_errorBuffer); 251  } 252 else 253  { 254 response->SetSucceed(true); 255  } 256 257 258 // add response packet into queue 259 pthread_mutex_lock(&s_responseQueueMutex); 260 s_responseQueue->addObject(response); 261 pthread_mutex_unlock(&s_responseQueueMutex); 262 263 // resume dispatcher selector 264 CCScheduler::sharedScheduler()->resumeTarget(HSBaseHttp::GetInstance()); 265  } 266 267 // cleanup: if worker thread received quit signal, clean up un-completed request queue 268 pthread_mutex_lock(&s_requestQueueMutex); 269 s_requestQueue->removeAllObjects(); 270 pthread_mutex_unlock(&s_requestQueueMutex); 271 s_asyncRequestCount -= s_requestQueue->count(); 272 273 if (s_pSem != NULL) { 274 #if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 275  sem_unlink(CC_ASYNC_HTTPREQUEST_SEMAPHORE); 276  sem_close(s_pSem); 277 #else 278  sem_destroy(s_pSem); 279 #endif 280 281 s_pSem = NULL; 282 283 pthread_mutex_destroy(&s_requestQueueMutex); 284 pthread_mutex_destroy(&s_responseQueueMutex); 285 286 s_requestQueue->release(); 287 s_responseQueue->release(); 288  } 289 290  pthread_exit(NULL); 291 292 return 0; 293 } 294 295 HSBaseHttp::HSBaseHttp() 296 { 297 this->iTimeoutForConnect = 30; 298 this->iTimeoutForRead = 60; 299 CCScheduler::sharedScheduler()->scheduleSelector(schedule_selector(HSBaseHttp::DispatchResponseCallbacks), this, 0, false); 300 CCScheduler::sharedScheduler()->pauseTarget(this); 301 } 302 303 HSBaseHttp::~HSBaseHttp() 304 { 305 need_quit = true; 306 307 if (s_pSem != NULL) 308  { 309  sem_post(s_pSem); 310  } 311 CCScheduler::sharedScheduler()->unscheduleSelector(schedule_selector(HSBaseHttp::DispatchResponseCallbacks), this); 312 } 313 314 HSBaseHttp* HSBaseHttp::GetInstance() 315 { 316 if (s_pBaseHttp == NULL) 317  { 318 s_pBaseHttp = new HSBaseHttp(); 319  } 320 321 return s_pBaseHttp; 322 } 323 324 void HSBaseHttp::DestroyInstance() 325 { 326 CCScheduler::sharedScheduler()->unscheduleSelector(schedule_selector(HSBaseHttp::DispatchResponseCallbacks),HSBaseHttp::GetInstance()); 327  CC_SAFE_RELEASE_NULL(s_pBaseHttp); 328 } 329 330 void HSBaseHttp::DispatchResponseCallbacks(float delta) 331 { 332 HSHttpResponse* response = NULL; 333 334 pthread_mutex_lock(&s_responseQueueMutex); 335 if (s_responseQueue->count()) 336  { 337 response = dynamic_cast<HSHttpResponse*>(s_responseQueue->objectAtIndex(0)); 338 s_responseQueue->removeObjectAtIndex(0); 339  } 340 pthread_mutex_unlock(&s_responseQueueMutex); 341 342 if (response) 343  { 344 --s_asyncRequestCount; 345 346 HSHttpRequest* request = response->GetHttpRequest(); 347 SelectorProtocol* pTarget = request->GetTarget(); 348 SEL_CallFuncND pSelector = request->GetSelector(); 349 350 if (pTarget && pSelector) 351  { 352 (pTarget->*pSelector)((CCNode *)this, response); 353  } 354 355 response->release(); 356  } 357 358 if (0 == s_asyncRequestCount) 359  { 360 CCScheduler::sharedScheduler()->pauseTarget(this); 361  } 362 } 363 364 bool HSBaseHttp::LazyInitThreadSemphore() 365 { 366 if(s_pSem != NULL) 367 return false; 368 369 #if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 370 s_pSem = sem_open(CC_ASYNC_HTTPREQUEST_SEMAPHORE, O_CREAT, 0644, 0); 371 if (s_pSem == SEM_FAILED) { 372 CCLog("Open HttpRequest Semaphore failed"); 373 s_pSem = NULL; 374 return false; 375  } 376 #else 377 int semRet = sem_init(&s_sem, 0, 0); 378 if (semRet < 0) { 379 CCLog("Init HttpRequest Semaphore failed"); 380 return false; 381  } 382 383 s_pSem = &s_sem; 384 #endif 385 386 s_requestQueue = CCArray::array(); 387 s_requestQueue->init(); 388 s_requestQueue->retain(); 389 390 s_responseQueue = CCArray::array(); 391 s_responseQueue->init(); 392 s_responseQueue->retain(); 393 394 pthread_mutex_init(&s_requestQueueMutex, NULL); 395 pthread_mutex_init(&s_responseQueueMutex, NULL); 396 397 pthread_create(&s_networkThread, NULL, NetworkThread, NULL); 398  pthread_detach(s_networkThread); 399 400 need_quit = false; 401 402 return true; 403 404 } 405 406 void HSBaseHttp::Send(HSHttpRequest* request) 407 { 408 if (!LazyInitThreadSemphore() || !request) 409  { 410 return; 411  } 412 413 ++s_asyncRequestCount; 414 415 request->retain(); 416 417 pthread_mutex_lock(&s_requestQueueMutex); 418 s_requestQueue->addObject(request); 419 pthread_mutex_unlock(&s_requestQueueMutex); 420 421 // Notify thread start to work 422  sem_post(s_pSem); 423 } 424 425 void HSBaseHttp::SetTimeoutForConnect( int value ) 426 { 427 this->iTimeoutForConnect = value; 428 } 429 430 int HSBaseHttp::GetTimeoutForConnect() 431 { 432 return this->iTimeoutForConnect; 433 } 434 435 void HSBaseHttp::SetTimeoutForRead( int value ) 436 { 437 this->iTimeoutForRead = value; 438 } 439 440 int HSBaseHttp::GetTimeoutForRead() 441 { 442 return this->iTimeoutForRead; 443 }

 

 

 1 /********************************************************************  2  * Copyright(C) 2012 Ambition( All rights reserved. )  3  * Created: 2012/09/20 9:41  4  * File base: HSHttpRequest.h  5  * Author: Ambition  6  *  7  * Purpose: 请求消息发送和设置(不是发送的具体数据)  8 *********************************************************************/  9 #ifndef __HSHttpRequest_H__ 10 #define __HSHttpRequest_H__ 11 #pragma once 12 #include "cocos2d.h" 13 using namespace cocos2d; 14 15 class HSHttpRequest : public CCObject 16 { 17 public: 18 HSHttpRequest(void); 19 ~HSHttpRequest(void); 20 21 public: 22 typedef enum 23  { 24  HTTP_MODE_GET, 25  HTTP_MODE_POST, 26  HTTP_MODE_UNKOWN, 27  }HttpRequestType; 28 29 protected: 30 SelectorProtocol* pTarget; 31 SEL_CallFuncND pSelector; //回调选择器 32 HttpRequestType requestType; //请求类型 33 string strUrl; 34 vector<char> vRequestData; 35 string tag; 36 void* pUserData; //用语在请求前添加的标志 例(Ambition:xxxxxx) 37 38 public: 39 SelectorProtocol* GetTarget(); 40  SEL_CallFuncND GetSelector(); 41 void SetRequestType(HttpRequestType type); 42  HttpRequestType GetRequestType(); 43 44 void SetUrl(const char* url); 45 const char* GetUrl(); 46 47 void SetRequestData(const char* buffer, unsigned int len); 48 char* GetRequestData(); 49 50 int GetRequestDataSize(); 51 52 void SetResponseCallback(SelectorProtocol* pTarget, cocos2d::SEL_CallFuncND pSelector); 53 54 void SetTag(const char* tag); 55 const char* GetTag(); 56 57 void SetUserData(void* pUserData); 58 void* GetUserData(); 59 }; 60 61 62 #endif // __HSHttpRequest_H__

 

 

 1 #include "HSHttpRequest.h"  2  3 HSHttpRequest::HSHttpRequest(void)  4 {  5 this->requestType = HTTP_MODE_UNKOWN;  6 this->vRequestData.clear();  7 this->tag.clear();  8 this->pTarget = NULL;  9 this->pSelector = NULL; 10 this->pUserData = NULL; 11 } 12 13 HSHttpRequest::~HSHttpRequest(void) 14 { 15 } 16 17 SelectorProtocol* HSHttpRequest::GetTarget() 18 { 19 return this->pTarget; 20 } 21 22 cocos2d::SEL_CallFuncND HSHttpRequest::GetSelector() 23 { 24 return pSelector; 25 } 26 27 HSHttpRequest::HttpRequestType HSHttpRequest::GetRequestType() 28 { 29 return this->requestType; 30 } 31 32 void HSHttpRequest::SetUrl( const char* url ) 33 { 34 strUrl = url; 35 } 36 37 const char* HSHttpRequest::GetUrl() 38 { 39 return strUrl.c_str(); 40 } 41 42 void HSHttpRequest::SetRequestData( const char* buffer, unsigned int len ) 43 { 44 vRequestData.assign(buffer, buffer + len); 45 } 46 47 char* HSHttpRequest::GetRequestData() 48 { 49 return &(vRequestData.front()); 50 } 51 52 int HSHttpRequest::GetRequestDataSize() 53 { 54 return vRequestData.size(); 55 } 56 57 void HSHttpRequest::SetRequestType( HttpRequestType type ) 58 { 59 requestType = type; 60 } 61 62 void HSHttpRequest::SetResponseCallback( SelectorProtocol* pTarget, cocos2d::SEL_CallFuncND pSelector ) 63 { 64 this->pTarget = pTarget; 65 this->pSelector = pSelector; 66 67 if (this->pTarget) 68  { 69 //this->pTarget->retain(); 70 //如果有问题以后修改 71  } 72 } 73 74 void HSHttpRequest::SetTag( const char* tag ) 75 { 76 this->tag = tag; 77 } 78 79 const char* HSHttpRequest::GetTag() 80 { 81 return this->tag.c_str(); 82 } 83 84 void HSHttpRequest::SetUserData( void* pUserData ) 85 { 86 this->pUserData = pUserData; 87 } 88 89 void* HSHttpRequest::GetUserData() 90 { 91 return this->pUserData; 92 }

 

 1 /********************************************************************  2  * Copyright(C) 2012 Ambition( All rights reserved. )  3  * Created: 2012/09/20 9:42  4  * File base: HSHttpResponse.h  5  * Author: Ambition  6  *  7  * Purpose: 响应包含返回的具体数据,但是没有解析  8 *********************************************************************/  9 #ifndef __HSHttpResponse_H__ 10 #define __HSHttpResponse_H__ 11 #pragma once 12 #include "HSHttpRequest.h" 13 14 15 class HSHttpResponse : public CCObject 16 { 17 public: 18 HSHttpResponse(HSHttpRequest* request); 19 virtual ~HSHttpResponse(void); 20 21 protected: 22 HSHttpRequest* pHttpRequest; //对应的请求指针 23 bool isSucceed; //是否成功 24 vector<char> vResponseData; 25 int iResponseCode; //响应代码 26 string strErrorBuffer; //错误信息缓存 27 28 public: 29 HSHttpRequest* GetHttpRequest(); 30 31 void SetResponseData(std::vector<char>* data); 32 vector<char>* GetResponseData(); 33 34 void SetResponseCode(int value); 35 int GetResponseCode(); 36 37 void SetSucceed(bool value); 38 bool GetSucceed(); 39 40 void SetErrorBuffer(const char* value); 41 const char* GetErrorBuffer(); 42 43 }; 44 45 #endif // __HSHttpResponse_H__

 

 1 #include "HSHttpResponse.h"  2  3 HSHttpResponse::HSHttpResponse(HSHttpRequest* request)  4 {  5 this->pHttpRequest = request;  6 if (this->pHttpRequest)  7  {  8 this->pHttpRequest->retain();  9  } 10 this->isSucceed = false; 11 this->vResponseData.clear(); 12 this->strErrorBuffer.clear(); 13 } 14 15 HSHttpResponse::~HSHttpResponse(void) 16 { 17 if (this->pHttpRequest) 18  { 19 this->pHttpRequest->release(); 20  } 21 } 22 23 HSHttpRequest* HSHttpResponse::GetHttpRequest() 24 { 25 return this->pHttpRequest; 26 } 27 28 void HSHttpResponse::SetResponseData( std::vector<char>* data ) 29 { 30 vResponseData = *data; 31 } 32 33 vector<char>* HSHttpResponse::GetResponseData() 34 { 35 return &vResponseData; 36 } 37 38 void HSHttpResponse::SetResponseCode( int value ) 39 { 40 iResponseCode = value; 41 } 42 43 int HSHttpResponse::GetResponseCode() 44 { 45 return this->iResponseCode; 46 } 47 48 void HSHttpResponse::SetSucceed( bool value ) 49 { 50 isSucceed = value; 51 } 52 53 bool HSHttpResponse::GetSucceed() 54 { 55 return this->isSucceed; 56 } 57 58 void HSHttpResponse::SetErrorBuffer( const char* value ) 59 { 60  strErrorBuffer.clear(); 61  strErrorBuffer.assign(value); 62 } 63 64 const char* HSHttpResponse::GetErrorBuffer() 65 { 66 return strErrorBuffer.c_str(); 67 }

 

测试代码:

 

 

 1 HSHttpRequest* request = new HSHttpRequest();  2 request->SetUrl("http://220.194.62.22/wapgame/a.jsp");  3 request->SetRequestType(HSHttpRequest::HTTP_MODE_POST);  4 request->SetResponseCallback(this, callfuncND_selector(HelloWorld::onHttpRequestCompleted));  5  6 // write the post data  7 const char* postData = "visitor=cocos2d&TestSuite=Extensions Test/NetowrkTest";  8 request->SetRequestData(postData, strlen(postData));  9 request->SetTag("POST test"); 10 HSBaseHttp::GetInstance()->Send(request); 11 request->release();

 

转载于:https://www.cnblogs.com/GameDeveloper/articles/2706562.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/110313.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号