Design of Asynchronous Callback for Cocos2dx Network Requests

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();
}

Title of this article:<Design of Asynchronous Callback for Cocos2dx Network Requests>Author:minimini
Original link:https://www.xxmjw.com/post/20.html
Unless otherwise specified, all content is original. Please indicate when reprinting.

Related

minimini

minimini