最近小编负责游戏开发,游戏附带直播,直播的话已经有方案,可以关注小编的《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.png1.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();
}
网友评论