cocos2d-x lua 中使用protobuf并对http进行处理

cocos2d-x lua 中使用protobuf并对http进行处理

本文介绍 cocos2d-x lua 中使用http 和 基于cocos2d-x 对lua http的封装(部分ok)

本博客链接

http://blog.csdn.net/vpingchangxin/article/details/24458051

protobuf  Google的一个很好用的传输数据的封装 说实话Google的东西确实比较好用 所以我们前后端数据交换就用他了 不过Google没有对lua进行支持 还好社区有开源的大侠们贡献 找了所有关于lua protobuf 我只找到 云风的 pbc 修改相关cocos2d-x中的类可以正常使用。protoc-gen-lua 我在使用的时候 总是报截断数据 在修改后cocs2d-x中的类之后没有对protoc-gen-lua 进行测试是否是这个问题导致

1)集成 云风 云大侠的(博客lua-pbc  标准c写的protobuf 具体看pbc的帮助很轻松集成

2) 生成pb文件(我自己写了个mac中批处理生成所有.proto文件为.pb文件)把pb 和proto文件都加入到项目资源中

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. #pb = "pb"  
  3. for i in *.proto  
  4. do  
  5.     #echo $i  
  6.     #echo ${i%.*}".pb"  
  7.     #echo ${i%.*}  
  8.     #pbn = $i | cut -d.  
  9.     pbname=${i%.*}".pb"  
  10.     #echo $pbn  
  11.     #echo $pbname  
  12.     protoc --descriptor_set_out $pbname $i  
  13. done  
  14. echo "finish"  

也可以用命令行手动生成

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protoc --descriptor_set_out aaa.pb aaa.proto  
3) 本步骤可以忽略了,可以直接用io进行读取(Android是路径问题请看本文最下面解释) 在lua中使用如下代码(我用的是cocos2d-x中绑定的CCFileUtils中的获取文件的方式,不过要手动用tolua++进行绑定到lua,可以参考我上个文章中的绑定方式,云大侠中的 用lua io形式获取在相关了解中不能跨平台所有就用这个了)
[javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. local protobuf = require "protobuf"  
  2.     local buffer = CCFileUtils:sharedFileUtils():getFileData("entity/p_result.pb","r",0)  
  3.     -- print(buffer)  
  4.     protobuf.register(buffer)  
4) 本步骤可以忽略了,可以直接用io进行读取(Android是路径问题请看本文最下面解释)  上一步完成后我们要对提到的CCFileUtils.cpp中的类进行修改 如果不修改读文件pb文件会时好时坏 原因是 读文件的时候结束总是添加多余字节我也不清楚这个问题 进行修改cocos2d-xCCFileUtils.cpp的下面方法中的读取数据后处理并在tolua++ 中添加下面方法绑定到lua层

修改CCFileUtils.cp getFileData(const char* pszFileName, const char* pszMode,unsignedlong * pSize)方法(在最后添加\0,保证字节不多余)如下代码

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. unsigned char* CCFileUtils::getFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize)  
  2. {  
  3.     unsigned char * pBuffer = NULL;  
  4.     CCAssert(pszFileName != NULL && pSize != NULL && pszMode != NULL, "Invalid parameters.");  
  5.     *pSize = 0;  
  6.     do  
  7.     {  
  8.         // read the file from hardware  
  9.         std::string fullPath = fullPathForFilename(pszFileName);  
  10.         FILE *fp = fopen(fullPath.c_str(), pszMode);  
  11.         CC_BREAK_IF(!fp);  
  12.           
  13.         fseek(fp,0,SEEK_END);  
  14.         *pSize = ftell(fp);  
  15.           
  16.         fseek(fp,0,SEEK_SET);  
  17.         pBuffer = new unsigned char[*pSize];  
  18.         *pSize = fread(pBuffer,sizeof(unsigned char), *pSize,fp);  
  19.         fclose(fp);  
  20.           
  21.     } while (0);  
  22.       
  23.     if (*pSize >0 && pBuffer[*pSize] != '\0')  
  24.         pBuffer[*pSize] = '\0';  
  25.       
  26.     if (! pBuffer)  
  27.     {  
  28.         std::string msg = "Get data from file(";  
  29.         msg.append(pszFileName).append(") failed!");  
  30.           
  31.         CCLOG("%s", msg.c_str());  
  32.     }  
  33.     return pBuffer;  
  34. }  

5)经过上一步骤lua层基本搞定可以创建本地的数据并encode成传输数据到服务器端了 如下代码

[javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. local major = {  
  2.         majorId = "795f94a9-3466-41b4-bf16-043ba8081fab"  
  3.     }  
  4.     local buffer = protobuf.encode("com.sj.web.proto.Major", major)  
6)我们客户端数据传输到服务器端 服务器端会返回数据给我们 同样我们接收的数据肯定也是protobuf数据了 用 protobuf.decode进行解数据
[javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. local t = protobuf.decode("com.sj.web.proto.Result", request:getResponseString())--tolua.cast(event.dataCString))--tolua.cast(event.dataCString,"CCString"):getCString())  
  2.         cclog(t)  
  3.           
  4.         print(t.major.gender)  
  5.         print(t.major.majorId)  
  6.         print(t.user.username)  
7)上一步中的数据是服务器端过来的数据,不过在http连接方面遇到了些小插曲

 (1)我先前用的是quick-cocos2d-x-lua中封装的CCHTTPRequest的这个进行服务器端交互 不过不如愿 因为服务器端过来的数据中是protobuf进行处理过的数据 在进行调试跟踪后发现过来的数据中不定什么地方都有\0结束符 这个导致直接在lua中调研CCHTTPRequest中的获取string 方法数据被截断不能正常解析 我在CCHTTPRequest::getResponseString进行处理过来的数据处理掉\0 也不行

(2)由于项目中要用的短连接socket我先前已经集成好luasocket,其实这个开源的socket很好用 也有对http的支持果断用这个测试下服务器端回来的数据 让我小小喜悦了一下 丢到protobuf.decode中进行解析正是我要的数据 不过有个不好的地方 luasocket对socket有设置一个超时时间 就可以不阻塞线程 但是htpp方式我找遍了网站上的资料也没找到非阻塞式的 不过这个没关系比较可以正常跑protobuf了 集成一个线程框架就ok了呀 可以用协同线程 或者是lua llthreads 自己选择吧如果要用 luasocket的http 先附上luasocket的http代码

[javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. local http = require 'socket.http'  
  2. local ltn12 = require 'ltn12'  
  3. response_body = ""  
  4. request_body = ""  
  5.   
  6. function http.post(u)  
  7.     local t = {}  
  8.     local r, c, h = http.request{  
  9.         url = u,  
  10.         method = "POST",  
  11.         headers = {  
  12.             ["Content-Type"] = "application/x-protobuf",  
  13.             ["Content-Length"] = #request_body,  
  14.         },  
  15.         source = ltn12.source.string(request_body),  
  16.         sink = ltn12.sink.table(t)}  
  17.     return r, c, h, t  
  18. end  
  19.   
  20. -- url = "http://www.baidu.com"  
  21. r,c,h,body=http.post(HTTP_URL)  
  22. print(c)  
  23. if c~= 200 then  
  24.     return  
  25. end  
  26. local protobuf = require "protobuf"  
  27. local buffer = CCFileUtils:sharedFileUtils():getFileData("entity/p_result_test.pb","r",0)  
  28. -- print(buffer)  
  29. protobuf.register(buffer)  
  30.   
  31. local t = protobuf.decode("com.sj.web.proto.Result", body[1])  
  32. cclog(t)  

(3)不过我没有用上面中提到的luasocket http 个人感觉还是直接用coco2d-x中的CCHttpClient比较好用也不用处理线程的东西 因为我通过这个测试过c++层中protobuf进行解析是完全没问题的所以我就模仿(1)中提到的CCHTTPRequest对cocos2d :: extension :: CCHttpClient封装使用不过有点不顺利数据传到lua层还是不正常, 我就进行对\0 进行处理 又让我喜悦了  处理获取的服务器数据如下
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. std::vector<char> *data = response->getResponseData();  
  2.     std::stringstream mystream;  
  3.     for(int i=0;i<data->size();i++){  
  4.         //if ((*data)[i] != '\0') {  
  5.             mystream << (*data)[i];  
  6.         //}else{  
  7.         //    mystream << '\b';  
  8.         //}  
  9.     }  
  10.     mResponseData = mystream.str();  
  11.       
  12.     std::cout << mystream.str() << std::endl;  
  13.     CCLog("ddd:%s",mystream.str().c_str());  
  14.     CCString * cstr = CCString::create(temp);  
  15. //    com::sj::web::proto::Result *r = new com::sj::web::proto::Result::Result();  
  16. //    r->ParseFromString(temp.c_str());  
  17. //    CCLog("ParseFromString:::::::::%d  %s",r->resultcode(),r->release_major()->majorcode().c_str());  
  18.     CCLuaValueDict dict;  
  19.     dict["request"] = CCLuaValue::ccobjectValue(this"HTTPRequest");  
  20.     dict["data"] = CCLuaValue::stringValue(mystream.str()); // 传值回到lua层  
  21.     dict["dataCString"] = CCLuaValue::ccobjectValue(cstr, "CCString");  
  22.     dict["dddd"] = CCLuaValue::stringValue("dssddsdsds");  
  23.       
  24.     LUA_FUNCTION listener = (LUA_FUNCTION)response->getHttpRequest()->getUserData();  
  25.     CCLuaStack *stack = CCLuaEngine::defaultEngine()->getLuaStack();  
  26.     stack->clean();  
  27.     stack->pushCCLuaValueDict(dict);  

做好上一步进行修改cocos2d-x CCLuaStack.cpp 方法pushCCLuaValue(....) 中 代码293行代码,修改如下(这样做可以直接使用 lua_pushlstring(m_state, stringValue, length) 把所有数据压到lua上层)

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. return pushString(value.stringValue().c_str(), value.stringValue().length());  

完整方法代码为:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void CCLuaStack::pushCCLuaValue(const CCLuaValue& value)  
  2. {  
  3.     const CCLuaValueType type = value.getType();  
  4.     if (type == CCLuaValueTypeInt)  
  5.     {  
  6.         return pushInt(value.intValue());  
  7.     }  
  8.     else if (type == CCLuaValueTypeFloat)  
  9.     {  
  10.         return pushFloat(value.floatValue());  
  11.     }  
  12.     else if (type == CCLuaValueTypeBoolean)  
  13.     {  
  14.         return pushBoolean(value.booleanValue());  
  15.     }  
  16.     else if (type == CCLuaValueTypeString)  
  17.     {  
  18.         return pushString(value.stringValue().c_str(), value.stringValue().length()); //pushString(value.stringValue().c_str());  
  19.     }  
  20.     else if (type == CCLuaValueTypeDict)  
  21.     {  
  22.         pushCCLuaValueDict(value.dictValue());  
  23.     }  
  24.     else if (type == CCLuaValueTypeArray)  
  25.     {  
  26.         pushCCLuaValueArray(value.arrayValue());  
  27.     }  
  28.     else if (type == CCLuaValueTypeCCObject)  
  29.     {  
  30.         pushCCObject(value.ccobjectValue(), value.getCCObjectTypename().c_str());  
  31.     }  
  32. }  

好了就介绍到这里吧希望对cocos2d-x lua 开发的同行们有所帮助 如果有什么好的protobuf对cocos2d-x lua 的支持 或者是更方便的集成 也请贡献给我一份哟 谢谢

本人用风云的pbc 心得 总的来说还算可以

1)浮点类型:java—>lua 没问题;lua—>java 没通过。我们后端用的java 估计是lua对number浮点数类型进行decode的 有整形进行传递的时候导致没有进行对浮点类型转换(或者java对类型比较严格导致解码的时候对高地位转换失败所致) 由于开发效率前后端改成string进行传递(测试过protoc-gen-lua 也有这个问题)如有朋友能使用lua—>java进行浮点类型通信OK 迫切滴希望您共享我一份解决方案

2)枚举类型 proto文件中序号从1开始计数 否则pbc pb文件加载失败

3)上面提到的加载pb文件的时候ios win 也可以根据 CCFileUtils获取pb文件路径后用 lua io进行注册,测试android(我还是用上面提到的方法进行读文件)用这种方式没成功 应该是路径和权限问题导致(已经验证过是android中读取文件路径问题,我的方式是把proto写到本地后lua用io进行读取ok)读不到文件 

[javascript]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. --其他平台  
  2. local filePath = CCFileUtils:sharedFileUtils():fullPathForFilename("entity/p_result.pb")  
  3.         local addr = io.open(filePath,"rb")  
  4.         protobuffer = addr:read "*a"  
  5.         addr:close()  
  6.         protobuf.register(protobuffer  
  7. -- android如下  
  8. local filePath = callJava()  
  9.         local addr = io.open(filePath,"rb")  
  10.         protobuffer = addr:read "*a"  
  11.         addr:close()  
  12.         protobuf.register(protobuffer)  

最近Google产品及服务全面禁封 表示对国内互联网管控的表示吐槽 但是好的技术成果是无界的(我对protobuf的使用一如既往,如有想用的,请代理去下载吧) 对protobuf的使用总结

1)发现数据传输上字节上特别节省轻量,从这个优势上传输速度明显快过其他如json,尤其适合游戏通信,如果是考虑到用户通信流量及带宽资源节省问题可以考虑

2)一点不好就是客户输出数据不全估计是pbc反序列化优化table对象问题 (和后端交互数据全靠java写的小客户端日志输出查看,只是为了方便查看后端返回数据) 然后回lua项目进行处理信息


转载一些自己感觉好的文章,只是为了记录,以后查找方便,大家尽量去阅读原文。

原文地址:http://blog.csdn.net/vpingchangxin/article/details/24458051

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值