The encapsulation of an easy-to-use network request and callback mechanism by cocos2dx is fundamental and necessary. The usage example I have encapsulated is as follows
<pre class="prism-highlight prism-language-cpp">// 请求用户登录
net::request(c2s::USER, c2s::UserAction::LOGIN, data, [=](Response response) {
auto sceneParent = dynamic_cast<SceneParent*>(getParent());
if (response.result == false)
{
sceneParent->alert(response.msg);
}
else
{
refreshUserInfo();
CONFIG->setLoginAccount(username,password);
}
});
Directly referencing header files
net.hpp
<pre class="prism-highlight prism-language-cpp">#pragma once
#include "network.h"
#include <functional>
#include "yutil/json.h"
namespace net
{
// 请求
void request(c2s::Type type, int action, const ylib::json& data = ylib::json(), std::function<void(Response)> callback = nullptr){
ylib::env->to<TcpClient>("tcp_client")->request(type, action, data, callback);
}
}
The TCP client code is as follows
tcpclient.h
<pre class="prism-highlight prism-language-cpp">#pragma once
#include <functional>
#include "network.h"
#include "ynet/tcp_client.h"
#include "yutil/json.h"
#include "yutil/cache.h"
#include "yutil/counter.hpp"
#include "yutil/thread.h"
#include "yutil/queue.hpp"
class TcpClient:private ylib::ithread {
private:
struct ResponseTimeoutAttr {
// 创建时间
uint32 create_sec = 0;
// 附加数据
ylib::json extra;
// 请求索引
uint32 index = 0;
// 响应数据
ylib::json response;
bool responsed = false;
// 回调函数
std::function<void(Response)> callback;
};
public:
TcpClient();
~TcpClient();
// 连接
bool connect();
// 请求数据
bool request(c2s::Type type, int action,const ylib::json& data, std::function<void(Response)> callback);
// 接收回调-服务器主动
void activeResponse(std::function<void(ActiveResponse response)> callback) { m_active_response_callback = callback; }
void handleQueue();
private:
void initEvent();
void handleRecvData();
private:
// TCP客户端
network::tcp::client *m_client = nullptr;
// 请求计数器
std::shared_ptr<ylib::counter<uint32>> m_counter;
// 请求缓存包
std::map<uint32/*index*/, ResponseTimeoutAttr> m_response_timeout_attr;
std::mutex m_mutex;
std::function<void(ActiveResponse response)> m_active_response_callback;
// 接收缓存池
ylib::buffer m_receive_cache;
// 响应队列
ylib::queue<Response> m_reponse_queue;
// 通过 ithread 继承
bool run() override;
};
tcpclient.cpp
<pre class="prism-highlight prism-language-cpp">#include "tcpclient.h"
#include "define.h"
#include "yutil/codec.h"
#include "ybase/conversion.h"
#include "yutil/time.h"
#include "yutil/system.h"
#include "cocos2d.h"
#include "HPSocket/HPSocket.h"
#include "yutil/print.h"
#ifdef _WIN32
#include <Windows.h>
#endif
bool TcpClient::connect()
{
return m_client->connect({ REMOTE_SERVER_ADDRESS ,REMOTE_SERVER_PORT});
}
bool TcpClient::request(c2s::Type type, int action, const ylib::json& data, std::function<void(Response)> callback)
{
ylib::json req_json;
uint32 index = m_counter->make();
req_json["type"] = type;
req_json["action"] = action;
req_json["index"] = index;
req_json["data"] = data;
ResponseTimeoutAttr rta;
rta.callback = callback;
rta.index = index;
rta.create_sec = time::now_sec();
m_response_timeout_attr.emplace(index,rta);
std::string req_data = req_json.to_string();
std::cout << "[send]: \t" << req_data << std::endl;
m_client->send(req_data.c_str(), req_data.length());
return true;
}
void TcpClient::handleQueue()
{
Response response;
while (m_reponse_queue.pop(response))
response.____callback(response);
}
void TcpClient::initEvent()
{
m_client->on_recv([&](network::tcp::client*,const char* data,uint32 length) {
std::string str(data, length);
ylib::println("[recv]:\t" + codec::to_gbk(str), (ylib::ConsoleTextColor)3);
ylib::json json_data = ylib::json::from(str);
if (json_data["server"].to<bool>() == false) {
// 被动响应
m_mutex.lock();
auto iter = m_response_timeout_attr.find(json_data["index"].to<uint32>());
if (iter != m_response_timeout_attr.end()) {
iter->second.responsed = true;
iter->second.response = json_data;
}
m_mutex.unlock();
}
else
{
// 主动请求
ActiveResponse ar;
ar.type = (s2c::Type)json_data["type"].to<int>();
ar.action = json_data["action"].to<int>();
ar.data = json_data["data"];
if (m_active_response_callback != nullptr) {
m_active_response_callback(ar);
}
}
});
}
bool TcpClient::run()
{
m_mutex.lock();
uint32 now_sec = time::now_sec();
for (auto iter = m_response_timeout_attr.begin(); iter != m_response_timeout_attr.end();)
{
if (iter->second.responsed)
{
// 已响应
if (iter->second.callback) {
Response response;
response.result = iter->second.response["result"].to<bool>();
response.msg = iter->second.response["msg"].to<std::string>();
response.data = iter->second.response["data"];
response.____callback = iter->second.callback;
m_reponse_queue.push(response);
c2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([&]() {
this->handleQueue();
});
}
iter = m_response_timeout_attr.erase(iter);
continue;
}
if (now_sec > iter->second.create_sec + RESPONSE_TIMEOUT_SEC)
{
// 超时
if (iter->second.callback) {
Response response;
response.result = false;
response.msg = "请求超时,请稍后重新访问";
response.____callback = iter->second.callback;
m_reponse_queue.push(response);
c2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([&]() {
this->handleQueue();
});
}
iter = m_response_timeout_attr.erase(iter);
continue;
}
iter++;
}
m_mutex.unlock();
system::sleep_msec(100);
return true;
}
TcpClient::TcpClient()
{
m_counter.reset(new ylib::counter<uint32>(1));
// tcp client
m_client = new network::tcp::client(ylib::receive_model::PACK);
initEvent();
if (m_client->start() == false) {
#ifdef _WIN32
MessageBoxA(NULL,m_client->last_error().c_str(),"error",0);
abort();
#endif
}
m_receive_cache.resize(1024 * 1024 * 5);
// thread
::ithread::start();
}
TcpClient::~TcpClient()
{
m_client->close();
delete m_client;
::ithread::stop();
::ithread::wait();
}
main.cpp: init
<pre class="prism-highlight prism-language-cpp">#include "main.h"
#include "config.h"
#include "AppDelegate.h"
#include "cocos2d.h"
#include "utils/ImageUtils.h"
#include "network/tcpclient.h"
#include "ybase/environment.h"
USING_NS_CC;
void initNetwork()
{
ylib::env->set<TcpClient>("tcp_client", new TcpClient());
ylib::env->set<Config>("config", new Config());
}
#if _DEBUG
int main()
{
#else
int WINAPI _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
#endif
initNetwork();
//test();
// create the application instance
AppDelegate app;
return Application::getInstance()->run();
}