cocos2dx和skynet通信

最近使用skynet作为服务器开发前端使用cocos2dx-lua。由于前后端都是用lua编写。
在搭建双方的通信环节是有一个步骤就是选择通信的协议。通用选择是protobuf+socket的方式。由于protobuf对lua的支持问题。我们选择了pbc这个proto解析库。

本文解决的问题

前端cocos2dx-lua引擎,使用luasocket+protobuf 和skynet进行通信?

前端使用: luasocket pbc lpack
后端使用: socket pbc string.pack string.unpack

skynet在数据发送时使用的是string.pack() string.unpack() 进行打包解包。然后这两个函数是lua5.3的。前端的cocos是lua5.1没有这个API。
因此才有了lpack这个库。

pbc: https://github.com/cloudwu/pbc.git
lpack: https://github.com/LuaDist/lpack.git
skynet: https://github.com/cloudwu/skynet.git

已经定好协议并且可通信的skynet服务器和cocos2dx客户端:
https://github.com/gameloses/cocos2dx_lua_skynet_client.git
git clone https://github.com/gameloses/skynet_pbc.git

接下来就是进行一个前后端的对接逻辑。

cocos2dx集成pbc和lpack

这是一个已经集成好的cocos2dx的例子,代码开源在github
https://github.com/gameloses/cocos2dx_lua_skynet_client.git
这里例子已经可以可skynet进行通信。 进入这个链接会有u详细的教程。
下面是详细的集成教程。

集成pbc

  1. 编译pbc
    pbc下的src拷贝到编译目录。假设vs。环境下你得建一个pbc目录然后把src下的代码copy到pbc下。编译。
    发现pbc的.c在VS中不能按C代码编译,而应该按照C++编译,在所有.c的属性页中的“C/C++ => 高级”中,设置“编译为C++代码”后编译通过。因为pbc全是c代码索引得设置一下。
  2. 添加luabindings代码
    将pbc目录下binding/lua/pbc-lua.c文件copy到项目中。
    添加头文件:pbc-lua.h。代码如下:
#ifndef __PBC_LUA_H__
#define __PBC_LUA_H__

#ifdef __cplusplus
extern "C" {
#endif
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

#ifdef __cplusplus
}
#endif

#ifdef __cplusplus
extern "C" {
#endif
    int luaopen_protobuf_c(lua_State *L);
#ifdef __cplusplus
}
#endif

#endif

编写桥接代码添加到工程:lua_cocos2dx_pbc_manual.h,lua_cocos2dx_pbc_manual.cpp

//lua_cocos2dx_pbc_manual.h
#ifndef lua_cocos2dx_pbc_manual_h__
#define lua_cocos2dx_pbc_manual_h__

#ifdef __cplusplus
extern "C" {
#endif
#include "tolua++.h"
#ifdef __cplusplus
}
#endif

TOLUA_API int  register_pbc_module(lua_State* L);

#endif
//lua_cocos2dx_pbc_manual.cpp
#include "lua_bindings/lua_cocos2dx_pbc_manual.h"

#include "platform/CCPlatformConfig.h"
#include "base/ccConfig.h"
#include "scripting/lua-bindings/manual/tolua_fix.h"
#include "scripting/lua-bindings/manual/LuaBasicConversions.h"
#include "scripting/lua-bindings/manual/CCLuaEngine.h"

#include "lua_bindings/pbc-lua.h"

#include "cocos/platform/CCFileUtils.h"

int read_protobuf_file(lua_State *L){
    const char *buff = luaL_checkstring(L, -1);
    Data data = cocos2d::FileUtils::getInstance()->getDataFromFile(buff);
    lua_pushlstring(L, (const char*)data.getBytes(), data.getSize());
    return 1;
}

TOLUA_API int register_pbc_module(lua_State* L)
{
    lua_getglobal(L, "_G");
    if (lua_istable(L, -1))//stack:...,_G,
    {
        lua_register(L, "read_protobuf_file_c", read_protobuf_file);
        luaopen_protobuf_c(L);
    }
    lua_pop(L, 1);
    return 1;
}
  1. AppDelegate中添加一下代码
#include "lua_bindings/lua_cocos2dx_pbc_manual.h"   
register_pbc_module(L);
  1. 编译一下就ok。
    android和mac平台比较简单就不说了。
  2. 将binding/lua 下的 protobuf.lua parser.lua 拷贝到lua代码目录下。
  3. 测试
    添加协议
    pbhead.proto
package PbHead;
message MsgHead{
    required int32 msgtype = 1;
    required string msgname = 2; 
    required int32 msgret = 3; 
}

协议生成

protoc --descriptor_set_out  proto/pbhead.pb proto/pbhead.proto

解析协议

require "protobuf"
local buffer = io.read("proto/pbhead.pb") --读出文件内容
protobuf.register(buffer)

编码解码

local msg_head={msgtype = 1, msgname = msg_name, msgret = 0};
local pb_head = protobuf.encode("PbHead.MsgHead", msg_head)
local data = protobuf.decode("PbHead.MsgHead", pb_head);

集成lpack

  1. 下载lpack.添加lpack.c到项目中。
  2. 添加一个lpack.h
#ifndef __LAPCK_H__
#define __LAPCK_H__

#ifdef __cplusplus
extern "C" {
#endif
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

#ifdef __cplusplus
}
#endif

#ifdef __cplusplus
extern "C" {
#endif
    int luaopen_pack(lua_State *L);
#ifdef __cplusplus
}
#endif

#endif

3.在Appdelegate中添加代码

#include "lpack/lpack.h"
luaopen_pack(L);
  1. 测试
    lpack是注册在string类里面的。
    string.pack
    string.unpack
    pack(f,...)
    unpack(s,f,[init])
    注意unpack多返回了一个长度信息。(坑)

前后端消息格式定义

protocol buffer数据包结构如下:

1 2 3 4 5
2字节 2字节 PBMsgHead PBMsgBody 't'

开头2字节存放一个short数据,表示整个数据包的长度。即:
数据包总长度 = 2 byte + 2 byte + PBMsgHead字节数 + PBMsgBody字节数 + 1 byte

随后的2字节存放一个short数据,表示PBMsgHead数据的长度。
PBMsgBody数据长度 = 数据包总长度 - 2 - 2 - 1 - PBMsgHead数据长度。

需要注意的是 PBMsgBody有可能为空,即没有这个数据。因为错误码放在PBMsgHead里面,当逻辑出错时候,只有错误码,没有PBMsgBody。

luasocket进行数据收发

local LuaSock = class("LuaSock")

function LuaSock:connect()
    local socket = require('luasocket.socket'); 
    self.m_sock = socket.tcp(); 
    self.m_sock:settimeout(0);  --非阻塞
    self.m_sock:setoption("tcp-nodelay", true) --去掉优化 不用处理粘包
    self.m_sock:connect(self.m_ip, self.m_port); 
    
    --定时检测是否可写以判断socket的状态
    self.check_ = schedule(function()
        if self:connect_is_success() then
            unschedule(self.check_)
            
            
        end
    end)
end

function LuaSock:connect_is_success( ... )
    local for_write = {};
    table.insert(for_write,self.m_sock);
    local ready_forwrite;
    _,ready_forwrite,_ = socket.select(nil,for_write,0);
    if #ready_forwrite > 0 then
        return true;
    end
    return false;
end


function LuaSock:receive() 
    local recvt, sendt, status = socket.select({self.m_sock}, nil, 1)
    print("input", #recvt, sendt, status)
    if #recvt <= 0 then
        return;
    end

    local buffer,err = self.m_sock:receive(2);
    if buffer then 
        --读取二进制数据流
        local first, sencond = string.byte(buffer,1,2);
        local len=first*256+sencond;--通过位计算长度
        print("收到数据长度=",len)
        local buffer,err = self.m_sock:receive(len); 
        
        --unpack 使用pbc decode
        local  pb_len, pb_head,pb_body,t = string.unpack(buffer, ">PPb"); 
        local msg_head = protobuf.decode("PbHead.MsgHead", pb_head) 
        local msg_body = protobuf.decode(msg_head.msgname, pb_body)
        print("t:"..t..":"..string.char(t))
    
    end
end

function LuaSock:send()  
    --拼装头
    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'));

    --数据发送
    local _len ,_error = self.m_sock:send(data);
    if _len ~= nil and _len == #data then 
        --表示发送成功 
    end

end

至此客户端的工作是完成了。我放在了github上。

skynet 使用pbc

skynet使用pbc解析protobuf,使用socket和客户端通信。
前提是搭建编译skynet的环境。
下载pbc编译make得到protobuf.so
将pbc/binding/lua53/protobuf.lua复制到根目录的lualib目录
protobuf.so文件复制到根目录的luaclib目录。pbc就算是集成好了。
具体的步骤看下面。

服务器编译步骤

clone完整的代码 代码中引用了skynet和pbc

git clone https://github.com/gameloses/skynet_pbc.git

cd skynet_pbc
git submodule init
git submodule update

cd skynet
git submodule init
git submodule update

编译skynet

cd skynet
make linux

编译pbc

cd ../3rd/pbc
make

cd binding
cd lua53
make

如果提示找不到lua.h则需要安装一下lua5.3. make && make install
(或者修改一下Makefile文件,设置lua.h的路径)

将protobuf.lua复制到根目录的lualib目录
protobuf.so文件复制到根目录的luaclib目录

编译proto文件

回到根目录
make

如果没有安装protobuf的话会报错,因为要使用到protoc
yum install protobuf-devel.x86_64
yum install protobuf.x86_64

运行

. run.sh
具体教程见wiki

使用cocos2dx 测试一下连接。

clinet的地址: https://github.com/gameloses/cocos2dx_lua_skynet_client.git

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容