0801 - luasocket 在 cocos2d-lua 的应用

基于 SocketTCP

pb + luasocket + 收发缓存

原版的 quick 里面带了一个 socketTCP 的 luasocket 封装,但是并没有处理网络粘包和少包等情况。我们在这个基础上扩展一个带收发缓存数据,数据格式是 pb 工具。

local scheduler = require ("cocos.framework.scheduler")
local tcp = require 'cocos.framework.net.SocketTCP'

local headerSize = 2

local NetMgr = class("NetMgr", tcp)

function NetMgr:ctor()
    NetMgr.super.ctor(self)

    self.recvBuffer = ''
    self.process_id = nil
    self.send_task_list = {}

    self:on(tcp.EVENT_CLOSE, handler(self, self.stop))
    self:on(tcp.EVENT_CLOSED, handler(self, self.stop))
    self:on(tcp.EVENT_CONNECTED, function (  )
        print('connected')
        self.process_id = scheduler.scheduleGlobal(function ( dt )
            self:processSocketIO()
        end, 0.1)
    end)
    self:on(tcp.EVENT_DATA, function ( data )
        self.recvBuffer = self.recvBuffer .. data.data
    end)
end

function NetMgr:getInstance()
    if not self.instance_ then
        self.instance_ = NetMgr:new()
    end
    return self.instance_
end

function NetMgr:stop()
    print('> socket close')
    if self.process_id then
        scheduler:unscheduleScriptEntry(self.process_id)
    end
end

function NetMgr:processSocketIO()
    if not self.isConnected then
        return
    end
    self:processInput()
    self:processOutput()
end

function NetMgr:processInput()
    while true do
        local len = string.len(self.recvBuffer)

        if len == 0 then
            return
        end

        if len < headerSize then
            print('header not full')
            return
        end

        --计算包的长度
        local first, sencond = string.byte(self.recvBuffer, 1, 2)
        local bodySize = first * 256 + sencond --通过位计算长度
        print("收到数据长度 = ",bodySize)

        if string.len(self.recvBuffer) < headerSize + bodySize then
            print('body not full')
            return
        end

        --解析包
        local data = string.sub(self.recvBuffer, 1, headerSize + bodySize)
        local ret, len, pb_head, pb_body, char_data = string.unpack(data, ">HPPb")
        local msg_head = protobuf.decode("PbHead.MsgHead", pb_head)
        local msg_body = protobuf.decode(msg_head.msgname, pb_body)
        print("收到服务器数据", msg_head.msgname)

        dump(msg_head)
        dump(msg_body)

        self.recvBuffer = string.sub(self.recvBuffer, headerSize + bodySize + 1) -- 更新缓存
        self:dispatchEvent({name = msg_head.msgname, data = msg_body}) -- 把事件和数据抛出去
    end
end

function NetMgr:processOutput()
    if self.send_task_list  and #self.send_task_list > 0 then
        local data = self.send_task_list[#self.send_task_list]
        if data then
            local _len ,_error = self:send(data)
            -- print("socket send"..#data, "_len:", _len, "error:", _error)
            --发送长度不为空,并且发送长度==数据长度
            if _len ~= nil and _len == #data then
                table.remove(self.send_task_list, #self.send_task_list)
            else

            end
        end
    end
end

function NetMgr:sendPB(msg_name, msg_body)
    --拼装头
    local msg_head={msgtype = 1, msgname = msg_name, msgret = 0}
    local pb_head = protobuf.encode("PbHead.MsgHead", msg_head)
    local pb_body = protobuf.encode(msg_name, msg_body)
    --计算长度
    local pb_head_len = #pb_head
    local pb_body_len = #pb_body
    local pb_len = 2 + pb_head_len + 2 + pb_body_len + 1

    local data = string.pack(">HPPb",pb_len, pb_head, pb_body, string.byte('t'))
    print("GameNet send msg:"..msg_name..":"..string.char(string.byte('t')))

    table.insert(self.send_task_list, 1, data)
end

return NetMgr

测试

luvit 的一个 tcp-echo 服务器

--[[
-- luvit file.lua
--]]
local net = require('net')

local server = net.createServer(function(client)
  print("Client connected")

  -- Add some listenners for incoming connection
  client:on("error",function(err)
    print("Client read error: " .. err)
    client:close()
  end)

  client:on("data",function(data)
    print(data)
    client:write(data)
    client:write(data)
  end)

  client:on("end",function()
    print("Client disconnected")
  end)
end)

-- Add error listenner for server
server:on('error',function(err)
  if err then error(err) end
end)

server:listen(1234)

打包变量类型定义

    #define OP_ZSTRING  'z'     //空字符串
    #define OP_BSTRING  'p'     //长度小于2^8的字符串
    #define OP_WSTRING  'P'     //长度小于2^16的字符串
    #define OP_SSTRING  'a'     //长度小于2^32/64的字符串*/
    #define OP_STRING   'A'     //指定长度字符串
    #define OP_FLOAT    'f'     /* float */
    #define OP_DOUBLE   'd'     /* double */
    #define OP_NUMBER   'n'     /* Lua number */
    #define OP_CHAR     'c'     /* char */
    #define OP_BYTE     'b'     /* byte = unsigned char */
    #define OP_SHORT    'h'     /* short */
    #define OP_USHORT   'H'     /* unsigned short */
    #define OP_INT      'i'     /* int */
    #define OP_UINT     'I'     /* unsigned int */
    #define OP_LONG     'l'     /* long */
    #define OP_ULONG    'L'     /* unsigned long */

打包字节序,分为以下3种

#define OP_LITTLEENDIAN '<' /* little endian */
#define OP_BIGENDIAN '>' /* big endian */ 
#define OP_NATIVE '=' /* native endian */

参考链接

  1. http://blog.leanote.com/post/qq-games/cocos2dx
  2. https://github.com/gameloses/cocos2dx_lua_skynet_client

你可能感兴趣的:(0801 - luasocket 在 cocos2d-lua 的应用)