基于cocos2d-x引擎游戏网络安全处理(C++环境)
【原创博文,转载请注明出处!】
前段时间棋牌游戏在弱网环境下奔溃率比较高,综合各种测试环境和奔溃日志,比较多见的情况是:弱网下离开当前的界面后,当前界面的网络请求才回调过来刷新UI。与iOS 原生开发UIViewController不同的是,Cocos2d-x 引擎中c++环境下创建的类的回收是在析构函数执行完之后的某个合理时间才会进行。所以,离开当前的Scene或Layer等经常发起网络请求的地方,回调回来后,本质上Scene已经被销毁了,但是内存还在,因此在回调刷新UI的时候,因为内部控件被销毁造成各种野指针奔溃。
解决办法:构造了一个网络工具安全助手。
工具类的3个重量级方法:
/**
将发起请求的类名加入注册池
node : 当前调用的类对象(取对象的地址作为唯一标识符)
*/
static void registerClassName(Node *node);
/**
将发起请求的类名移出注册池
node : 当前调用的类对象(取对象的地址作为唯一标识符)
*/
static void unregisterClassName(Node *node);
/**
是否需要进行网络请求回调
node : 当前调用的类对象(取对象的地址作为唯一标识符)
*/
static bool needCallback(Node *node);
思路:
Step 1:在类的 onEnter();方法里面调用 static void registerClassName(Node *node); 注册。
Step 2:在类的 onExit();方法里面调用static void unregisterClassName(Node *node); 解注册。
Step 3:在网络请求回调处调用static bool needCallback(Node *node); 拦截,如果那个类不存在了,回调就直接pass掉,否则就放过它。
涉及到如何选择唯一标识符的问题,也就是上述方法中的node。
参考cocos2d引擎,发现官方有使用其类的内存地址作为唯一ID,觉得很棒。
string className = StringUtils::format("%p",node);
之前我们选择类名作为ID,这样在弱网下还会存在一个bug:如果用户手速快,前后两次进入同一个layer,由于标识符取了类名,所以这两次肯定相同,所以不会拦截前一次的回调,然后再刷新UI,(前一次的子控件实际上已经销毁了)然后就creash.
唯一标识符选择当前类在内存中的地址,很安全!想一下系统先创建类A,然后创建类B,再销毁类A,这个时候B和A的内存地址相同的概率有多大?
(先创建类A,然后创建类B,再销毁类A)。
(先创建类A,然后创建类B,再销毁类A)。
(先创建类A,然后创建类B,再销毁类A)。
B和A的内存地址还可以相同???
miaowu....jpg下面让我们一起来看看这个网络安全助手的实现细节吧:
//
// SafeNetworkHandler.cpp
// HelloCpp-mobile
//
// Created by RephontilZhou on 2018/3/20.
//
#include "SafeNetworkHandler.hpp"
static SafeNetworkHandler *singleInstance = nullptr;
void SafeNetworkHandler::init()
{
if (!singleInstance) {
singleInstance = new SafeNetworkHandler();
}
}
/**
将发起请求的类名加入注册池
node : 当前调用的类对象(取对象的地址作为唯一标识符)
*/
void SafeNetworkHandler::registerClassName(Node *node)
{
init();
string className = StringUtils::format("%p",node);
singleInstance->classNameList.push_back(className);
}
/**
将发起请求的类名移出注册池
node : 当前调用的类对象(取对象的地址作为唯一标识符)
*/
void SafeNetworkHandler::unregisterClassName(Node *node)
{
string className = StringUtils::format("%p",node);
if (singleInstance->classNameList.size() > 0)
{
for (vector<string>::iterator it = singleInstance->classNameList.begin(); it != singleInstance->classNameList.end();)
{
if ((*it) == className) {
it = singleInstance->classNameList.erase(it);
}
else {
it++;
}
}
}
}
/**
是否需要进行请求回调
node : 当前调用的类对象(取对象的地址作为唯一标识符)
*/
bool SafeNetworkHandler::needCallback(Node *node)
{
string className = StringUtils::format("%p",node);
bool enable = false;
if (singleInstance->classNameList.size() > 0)
{
for (vector<string>::iterator it = singleInstance->classNameList.begin(); it != singleInstance->classNameList.end(); ++it) {
if ((*it) == className) {
enable = true;
}
}
}
return enable;
}
好啦,简书上面的第一篇技术分享到此结束啦,如果此时您正好也使用cocos2d-X的引擎开发游戏,碰巧在网络请求中遇到同样棘手的问题,那么上面的解决方案肯定能让你笑得像菊花一样地灿烂。
看到这里,点个赞呗❤️.png