美文网首页
Protocol Buffer c++

Protocol Buffer c++

作者: 小小希奇幻记 | 来源:发表于2016-07-15 16:27 被阅读634次

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

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

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

    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文件

    opq.png 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<PlayerDataMsg>* dateMsg = msgOut.mutable_actordata();
                google::protobuf::RepeatedPtrField<PlayerDataMsg>::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++

          本文链接:https://www.haomeiwen.com/subject/dtmyjttx.html