cocos2dx-lua 3.x socket 发送protobuf包装的数据

目前博客或者论坛中,几乎很少有cocos2dx-lua socket连接发送protobuf的博客,我也走了很多弯路才调通了protobuf。现在给大家详细的写下protobuf的流程,大牛们不要喷,小弟不才,帮忙看看还有什么漏洞。

一.lua-protobuf,目前网上cocos2dx-lua去集成protobuf的例子有很多,本篇主要去介绍怎么去使用protobuf,所以在这里我给大家发一个链接(转载别人的,这个人写的很详细,有问题也可以私信我)《cocos2dx 3.x 集成protobuf》。

二.lua-socket,我这里就以cocos2dx-lua 3.3为例子。

(1)首先创建一个socket

require("socket")
require("protobuf")--集成的protobuf
local n,receive_status = socket:connect("192.168.1.125", 9999)
print(n,receive_status)--是否连接正常
socket:settimeout(0)

到这里已经完成了socket的创建。接下来就是两个很重要的方法,send和receive,很多人会说lua发送数据给后台直接发json啊,但是这里在效率上是比不过protobuf的,这个知识点大家可以看看《protobuf与json》。

(2)send方法遇到的问题以及解决方法

local mmoPbFilePath = cc.FileUtils:getInstance():fullPathForFilename("res/MMO.pb")
   local mmoBuffer = read_protobuf_file_c(mmoPbFilePath)
   protobuf.register(mmoBuffer)

   local mmobufferStr = protobuf.encode("MMOProto.Hello",{
       id = 999,
       name="小明",
       isMale = false,
       })

在没了解protobuf之前我直接用

socket:send(mmobufferStr) 

这导致服务器一直收不到数据,这就欠缺了分包粘包的概念的概念。可以参考《socket分包和粘包》。这里简单介绍下socket分包粘包的三种方法:

        1.消息头,消息体:消息头笼统的来说就是消息体的长度,根据消息头的长度去获取消息体,所以说消息头的长度如果不正确必会影响到消息体的完整程度,甚至报错;

        2.定长:固定一个长度发送,这样的弊端就是消息体不能超过定长;

        3.标记位:在一个消息的末尾加上自己定位的结束位,例如"hello world()()()()",那么()()()()这个就是结束的标记。(这个最好不要和会出现的字符串冲突。我这里只是简单介绍下,并没有去很好的设计)。

我们这里采取的方法是消息头,消息体的方法,也是效率最高的方法。那么这里就要需要获取mmobufferStr的消息头,也就是长度。到这里又一个坑要出现了。长度不单单是string.len(mmobufferStr)去得到长度,还需要把这个长度转成二进制的形式发送。这里我也搜过很多帖子,也是有位大佬有一套完善的转换方法。转载自http://blog.csdn.net/u013654125/article/details/77184616

--  工具类  
Utils = class("Utils")  
  
-- 下面的二进制=ascii  
  
-- 二进制转int  
function Utils:bufToInt32(num1, num2, num3, num4)  
    local num = 0;  
    num = num + self:leftShift(num1, 24);  
    num = num + self:leftShift(num2, 16);  
    num = num + self:leftShift(num3, 8);  
    num = num + num4;  
    return num;  
end  
  
-- int转二进制  
function Utils:int32ToBufStr(num)  
    local str = "";  
    str = str .. self:numToAscii(self:rightShift(num, 24));  
    str = str .. self:numToAscii(self:rightShift(num, 16));  
    str = str .. self:numToAscii(self:rightShift(num, 8));  
    str = str .. self:numToAscii(num);  
    return str;  
end  
  
-- 二进制转shot  
function Utils:bufToInt16(num1, num2)  
    local num = 0;  
    num = num + self:leftShift(num1, 8);  
    num = num + num2;  
    return num;  
end  
  
-- shot转二进制  
function Utils:int16ToBufStr(num)  
    local str = "";  
    str = str .. self:numToAscii(self:rightShift(num, 7));  
    str = str .. self:numToAscii(num);  
    return str;  
end 

function Utils:int8ToBufStr(num)
	local str = "";  
    str = str .. self:numToAscii(num);  
    return str;
end

-- 二进制转Ascii  
function Utils:bufToInt16(num1, num2)  
    local num = 0;  
    num = num + num2;  
    return num;  
end   

function Utils:leftShift(num, shift)  
    return math.floor(num * (2 ^ shift));  
end  
  
function Utils:rightShift(num, shift)  
    return math.floor(num/(2^shift));  
end  
  
function Utils:numToAscii(num)  
    num = num % 256;  
    return string.char(num);  
end  

接下来我们来组消息头消息体的数据:

local slen = string.len(stringbuffer)
local stringNumByte = Utils:int32ToBufStr(slen)
socket:send(stringNumByte)--发送消息头
socket:send(stringbuffer)--发送消息体

这样send数据就大功告成了。


(3)receive:这里只需要一个调度器去时时接收服务器数据

cc.Director:getInstance():getScheduler():scheduleScriptFunc(function (  )
        local chunk ,status, partial = socket:receive(4)--首先接受4字节的消息头,根据数据头算出消息提的长度
        if chunk~=nil and chunk~="" then
            local num = {}
            for i=1,string.len(chunk) do
                num[i] = string.byte(chunk,i,i) 
            end
            local longBuff = Utils:bufToInt32(num[1],num[2],num[3],num[4])--得出消息体的长度
            local chunk ,status, partial = socket:receive(longBuff)--去接收消息体
            if chunk~=nil and chunk~="" then
                local sendChunk = chunk
                -- print(chunk) 
                local rootResult = protobuf.decode("RootProto.Msg",chunk)
                if rootResult==false then
                    return  
                end
                local data = rootResult.data
                local result = protobuf.decode(rootResult.type,data)
                if result==false then
                    return 
                end 
                print(result.name)
            end
        end
    end, 1/30,false)

注:这里解释下为什么接收数据头是4字节的,首先4字节对于我们传输的数据是完全够用的,其次数据头编码方式采用varint32编码,他这个编码是灵活多变的,最大支持4字节,我们这里直接干掉了varint32判断长度去得到字节数,直接规定4字节。(大家可以随意改,这个就是你和服务器商量来的事情了,怎么做怎么好~)。


如果大家有什么问题还可以留言讨论下,也提前感谢下各路大佬对我的错误的指出,我会不断完善!如果有错误大佬可以在评论指出下哦!


  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值