设计上:

(1)存档方式

定时器在一定范围的分钟内随机存档一次,为了避免集中式存档

第一次需要全部存档一次,因为需要存档版本字段

分标签实时存档设置了标签的数据,减少每次存档数据

(2)压缩处理

档案消息中的角色二进制数据是经过zlib算法压缩处理的,减少每次存档数据

被压缩数据也以压缩形式的二进制存入数据库mysql,在读档案到场景服务器中才会解档

(3)角色属性数据

关键性角色属性数据没有进行压缩处理,以明文方式写入数据库,可直接查询

1、存档方式

在场景服务器:

定时器在一定范围的分钟内随机存档一次。

第一次需要全部存档一次,因为需要存档版本字段。

分标签实时存档设置了标签的数据。

场景服务器循环
if (_write_record_timer(main_logic_thread::currentTime))
{
//随机时间存档
save(MSG::DB::WRITEBACK_TIMETICK);
first_save = true;
}
else
{
if(!first_save)
{
save(MSG::DB::FIRST_SAVE);//第一次全部内容存档
first_save = true;
}
else
{
operation_save(); //实时标签存档
}
}

2、场景服务器档案处理

(1)存档

序列化一个用户的所有存档数据

struct SerializeRoleData
{SerializeRoleData(){size = 0;}RoleData roledata;//角色属性uint32 size;char data[0];//其他数据
};

全部内容存档

bool scene_player::save(MSG::DB::WriteBack_Type type)
{update_roledata(type);BUFFER_CMD(MSG::DB::stWriteplayerRoleDataRecordCmd,send,UNZIP_BINARY_SIZE);send->writeback_type = type;send->charid = id;SerializeRoleData *sall = (SerializeRoleData*)(&send->data);constructInPlace(sall);sall->size = 0;5 种类型的存档bcopy(&roledata, &sall->roledata, sizeof(sall->roledata));other_serialize((char*)(&(send->data.data[0])), send->data.size);money_serialize((char*)(&(send->data.data[0])), send->data.size);task_serialize((char*)(&(send->data.data[0])), send->data.size);item_serialize((char*)(&(send->data.data[0])), send->data.size);dbClient->sendmsg(send, sizeof(MSG::DB::stWriteplayerRoleDataRecordCmd) + send->data.size);///清除存档标签clear_save_tag();//g_log->debug("角色二进制存档数据:%u", send->data.size);return true;
}

标签内容存档

void scene_player::operation_save()
{if(0 != this->save_tag ){if((this->save_tag & (1<<SAVE_ROLE_DATA)))  ///有roledata更新的情况{update_roledata(MSG::DB::WRITEBACK_OPERATION);BUFFER_CMD(MSG::DB::stWriteplayerRoleDataRecordCmd,send,UNZIP_BINARY_SIZE);send->writeback_type = MSG::DB::WRITEBACK_OPERATION;send->charid = id;send->save_tag = this->save_tag;SerializeRoleData *sall = (SerializeRoleData*)(&send->data);constructInPlace(sall);sall->size = 0;bcopy(&roledata, &sall->roledata, sizeof(sall->roledata));//角色属性存档if(this->save_tag & (1<<SAVE_ITEM))//道具存档{item_serialize((char*)&sall->data, sall->size);}if(this->save_tag & (1<<SAVE_TASK))//任务存档{task_serialize((char*)&sall->data, sall->size);}if(this->save_tag & (1<<SAVE_MONEY))//金钱存档{money_serialize((char*)&sall->data, sall->size);}if(this->save_tag & (1<<SAVE_OTHER))//其他存档{other_serialize((char*)&sall->data, sall->size);}dbClient->sendmsg(send, sizeof(MSG::DB::stWriteplayerRoleDataRecordCmd) + send->data.size);}else //没有roledata{BUFFER_CMD(MSG::DB::stNewSaveBinaryCmd,send,UNZIP_BINARY_SIZE);send->charid = id;send->save_tag = this->save_tag;if(this->save_tag & (1<<SAVE_ITEM)) 二进制压缩道具数据{item_serialize((char*)&send->data, send->size);}if(this->save_tag & (1<<SAVE_TASK)) ///二进制压缩任务数据{task_serialize((char*)&send->data, send->size);}if(this->save_tag & (1<<SAVE_MONEY)) 金钱(不进行zlib压缩)存档{money_serialize((char*)&send->data, send->size);}if(this->save_tag & (1<<SAVE_OTHER)) 其它二进制存档{other_serialize((char*)&send->data, send->size);}dbClient->sendmsg(send, sizeof(MSG::DB::stNewSaveBinaryCmd) + send->size);}  clear_save_tag();///清除存档标签}
}

道具存档

bool scene_player::item_serialize(char *pdata, uint32 & cursor)
{char* data = pdata;char temp[UNZIP_BINARY_SIZE];//存档缓冲区bzero(temp,sizeof(temp));uint32 itemsize = packs.uim.serialize(temp);if(0 != itemsize){*(uint32*)(&data[cursor]) = BinaryType_Item;//道具标签cursor += sizeof(uint32);char zipData[ZIP_BINARY_SIZE];//压缩缓冲区bzero(zipData,sizeof(zipData));uint32 zipLen = compressBinary_common(zipData, ZIP_BINARY_SIZE, temp, itemsize);//zlib压缩if(zipLen == (uint32)-1){g_log->error("压缩失败道具");assert(0);}else{bcopy((const void *)(&zipLen), (void*)(&data[cursor]), sizeof(zipLen));//压缩后长度cursor += sizeof(zipLen);bcopy((const void *)zipData, (void*)(&data[cursor]), zipLen);//压缩后数据cursor += zipLen;}}return true;
}

(2)解档

场景服务器解档

bool scene_player::unSerialize(const char *in)
{
SerializeRoleData *sall = (SerializeRoleData *)in;
bcopy(&sall->roledata, &roledata,sizeof(sall->roledata));
if(isNewRole())
{return true;
}if(this->roledata.lastLoginTime > this->roledata.lastOfflineTime){g_log->error("[%u,%s]lastLoginTime%u,lastLoginTime%u",this->id,this->name,this->roledata.lastLoginTime,this->roledata.lastOfflineTime);this->roledata.lastOfflineTime = this->roledata.lastLoginTime + 1;}//g_log->debug("角色[%s]二进制大小:%u",this->name, sall->size);if(sall->size == 0)
{
return true;
}uint32 dataCursor = 0;while(dataCursor != sall->size){switch(*(uint32*)(&(sall->data[dataCursor]))){case BinaryType_Other://其他解档{dataCursor += sizeof(uint32);//标签uint32 otherDataLen = *(uint32*)&sall->data[dataCursor];dataCursor += sizeof(uint32);//数据长度字段other_unserialize(&sall->data[dataCursor], otherDataLen);dataCursor += otherDataLen;//数据长度}break;case BinaryType_Item://道具解档{dataCursor += sizeof(uint32);uint32 itemDataLen = *(uint32*)&sall->data[dataCursor];dataCursor += sizeof(uint32);item_unserialize(&sall->data[dataCursor], itemDataLen);dataCursor += itemDataLen;}break;case BinaryType_Money://金钱解档{dataCursor += sizeof(uint32);uint32 moneyDataLen = *(uint32*)&sall->data[dataCursor];dataCursor += sizeof(uint32);money_unserialize(&sall->data[dataCursor], moneyDataLen);dataCursor += moneyDataLen;}break;case BinaryType_Task://任务解档{dataCursor += sizeof(uint32);uint32 taskDataLen = *(uint32*)&sall->data[dataCursor];dataCursor += sizeof(uint32);task_unserialize(&sall->data[dataCursor], taskDataLen);dataCursor += taskDataLen;}break;default:{g_log->error("%u,%s 解档错误:%u,%u",this->id, this->name,dataCursor, sall->size);assert(0);return false;}break;}}g_log->debug("角色【%u,%s】解档成功。准备进入场景", this->id, this->name);
this->packs.stallpack.setVipAddtion();//摆摊背包VIP扩展
return true;
}

道具解档

bool scene_player::item_unserialize(const char* itemData, const uint32 itemDataLen, const uint32 version)
{unsigned char temp[UNZIP_BINARY_SIZE];SerializeBinaryMember*next= uncompressBinary_common(itemData, itemDataLen, temp, sizeof(temp));//解压道具档案数据packs.uim.unSerialize(next,binary_version,this);return true;
}

3、db服务器档案处理

(1)存档

接收场景服务器发来的存档消息

bool db_player::update_all(MSG::DB::stWriteplayerRoleDataRecordCmd *recv)
{if(recv == NULL) return false;update_roledata((const void*)&recv->data.roledata);//写角色属性档案if(recv->data.size){update_binary_buffer(&(recv->data.data[0]), recv->data.size);}//g_log->debug("%s,%u",__PRETTY_FUNCTION__, recv->data.size);return true;
}

写角色二进制档案

bool db_player::update_binary_buffer(const char* pdata, const uint32 dataLen)
{const char* data = pdata;uint32 dataSize = 0;while (dataSize != dataLen) {switch (*(uint32*)&data[dataSize]){case BinaryType_Item:{dataSize += sizeof(uint32);//标签uint32 itemSize = *(uint32*)(&data[dataSize]);if(itemSize != 0){bcopy((const void*)&data[dataSize], (void*)item_buffer, itemSize + sizeof(uint32));dataSize += sizeof(uint32); //压缩后长度字段dataSize += itemSize;//压缩后数据长度save_tag |= (1<<SAVE_ITEM);//添加物品保存标签}else{dataSize += sizeof(uint32); }//g_log->info("BinaryType_Item size:%u",itemSize);}break;case BinaryType_Task:{dataSize += sizeof(uint32);uint32 taskSize = *(uint32*)(&data[dataSize]);if(taskSize != 0){bcopy((const void*)&data[dataSize], (void*)task_buffer, taskSize + sizeof(uint32));dataSize += sizeof(uint32); dataSize += taskSize;save_tag |= (1<<SAVE_TASK);//添加任务标签}else{dataSize += sizeof(uint32); }//g_log->info("BinaryType_Task size:%u",taskSize);}break;case BinaryType_Money:{dataSize += sizeof(uint32);uint32 moneySize = *(uint32*)(&data[dataSize]);if(moneySize != 0){bcopy((const void*)&data[dataSize], (void*)money_buffer, moneySize + sizeof(uint32));dataSize += sizeof(uint32); dataSize += moneySize;save_tag |= (1<<SAVE_MONEY);//添加金钱标签}else{dataSize += sizeof(uint32); }//g_log->info("BinaryType_Money size:%u",moneySize);}break;case BinaryType_Other:{dataSize += sizeof(uint32);uint32 otherSize = *(uint32*)(&data[dataSize]);if(otherSize != 0){bcopy((const void*)&data[dataSize], (void*)other_buffer, otherSize + sizeof(uint32));dataSize += sizeof(uint32); dataSize += otherSize;save_tag |= (1<<SAVE_OTHER);//添加其他标签}else{dataSize += sizeof(uint32); }//g_log->info("BinaryType_Other size:%u",otherSize);}break;default:{g_log->error("角色[%u]二进制数据长度有误 %u,%u", this->id, dataLen, dataSize);assert(0);}break;}}return true;
}

遍历所有的db角色循环

struct EveryplayerLoopExec : public callback<db_player>
{
bool invoke(db_player *entry)
{
entry->loop();//db角色循环
return true;
}
}user_loop_exec;

db服务器循环处理消息派送和检查db角色写档案缓存

void main_logic_thread::run()
{
while(!isFinal())
{
setRunning();
main_logic_thread::currentTime.now();
g_server.handle_msg();//中心服务器消息
g_db_session_manager.handle_msg();//检查其他连接来的服务器的消息
g_player_mgr.traverse_every_player(user_loop_exec);//遍历所有db角色
......
}
}

db角色循环检查写档案缓存

bool db_player::loop()
{
checkFlush();//检查缓存
return true;
}
db角色写档案检查缓冲区
void db_player::checkFlush()
{if (save_tag != 0) //检查标签{save();save_tag = 0;
}
}
db角色写档案
bool db_player::save()
{
bool ret = false;
mysql_handle *handle = db_server::mysqlPool->getHandle();//获取mysql连接句柄
if (!handle)
{
g_log->error("%u 不能从数据库连接池获取连接句柄", this->id);
return ret;
}
mysql_record column, where;
std::ostringstream os;
os << "charid = " << this->id;
where.put("charid", os.str());if(save_tag & (1 << SAVE_ROLE_DATA)){column.put("pk", roledata.pk);//角色属性......}if(save_tag & (1<<SAVE_ITEM)){uint32 itemSize = *(uint32*)(&item_buffer);column.put("itembinary", (const void *)(&item_buffer[4]), itemSize);//存档道具数据//g_log->debug("save itembinary size :%u", itemSize);}if(save_tag & (1<<SAVE_TASK)){uint32 taskSize = *(uint32*)(&task_buffer);column.put("taskbinary", (const void *)(&task_buffer[4]), taskSize);//存档任务数据//g_log->debug("save taskbinary size :%u", taskSize);}if(save_tag & (1<<SAVE_MONEY)){uint32 moneySize = *(uint32*)(&money_buffer);column.put("moneybinary", (const void *)(&money_buffer[4]), moneySize);//存档金钱数据if(0 != this->gold){g_log->error("角色[%s,%u]save moneybinary gold:%u", this->name,this->id,this->gold);}}if(save_tag & (1<<SAVE_OTHER)){uint32 otherSize = *(uint32*)(&other_buffer);column.put("otherbinary", (const void *)(&other_buffer[4]), otherSize);//存档其他数据//g_log->debug("save otherbinary size :%u", otherSize);}uint32 affect = handle->exeUpdate("ROLE", &column, &where);if (1 == affect || 0 == affect){ret = true;} else{g_log->error("用户 [%u,%s] 存档失败,errno:%u,tag:%u", this->id,this->name,affect,save_tag);ret = false;}db_server::mysqlPool->putHandle(handle);return ret;
}

db角色

class db_player:public player
{
......
//db角色存档缓存
/*** \ 道具存档二进制*/char   item_buffer[ZIP_ITEM_BINARY_SIZE];/***任务存档二进制*/char   task_buffer[ZIP_TASK_SIZE];/*** 金钱存档二进制*/char   money_buffer[UNZIP_MONEY_SIZE];/*** 其他二进制*/char   other_buffer[ZIP_OTHER_SIZE];
};

(2)读档

处理角色登录db服务器消息
创建db角色,读取角色数据档案以及充值数据

bool read = false;
BUFFER_USERCMD(t_playerInfo_SceneRecord, ret);
uint32  size = 0;
t_Loginplayer_InServer *rev = (t_Loginplayer_InServer*)ptrMsg;
db_player *pplayer = g_player_mgr.get_player_by_id(rev->charid);
if(pplayer != NULL)//如果db角色没有下线则需要删除旧的db角色
{g_log->debug("发现DB有角色没有下线:%u,%s",pplayer->id,pplayer->name);pplayer->checkFlush();g_player_mgr.remove_player(pplayer);SAFE_DELETE(pplayer);
}
if(rev->retcode == LoginplayerInServer_centerOK)//登录db服务器
{db_session *scene_temp = (db_session *)g_db_session_manager.getTaskByID(rev->scene_id);//有场景id获取场景服务器连接db_session *gate_temp = (db_session *)g_db_session_manager.getTaskByID(rev->gate_id);//由网关id获取网关服务器连接if(gate_temp && scene_temp){pplayer = new db_player();//创建db角色if(pplayer){pplayer->id = rev->charid;pplayer->accid = rev->accid;pplayer->scene = scene_temp;//记录场景服务器连接pplayer->gateway = gate_temp; //记录网关服务器连接strncpy(pplayer->name, rev->name, sizeof(pplayer->name));g_log->debug("%u,创建指针:%p",pplayer->id,pplayer);if(g_player_mgr.add_player(pplayer)){ret->loginType = READRECORD_LOGIN;ret->fill(*rev);ret->retcode = LoginplayerInServer_DBOK;read = true;if(read){if (pplayer->readRoleData((void*)(&ret->data))) //读取档案{size=sizeof(t_playerInfo_SceneRecord) + ret->data.size;ret->isPtLogin = rev->isPtLogin;}}MSG::stLoginStepSelectplayerCmd step;step.step = MSG::LOGIN_DB;pplayer->sendmsgToMe(&step,sizeof(step));}else{g_log->error("角色登陆(%d,%d,%s)失败,可能是重复角色",pplayer->id,pplayer->accid,pplayer->name);}}}else{g_log->error("角色登陆(%d,%d,%s)失败,场景或者网关关闭", rev->charid,rev->accid,rev->name);}
}
else
{g_log->debug("角色%u,%u登陆DB服务器失败",rev->charid,rev->retcode);g_player_mgr.remove_player_by_id(rev->charid);SAFE_DELETE(pplayer);
}
//返回场景角色注册成功的消息
if (read)
{CHECK_BUFFER(ret);//检查buff的合法性(档案没有超过长度)db_session::sendLoginMsgToScene(rev->scene_id, ret, size);if(pplayer)       {pplayer->getPayInfo(rev->scene_id);//查询角色充值信息并发送到场景服务器if(main_logic_thread::isMergeServer){g_log->info("处理合区充值表");pplayer->getPayInfo_temp(rev->scene_id);}}else{g_log->error("扫描充值表找不到角色%u指针",rev->charid);}
}
else
{error_log("读档失败...........%u,%u",rev->charid,rev->retcode);t_Loginplayer_InServer send(*rev);db_session::sendLoginMsgToScene(rev->scene_id, &send, sizeof(send));
}

从mysql读取角色数据档案

bool db_player::readRoleData(void* _buffer) //角色进地图读档
{mysql_handle *handle = db_server::mysqlPool->getHandle();if (handle) {std::ostringstream os;os.str("");os << " SELECT * FROM ROLE WHERE CHARID = "<< this->id  << " LIMIT "<< 1 ;//选择角色表中的角色数据(这里根据需求可以改动)std::string sql = os.str();mysql_record_set *recordset = handle->exeSelect(sql.c_str(),sql.length());if (recordset && !recordset->empty()) {      SerializeRoleData *data = (SerializeRoleData *) _buffer;data->size = 0;///其它二进制相关{uint32 other = (uint32) recordset->get(0)->getvalue("otherbinary").size();if(other != 0){*((uint32*)(&data->data[data->size])) = BinaryType_Other;data->size += sizeof(uint32);*((uint32*)(&data->data[data->size])) = other;data->size += sizeof(uint32);bcopy((const char *) recordset->get(0)->getvalue("otherbinary"),(void*)(&data->data[data->size]), other);data->size += other;//info_log("get other:%u", other);}}///道具二进制相关{uint32 item = (uint32) recordset->get(0)->getvalue("itembinary").size();if(item != 0){*((uint32*)(&data->data[data->size])) = BinaryType_Item;data->size += sizeof(uint32);*((uint32*)(&data->data[data->size])) = item;data->size += sizeof(uint32);bcopy((const char *) recordset->get(0)->getvalue("itembinary"),(void*)(&data->data[data->size]), item);data->size += item;//info_log("get item:%u", item);}}///货币二进制相关{uint32 money = (uint32) recordset->get(0)->getvalue("moneybinary").size();if(money != 0){*((uint32*)(&data->data[data->size])) = BinaryType_Money;data->size += sizeof(uint32);*((uint32*)(&data->data[data->size])) = money;data->size += sizeof(uint32);bcopy((const char *) recordset->get(0)->getvalue("moneybinary"),(void*)(&data->data[data->size]), money);data->size += money;}}///任务二进制相关{uint32 task = (uint32) recordset->get(0)->getvalue("taskbinary").size();if(task != 0){*((uint32*)(&data->data[data->size])) = BinaryType_Task;//标签data->size += sizeof(uint32);*((uint32*)(&data->data[data->size])) = task;//长度data->size += sizeof(uint32);bcopy((const char *) recordset->get(0)->getvalue("taskbinary"),(void*)(&data->data[data->size]), task);//二进制内容data->size += task;}}data->roledata.accid = this->accid;data->roledata.id = this->id;strncpy(data->roledata.name,(const char*) recordset->get(0)->getvalue("name"),MAX_NAME_LEN);//复制结果集角色属性数据到缓冲区...}SAFE_DELETE(recordset);db_server::mysqlPool->putHandle(handle);}return true;
}

4、压缩解压

场景服务器发送到db服务器的档案中,其中二进制数据是经过压缩处理的,格式如:

标签(4字节) + 数据长度(字节) + 数据(物品或任务或金钱或其他)+ 标签(4字节) + 数据长度(字节) + 数据 + ...

其中数据部分是被 zlib 算法压缩过的。

(1)压缩

压缩函数

template<typename T>
uint32 compressBinary_common(T *out, uint32 bufferSize, char* in, uint32 insize)
{uLongf zsize = bufferSize;int retcode = compress((unsigned char*)out, &zsize, (unsigned char*)in, insize);switch(retcode){case Z_OK:{return zsize;}break;case Z_MEM_ERROR:case Z_BUF_ERROR:{return (uint32)-1;}break;default:{return (uint32)-1;}break;}
}

(2)解压

解压函数

SerializeBinaryMember* uncompressBinary_common(const char *data, const uint32 dataSize, uint8 buffer[], uint32 size)
{bzero(buffer, size);SerializeBinaryMember*tempbinary = (SerializeBinaryMember*)buffer;uLongf unsize = size;if(uncompress((Bytef *)&tempbinary->data[0], &unsize, (Bytef *)data, (uLong)dataSize) != Z_OK){g_log->error("解压数据失败");return NULL;}tempbinary->size = unsize;g_log->info("dataSize:%u, unzipSize :%lu", dataSize, unsize);return tempbinary;
}

解压出来的一个数据管理存档二进制的结构

struct SerializeBinaryMember
{uint32 allsize() { return sizeof(*this)+size; }uint32 type;uint32 size;char data[0];
};

游戏服务器之存档读档相关推荐

  1. 正版七日杀服务器存档,七日杀网吧怎么存档 七日杀网吧存档读档方法介绍-游侠网...

    七日杀的电脑要求配置虽然不是特别高,但是相对于很多玩家的电脑来说还是一个不小的负担的,所以很多玩家会选择去网吧游玩,但是很多玩家发现网吧非主机玩家的人物数据重进总是清零.所以今天小编就为大家带来了便是 ...

  2. Unity_二进制,Jason,XML存档读档

    创建一个类Save 用来保存游戏的信息,以便于存档 Save.cs的代码 using System.Collections; using System.Collections.Generic; usi ...

  3. Python 名片管理系统(文件版,可存档读档)

    Python 名片管理系统(文件版,可存档读档) 相比于函数版的程序,文件版增加了存档读档操作 函数版的可以参考Python名片管理系统(列表.字典和函数的综合应用) 以下是新增部分的代码(不包括调用 ...

  4. SaveLoad--Unity存档读档的学习总结

    存档与读档功能 举例: 传统RPG游戏(仙剑.空之轨迹): 1.角色信息(生命值,等级) 2.道具信息(装备,药品) 3.场景信息(场景名称.角色坐标) 4.事件信息(任务相关) 关卡类游戏:关卡的通 ...

  5. 游戏服务器之防御式开发

    游戏服务端承担着游戏复杂业务逻辑实现,玩家数据持久化等重要作用.作为一个合格的服务端业务狗,我们有必要遵守一些好的防御手段,让自己的代码少踩些坑.或者当出现了bug,能够在第一时间进行抢救. 下边一些 ...

  6. PHP游戏服务器之GlobalData组件的运用

    众所周之,服务器的设计永远不能依赖于单进程/单线程,由于PHP本质上是不支持多线程的,所以在开发过程中,只能把不同的逻辑或者用户分发到不同的进程之间进行处理(这是由系统自己调用分发的).由此可知道在游 ...

  7. LINUX 游戏服务器之旅4_mongodb环境

    安装mongodb 1. 下载MongoDB,此处下载的版本是:mongodb-Linux-i686-3.0.2 http://fastdl.mongodb.org/ 2. 解压文件到某目录下,然后重 ...

  8. 游戏服务器之防加速器

    加速器是网页类游戏常使用的通过修改前端帧频率来达到加速操作目的的工具,常用的有游戏浏览器等.所以前端的时间会变得很快或者很慢(跟后端的比较). 防加速器设计上: 在网关服务器里限制连接发来(客户端发来 ...

  9. 游戏服务器之Timer计时器(定时器)动态链接库【超联网】

    对于游戏服务器而言,计时器(每个多长时间触发一次)和定时器(定点触发)是不可或缺的一个组件.而计时器和定时器的做法又有很多种,我大致将其分成3中: 1,非线程实现计时器和定时器: 2,单线程实现计时器 ...

最新文章

  1. 把 Eclipse 中的工程 Push 到 Github(适用 Windows 平台)
  2. 五子棋java判断平局_2020-10-03 Java初级项目——从零开始制作一个简易五子棋游戏...
  3. 基于Spark MLlib平台的协同过滤算法---电影推荐系统
  4. linux多核单进程,Linux的在多核处理器3个处理(每个进程在不同的核心上运行)之间共享存储器/ SMP...
  5. python包怎么用_python的包怎么应用
  6. 空间谱专题10:MUSIC算法
  7. android虚拟机鼠标左键拖动会输入C
  8. c语言 字符串 正序再倒序_python字符串
  9. 半导体八大工艺流程图_深度研究——半导体之光刻胶,看五大龙头谁能迈出国产化第一步?...
  10. 本地apk安装是什么意思_Sony电视安装第三方播放器
  11. 脱库和删库的实践及解决方案
  12. 3d模型多怎么优化_近似模型之响应面建模
  13. 计算机组成原理——存储器容量扩充
  14. 虚拟机Hyper-V的安装以及使用教程
  15. 3蛋白wb_99% 的实验小白都会收藏,WB、ELISA、IHC 进阶攻略
  16. 去掉word 2007中可恶的信息检索
  17. 【工控老马】西门子PLC Siemens PLC TCP协议详解
  18. RTL8762DW特殊管脚和FLASH的分布
  19. Java项目:物流快递管理系统(java+SSM+jsp+mysql)
  20. 2013九月十月百度人搜,阿里巴巴,腾讯华为小米搜狗笔试面试题

热门文章

  1. 密室逃脱2碧绿色房间
  2. 免费视差特效AE模板素材 Technology Presentation
  3. Java—统计一篇英文文章中出现的不重复单词的个数
  4. js正则效验不能全为数字、字母 不超过50字符
  5. 小程序,解决代码同步执行问题
  6. Oracle 11gR2 新技术 Cardinality Feedback
  7. DSP Flash运行代码
  8. 基于FPGA的数字频率计(设计全过程)
  9. [0x7FF95C3B7860] ANOMALY: use of REX.w is meaningless (default operand size is 64)
  10. python爬取网页json数据_python爬取json数据库