游戏服务器之内存数据库redis客户端应用(下)

(3)存储一个角色的基础信息(使用命令set)

存储结构:

key:BASE角色id ,value 角色基础信息

int playerId = player->get_player_base()->m_player_id;  
char tmpBuf[64];  
memset(tmpBuf,0,64);  
sprintf(tmpBuf,"BASE%d",playerId);  
string key(tmpBuf);  
CRWRedisClient redisClient;  
redis::client* tmpRedisClient = redisClient.get_redis_client();  
if(NULL == tmpRedisClient)  
{  
 return ;  
}  
try 
{  
    tmpRedisClient->set(key, value);  
}  
catch (redis::redis_error & e)  
{  
    cerr << "got exception: " << e.what() << endl << "FAIL" << endl;  
 return ;  
}  

4、读取redis的客户端应用

(1)一次获取一个玩家的装备包裹的所有道具(使用命令hgetall)

存储结构:

key : EQUIPMENTBAGplayerId frield: pos value:CBagItem

bool CRWRedisClientOperator::load_player_equipbag_from_redis(CGamePlayer* player)  
{  
 if(NULL == player)  
    {  
 return false;  
    }  
 //通过playerId得到key值 
 int playerId = player->get_player_base()->m_player_id;  
 if(0 == playerId)  
    {  
 return false;  
    }  
 
 char tmpBuf[64];  
    memset(tmpBuf,0,64);  
 //根据键(EQUIPMENTBAG%d",playerId),获取该玩家 所有装备道具的数据到vector<pair<string,string> > strPairVec 
 //使用到接口void hgetall( const string_type & key, string_pair_vector & out ) 
    sprintf(tmpBuf,"EQUIPMENTBAG%d",playerId);  
    string key(tmpBuf);  
 if(false == CRWRedisClientOperator::instance()->is_key_exist_in_redis(key))  
    {  
 return false;  
    }  
    vector<pair<string,string> > strPairVec;  
 //通过key值从redis取数据 
    CRWRedisClient redisClient;  
    redis::client* tmpRedisClient = redisClient.get_redis_client();  
 if(NULL == tmpRedisClient)  
    {  
 return false;  
    }  
 try 
    {  
        tmpRedisClient->hgetall(key,strPairVec);//获取一个玩家的所有装备背包道具 
    }  
 catch (redis::redis_error & e)  
    {  
        cerr << "got exception: " << e.what() << endl << "FAIL" << endl;  
 return false;  
    }  
 //得到背包 
    CPlayerBag* tmpBag = player->get_player_bag();  
 if(NULL == tmpBag)  
    {  
 return false;  
    }  
    vector<pair<string,string> >::iterator iter = strPairVec.begin();  
 for(;iter != strPairVec.end();iter++)  
    {  
        string frield = iter->first;  
 int pos = ACE_OS::atoi(frield.c_str());  
        string value = iter->second;  
 if(value.length() != sizeof(CBagItem))  
        {  
 return false;  
        }  
        CBagItem* equipItem = new CBagItem();  
 if(NULL == equipItem)  
        {  
 return false;  
        }  
        memcpy(equipItem,value.c_str(),value.length());  
 if(!tmpBag->insert_bagitem_equip(pos,equipItem))  
        {  
 return false;  
        }  
    }  
 return true;  
}  

(2)读取角色的基础信息(使用命令get)

存储结构:

key BASE角色id,value 角色基础信息

char tmpBuf[64];  
memset(tmpBuf,0,64);  
sprintf(tmpBuf,"BASE%d",player->get_player_base()->m_player_id);  
string key(tmpBuf);  
 
struct CPlayerBase playerMsg;  
memset(&playerMsg, 0, sizeof(CPlayerBase));  
string getValue;  
CRWRedisClient redisClient;  
redis::client* tmpRedisClient = redisClient.get_redis_client();  
 
if(NULL == tmpRedisClient)  
{  
 return false;  
}  
try 
{  
 if (false == tmpRedisClient->exists(key))  
    {  
        cout <<key <<" not exists !!!" << endl;  
 return false;  
    }  
    getValue = tmpRedisClient->get(key);  
}  
catch (redis::redis_error & e)//对于会抛出异常的接口,需要捕捉异常 
{  
    cerr << "got exception: " << e.what() << endl << "FAIL" << endl;  
 return false;  
}  
 
if(getValue.length() > sizeof(CPlayerBase))//如果比需要的长度要大,则是不合法的 
{  
 return false;  
}  
memcpy(&playerMsg, getValue.c_str(), getValue.length());//直接在redis,copy到player中 
//开始拷贝数据到角色指针的数据里 
player->get_player_base()->m_player_id = playerMsg.m_player_id;  
......  

5、redis客户端池

客户端池的初始化

bool CRedisClientPool::init_redis_client_pool()  
{  
    CRedisServer tmpRedisServer = CConfigManager::instance()->get_srv_config().get_redis_server_conf();  
    string redisIp = tmpRedisServer.get_ip();  
 int port = tmpRedisServer.get_port();  
 for(int i = 0; i < 5; i++)  
    {  
 if(false == init_redis_client(redisIp,port))  
        {  
 return false;  
        }  
    }  
 return true;  
}  

客户端连接初始化

bool CRedisClientPool::init_redis_client(string redisIp, int port)  
{  
 try 
    {  
        redis::client* m_redis_client = new redis::client(redisIp,port,"");  
 if(NULL != m_redis_client)  
        {  
            push_redis_client(m_redis_client);//压到redis客户端池列表 
        }  
    }  
 catch (redis::redis_error & e)  
    {  
        cerr << "got exception: " << e.what() << endl << "FAIL" << endl;  
 return false;  
    }  
 return true;  
}  

添加客户端连接到客户端池

void CRedisClientPool::push_redis_client(redis::client* redisClient)  
{  
 if(NULL == redisClient)  
    {  
 return ;  
    }  
    m_q_mutex.acquire();  
    m_redis_client_list.push_back(redisClient);  
    m_q_mutex.release();  
}  

6、第三方的库接口

(1)redis客户端对象

typedef base_client<default_hasher> client;  
struct default_hasher  
  {  
 inline size_t operator()(const std::string & key, const std::vector<connection_data> & connections)  
    {  
 return boost::hash<std::string>()(key) % connections.size();//每次操作是根据键哈希获取连接列表里的一个,这样每个键可以尽量使用不同的连接(可能是为了某些多线程场景的减少锁竞争) 
    }  
  };  
 
template<typename CONSISTENT_HASHER>  
 class base_client  
  {  
 private:  
 void init(connection_data & con)  
    {  
 char err[ANET_ERR_LEN];  
      con.socket = anetTcpConnect(err, const_cast<char*>(con.host.c_str()), con.port);//使用anet库做网络通信客户端接口 
 if (con.socket == ANET_ERR)  
      {  
        std::ostringstream os;  
        os << err << " (redis://" << con.host << ':' << con.port << ")";  
 throw connection_error( os.str() );  
      }  
      anetTcpNoDelay(NULL, con.socket);  
      select(con.dbindex, con);  
    }  
 
 
 public:  
   ...  
 explicit base_client(const string_type & host = "localhost",  
                    uint16_t port = 6379,const string_type &pwd ="",int_type dbindex = 0)  
    {  
      connection_data con;  
      con.host = host;  
      con.port = port;  
      con.dbindex = dbindex;  
      con.pwd = pwd;  
      init(con);  
      connections_.push_back(con);//初始化连接后放到连接列表里 
    }  

(2)接口函数

获取redis的哈希表的值

void hgetall( const string_type & key, string_pair_vector & out )  
    {  
 int socket = get_socket(key);  
      send_(socket, makecmd("HGETALL") << key);//命令 
      string_vector s;  
      recv_multi_bulk_reply_(socket, s);//获取多个返回的回应消息作为HGETALL 的结果 
 for(size_t i = 0; i < s.size(); i+=2)  
        out.push_back( make_pair(s[i], s[i+1]) );  
    }  

插入到redis的哈希表用到的

void hmset( const string_type & key, const string_pair_vector & field_value_pairs )  
    {  
 int socket = get_socket(key);  
      makecmd m("HMSET");//命令 
      m << key;  
 for(size_t i=0; i < field_value_pairs.size(); i++)  
        m << field_value_pairs[i].first << field_value_pairs[i].second;//把一个vector的键值发送过去设置 
      send_(socket, m);  
      recv_ok_reply_(socket);//接收应答结果 
    }  

get命令

string_type get(const string_type & key)  
    {  
 int socket = get_socket(key);  
      send_(socket, makecmd("GET") << key);  
 return recv_bulk_reply_(socket);  
    }  

读取网络数据

std::string recv_bulk_reply_(int socket)  
    {  
      int_type length = recv_bulk_reply_(socket, REDIS_PREFIX_SINGLE_BULK_REPLY );  
 
 if (length == -1)  
 return missing_value();  
 
      int_type real_length = length + 2;    // CRLF 
 
      std::string data = read_n(socket, real_length);  
 
#ifndef NDEBUG 
 //output_proto_debug(data.substr(0, data.length()-2)); 
#endif 
 
 if (data.empty())  
 throw protocol_error("invalid bulk reply data; empty");  
 
 if (data.length() != static_cast<std::string::size_type>(real_length))  
 throw protocol_error("invalid bulk reply data; data of unexpected length");  
 
      data.erase(data.size() - 2);  
 
 return data;  
    }  

(3)定义的异常

定义的redis异常的基础类

class redis_error : public std::exception  
  {  
 public:  
    redis_error(const std::string & err) : err_(err) {}  
 virtual ~redis_error() throw () {}  
    operator const std::string () const { return err_; }  
 virtual const char* what() const throw ()  
    {  
 return err_.c_str();  
    }  
 
 
 private:  
    std::string err_;  
  };  

原文发布于微信公众号 - Golang语言社区(Golangweb)

原文发表时间:2016-08-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏C#

C#文件安全管理解析

    在实际的项目开发中,我们经常需要使用到文件的I/O操作,主要包含对文件的增改删查等操作,这些基本的操作我们都是很熟悉,但是较少的人去考虑文件的安全...

41110
来自专栏码农阿宇

.Net Core中利用TPL(任务并行库)构建Pipeline处理Dataflow

在学习的过程中,看一些一线的技术文档很吃力,而且考虑到国内那些技术牛人英语都不差的,要向他们看齐,所以每天下班都在疯狂地背单词,博客有些日子没有更新了,见谅见谅...

1041
来自专栏跟着阿笨一起玩NET

一步一步学Linq to sql(七):并发与事务

为了看起来清晰,我已经事先把所有分类为1产品的价格和库存修改为相同值了。然后执行下面的程序:

612
来自专栏技术小讲堂

State模式的经典应用场景:订单处理(c#实现)场景描述遇到问题解决问题走起

State模式在对象内部状态发生变化的时候,改变自身的行为,这通常是通过切换内部状态对象实现的,对象将自身在各个状态的行为推给了状态对象,从而解开了行为与对象的...

2794
来自专栏技术博客

Entity Framework 简单查询

第一步还是先建立一个控制台的应用程序,然后通过Nuget添加Entity Framework。那么同时会给packages.config和App.config添...

1012
来自专栏大内老A

事件(Event),绝大多数内存泄漏(Memory Leak)的元凶[上篇]

最近这两天一直在忙着为一个项目检查内存泄漏(Memory Leak)的问题,对相关的知识进行了一下简单的学习和探索,其间也有了一些粗浅的经验积累,今天特意写一篇...

2456
来自专栏dotnet & java

Bootstrap-3-Typeahead

是Bootstrap-3-Typeahead,不是Twitter open source的typeahead,两者用法有差异。外加如果配合原生的Bootstra...

1636
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(21)-权限管理系统-跑通整个系统

这一节我们来跑通整个系统,验证的流程,通过AOP切入方式,在访问方法之前,执行一个验证机制来判断是否有操作权限(如:增删改等) 原理:通过MVC自带筛选器,在筛...

6257
来自专栏码农阿宇

.Net Core中利用TPL(任务并行库)构建Pipeline处理Dataflow

在学习的过程中,看一些一线的技术文档很吃力,而且考虑到国内那些技术牛人英语都不差的,要向他们看齐,所以每天下班都在疯狂地背单词,博客有些日子没有更新了,见谅见谅...

2281
来自专栏程序员的SOD蜜

一行代码调用实现带字段选取+条件判断+排序+分页功能的增强ORM框架

问题:3行代码 PDF.NET 是一个开源的数据开发框架,它的特点是简单、轻量、快速,易上手,而且是一个注释完善的国产开发框架,受到不少朋友的欢迎,也在我们公...

2949

扫码关注云+社区

领取腾讯云代金券