Protocol Buffer c++

最近小编负责游戏开发,游戏附带直播,直播的话已经有方案,可以关注小编的《ios直播》

上一篇小编主要写了cocos怎么搭建protocol buffer ,近期调试socket然后也没时间写文章。难得今天可以写点什么

如果按小编上一篇的操作,应该可以生成如下文件.h跟.cc,

Protocol Buffer c++_第1张图片
1.png

*1.1你用记事本写一个

//简单的一个message 保存成xxx.proto
option optimize_for = LITE_RUNTIME;
message LogonReqMessage {
          required int64 acctID = 1;
          required string passwd = 2;      
}

*1.2用小编的方法生成 LogonReqMessage.h LogonReqMessage.cc文件

cba.png

1.3 PB主要是序列化跟反序列化的一个过程

void testSimpleMessage()
    {
        printf("==================This is simple message.================\n");
        //序列化LogonReqMessage对象到指定的内存区域。
        LogonReqMessage logonReq;
        logonReq.set_acctid(20);
        logonReq.set_passwd("Hello World");
        //提前获取对象序列化所占用的空间并进行一次性分配,从而避免多次分配
        //而造成的性能开销。通过该种方式,还可以将序列化后的数据进行加密。
        //之后再进行持久化,或是发送到远端。
        int length = logonReq.ByteSize();
        char* buf = new char[length];
        logonReq.SerializeToArray(buf,length);
        //从内存中读取并反序列化LogonReqMessage对象,同时将结果打印出来。
        LogonReqMessage logonReq2;
        logonReq2.ParseFromArray(buf,length);
        printf("acctID = %I64d, password = %s\n",logonReq2.acctid(),logonReq2.passwd().c_str());
    //用完之后delete ,释放内存
        delete [] but;
    }

2.1以上是最简单的PB,小编拿自己项目中的PB 给大家演示一下实际开发中怎么做

//进入房间响应
message RoomEnterMessage {
    optional string roomName = 1;//房间名
    optional int32 blindLimit = 2;//大盲注
    repeated PlayerDataMsg actorData = 3;//玩家数据
    repeated string communityCards = 4;//公共牌
    repeated int32 pot=5; //底池 与边池
    optional string videoUrl = 6;//视频地址
    repeated string cards = 7;//自己手牌
    optional string actorId = 8;//玩家自己ID
    optional string nickName=9;//自己昵称
    optional int32 userType=10;//用户类型
    
}
//玩家数据结构体 有玩家进入房间时对其他玩家推送 :command=1120
message PlayerDataMsg {
    optional string actorId = 1;
    optional string actorName =2;//昵称
    optional int32 location = 3;//位置
    optional PlayerGameData gameData =4;
}

//游戏玩家数据
message PlayerGameData {
    optional string actorId = 1;
    optional int64 totalAmount=2; //剩余总筹码
    optional int32 betAmount=3; //下注筹码
    optional int32 action=4; //操作  跟注、加注等
    optional int32 actionTime = 5;//操作时间
}


*2.2 老规矩生成.h .cc文件

Protocol Buffer c++_第2张图片
opq.png
Protocol Buffer c++_第3张图片
iop.png

*2.3 socket连接,发送你这边的内容

void HelloWorld::connectServer()
{
    //初始化
    socket.Init();
    socket.Create(AF_INET, SOCK_STREAM,0);
    
    //设置服务器的ip地址,端口号
    //并连接服务器 Connect
    const char *ip = "192.168.1.68";
    int port = 9090;
    //发送连接
    bool result = socket.Connect(ip, port);
//    Data();
//    Data *data = 0x7a1d;
    
    
    RoomEnterRequest room;
    room.set_sessionid("2");
    room.set_roomid(2);
    std::string str=room.SerializeAsString();
    
    int len =  room.SerializeAsString().length() + 2;
    
//    “序列化”(Serialization)
//    “反序列化” (Deserialization)
    
    
    PbDataModule* pb;
    char *buff = pb->getDataWith(len,8, str);
    int idx = 8;
    
    socket.Send(buff,idx+len-2);
    
    std::string roomStr = room.SerializeAsString();
   
    //发送数据Send
    socket.Send(roomStr.c_str(), 5);
    
    if (result) {
        CCLOG("connect to server success!");
        //开启新线程,在子线程中,接收数据
        std::thread recvThread = std::thread(&HelloWorld::receiveData,this);
        recvThread.detach();//从主线程分离
    }else{
        CCLOG("can not connect to server");
        return;
    }

}

注意:RoomEnterRequest room这是我这边socket请求的pb,这中间有序列化得过程我把代码贴出来,

char* PbDataModule::getDataWith(int len,int cout,std::string roomStr)
{
//这是我们项目定义好的,如果读者序列化有问题可以咨询下小编,这一块底层要求要扎实

/*
序列化过程
*/
    unsigned char h[cout],*p;//先用一个容器,这个容器将存你请求socket字节流
    int  idx = 0;//第一位是0
    h[idx++]= 0x7a;//第二位7a
    h[idx++] = 0x1d;//第三位1d
    
    p = (unsigned char*)&len;
    h[idx++] = p[3];
    h[idx++] = p[2];
    h[idx++] = p[1];
    h[idx++] = p[0];
    
    
    h[idx++] = 0x04;//第七位04
    h[idx++] = 0x56;//第八位56
    
    
    char* buff = new char[idx + len -2];
    memcpy(buff,h,idx);
    memcpy(buff+idx,roomStr.c_str(),len-2);
    
    return buff;
    
    delete [] buff;//用完之后释放内存
}

*3.1前面一块就是socket请求,后面就是socket接收消息,解析PB的过程,小编全程注释

void HelloWorld::receiveData()
{
    //因为是强联网
    //所以可以一直检测服务端是否有数据传来
    while (true) {
        //接收数据 recv
        char data[512] = "";
        
        int result = socket.Recv(data, 512,0);
        
        printf("result,%d\n",result);
        //这里打印出来的接收的二进制流(data)总共是124个字节
        
        //与服务器的连接断开了
        if (result <= 0) {
            break;
            
        }
/*
反序列化过程
*/
        //将二进制的头,错误,还有长度用数组提取出来
        char chhead[2];
        char chlen[4];
        char chcmdcode[2];
        char cherrorcode[2];
        
       //data总共是124个字节
        //下面是取得过程
        //它的前两个字节,0、1是头
        memcpy(chhead, data, sizeof(chhead));
        //它的字节是长度是4个字节 从data的第2位之后开始取
        memcpy(chlen, data+2, sizeof(chlen));
        //它的字节是长度是2个字节 从data的第6位之后开始取
        memcpy(chcmdcode, data+6, sizeof(chcmdcode));
        //它的字节是长度是2个字节 从data的第8位之后开始取
        memcpy(cherrorcode, data+8, sizeof(cherrorcode));
        
        //剩下的字节就是pb的二进制流,从第十位开始取,那么只要result(总字节长度)-10
        char *pdata = new char[result-10];
        memcpy(pdata, data+10, result-10);//从第十个字节开始拷贝到 到pdata里面去
        
        RoomEnterMessage msgOut;
        //解析该字符串
        //问题出现在这里了。当把char*传入ParseFromString时,会把char*转换成string类型,会在第一个'\0'的地方,把这个缓冲区给截断,问题就出现在这里了。
        std::string strData(pdata,result-10);
        
        if (msgOut.ParseFromString(strData)) {
            //解析该字符串
            CCLOG("房间名:%s  大盲注:%d",msgOut.roomname().c_str(),msgOut.blindlimit());
            CCLOG("自己昵称%s 玩家自己ID%s  ",msgOut.nickname().c_str(),msgOut.actorid().c_str());
           // CCLOG("0:%s   1:%s",msgOut.cards(0).c_str(),msgOut.cards(1).c_str());
             //将socket返回的存入到单例当中
            Global::shareGlobal()->setRoomName(msgOut.roomname());
            Global::shareGlobal()->setBlindLimit(msgOut.blindlimit());
            Global::shareGlobal()->setActorId(msgOut.actorid());
            Global::shareGlobal()->setNickName(msgOut.nickname());
            //取出嵌套的PlayerDataMsg
            google::protobuf::RepeatedPtrField* dateMsg = msgOut.mutable_actordata();
            google::protobuf::RepeatedPtrField::iterator it = dateMsg->begin();
            
            for (; it != dateMsg->end(); ++it) {
                
                CCLOG("actorId:%s  actorName:%s  location:%d  ",it->actorid().c_str(),it->actorname().c_str(),it->location());
                //存入单例当中
                PDataMsg::getInstance()->setActorId(it->actorid());
                PDataMsg::getInstance()->setActorName(it->actorname());
                PDataMsg::getInstance()->setLocation(it->location());
                
                CCLOG("下注筹码:%d",it->gamedata().betamount());
                
//                CCLOG("剩余总筹码:%d",it->gamedata().totalAmount());
                
                CCLOG("操作  跟注、加注等:%d",it->gamedata().action());
                 //存入单例当中
                PGameData::getInstance()->setBetAmount(it->gamedata().betamount());
                PGameData::getInstance()->setAction(it->gamedata().action());
                
                
            }
            
            
            
        }
        
        auto scene = StartScene::createScene();
        auto tt = TransitionFade::create(1.0f, scene);
//        Director::getInstance()->replaceScene(tt);
        
        delete []pdata;
        
    }
    
    
    //关闭连接
    socket.Close();
}


如果小编讲的比较模糊,看不懂什么意思,那就惆怅了,惭愧了。

有问题留言吧,大家一起学习,

如果喜欢就点个赞哦!

你可能感兴趣的:(Protocol Buffer c++)