skynet 使用protobuf
一、安装protobuf
下面的操作方法都是在 centos 环境下操作
#下载 Protocol Buffers 源代码:
#您可以从 Protocol Buffers 的 GitHub 仓库中获取特定版本的源代码。使用以下命令克隆仓库
git clone -b v3.20.3 https://github.com/protocolbuffers/protobuf.git#编译和安装:
#进入克隆的目录,然后编译和安装 Protocol Buffers:
cd protobuf
./autogen.sh
./configure
make
sudo make install#验证安装:
protoc --version#您应该看到输出,指示安装的版本为 3.20.3。
二、安装pdb库
#第三方库安装在3rd目录下
cd skynet/3rd/
git clone https://github.com/cloudwu/pbc.gitcd pbc
make#编译成功后,打开skynet/3rd/pbc/binding/lua53/Makefile文件,修改里面的lua路径
CC = gccCFLAGS = -O2 -fPIC -WallLUADIR = ../../../lua #这个路劲就是skynet/3rd/luaTARGET = protobuf.so.PHONY : all cleanall : $(TARGET)$(TARGET) : pbc-lua53.c$(CC) $(CFLAGS) -shared -o $@ -I../.. -I$(LUADIR) -L../../build $^ -lpbcclean :rm -f $(TARGET)#去到lua53目录,编译生成protobuf.so库
cd ./binding/lua53
sudo make
三、将依赖文件放到工程目录下
将 protobuf.so 和 protobuf.lua 分别放入 luaclib 、lualib
cp protobuf.so ../../../../luaclib/ #将protobuf.so复制到存放C模块的lualib目录中
cp protobuf.lua ../../../../lualib/ #将protobuf.lua复制到存放Lua模块的lualib目录中
四、创建protobuf目录
在skynet目录下创建protobuf目录,用来存放原始 .proto 描述文件
这里举例 login.proto
mkdir protobuflogin.proto的内容如下代码所示:
syntax = "proto3";
package Login;message login {string account=1;string passwd=2;int32 result=3;
}
五、编译proto文件
protoc --descriptor_set_out login.pb login.proto
这里写了个gen_pb.sh脚本,将protobuf目录下的所有.sproto文件转成proto文件
#!/bin/bash # 指定要遍历的目录,这里使用当前目录"."
dir="." # 使用find命令查找所有.proto文件,并调用basename和cut命令来截取文件名
find "$dir" -type f -name "*.proto" | while read -r filepath; do # 使用basename命令获取文件名部分,然后使用cut命令去除后缀 filename=$(basename "$filepath") filename_without_extension="${filename%.*}" # 输出截取后的文件名 echo "$filename, $filename_without_extension" # 在这里可以添加其他操作,比如使用protoc编译等 protoc --descriptor_set_out "$filename_without_extension.pb" "$filename"
done
六、了解protobuf.lua 关键函数
1、 protobuf.register
- 功能:注册 Protobuf
- 参数:buffer .pb文件读取出来的二进制字符串
- 返回值:无。
function M.register(buffer)c._env_register(P, buffer)
end
备注: register需要自己去加载.pb文件内容,下面的register_file函数使用会更多
2、protobuf.register_file
- 功能:注册 Protobuf
- 参数:filename 为 .pb文件名。
- 返回值:无。
function M.register_file(filename)local f = assert(io.open(filename , "rb"))local buffer = f:read "*a"c._env_register(P, buffer)f:close()
end
3、protobof.encode
- 功能:将一个 Lua 表编码为 Protobuf 格式的二进制消息。
- 参数:
message是注册的 Protobuf 定义的名称,msg是要编码的 Lua 表。 - 返回值:编码后的二进制数据。
function M.encode( message, t , func , ...)local encoder = c._wmessage_new(P, message)assert(encoder , message)encode_message(encoder, message, t)if func thenlocal buffer, len = c._wmessage_buffer(encoder)local ret = func(buffer, len, ...)c._wmessage_delete(encoder)return retelse local s = c._wmessage_buffer_string(encoder)c._wmessage_delete(encoder)return send
end
4、protobof.decode
- 功能:将一个 Protobuf 编码的二进制消息解码为 Lua 表。
- 参数:
typename是注册的 Protobuf 定义的名称,buf是包含 Protobuf 编码消息的二进制数据。 - 返回值:解码后的 Lua 表。
function M.decode(typename, buffer, length)local ret = {}local ok = c._decode(P, decode_message_cb , ret , typename, buffer, length)if ok thenreturn setmetatable(ret , default_table(typename))elsereturn false , c._last_error(P)end
end
七、protobuf测试用例
在examples 目录下新建 test_protobuf.lua
package.path = package.path .. ";./lualib/?.lua"
package.cpath = package.cpath .. ";./luaclib/?.so"local protobuf = require "protobuf" --引入文件protobuf.lua
protobuf.register_file "./protobuf/common.pb" --注册pb文件
protobuf.register_file "./protobuf/login.pb" --注册pb文件local loginInfo = { account = "test", passwd = "pw"} local encodeData = protobuf.encode("Login.login", loginInfo)
print("encodeData:", encodeData)local decodeData = protobuf.decode("Login.login", encodeData)
print("decodeData account:", decodeData.account)
print("decodeData passwd:", decodeData.passwd)
八、skynet 使用protobuf进行网络通信
1、 将数据打包成二进制数据
--[[big endianhead 2 byte body size2 byte protonamesizen byte protonamebody n byte data@desc: 将lua格式的协议序列化为二进制数据
]]
function protobufDataHelper.encode( name,data )local stringbuffer = protobuf.encode(name, data) -- protobuf序列化 返回lua stringlocal body = string.pack(">s2s",name,stringbuffer) -- 打包包体 协议名 + 协议数据local head = string.pack(">I2",#body) -- 打包包体长度print("encode proto_name:", name, ",data_size:", #body, ",totalSize:", #head+#body)return head .. body -- 包体长度 + 协议名 + 协议数据
end
2、将二进制数据解包
--[[@desc: 将二进制数据反序列化为lua string--@msg: C Point @return:协议名字,协议数据
]]
function protobufDataHelper.decode( msg )--- 前两个字节在netpack.filter 已经解析print("msg size:", #msg)local proto_name,stringbuffer = string.unpack(">s2s",msg)print("proto_name", proto_name, "data:", stringbuffer)local body = protobuf.decode(proto_name, stringbuffer)return proto_name,body
end
3、skyent unpack类型指定为二进制字符串
这里在agent.lua 注册消息协议类型时处理
skynet.register_protocol {name = "client",id = skynet.PTYPE_CLIENT, unpack = skynet.tostring, --- 将C point 转换为lua 二进制字符串
}
在dispatch_message消息地方反序列化消息
--- 分发消息
local function dispatch_message(msg)--- 反序列化二进制string数据local pack_name,data = dataHelper.decode(msg) -- pack_name = c2s.testlocal sub_name = pack_name:match(".+%.(%w+)$") -- sub_name = test......local f = REQUEST[sub_name]if f == nil thenprint("not function define handle package:", pack_name)returnendf(data)
end
agent.lua 改造测试代码如下
local skynet = require "skynet"
local socket = require "skynet.socket"
local sproto = require "sproto"
local sprotoloader = require "sprotoloader"
local login = require "login"
local tableutil = require "tableutil"local dataHelper = require "protobufDataHelper"local WATCHDOG
local host
local send_requestlocal CMD = {}
local REQUEST = {}
local client_fdfunction REQUEST:get()print("get", self.what)local r = skynet.call("SIMPLEDB", "lua", "get", self.what)return { result = r }
endfunction REQUEST:set()print("set", self.what, self.value)local r = skynet.call("SIMPLEDB", "lua", "set", self.what, self.value)
endfunction REQUEST:handshake()return { msg = "Welcome to skynet, I will send heartbeat every 5 sec." }
endfunction REQUEST:quit()skynet.call(WATCHDOG, "lua", "close", client_fd)
endlocal function send_data(name, args)local data = dataHelper.encode(name, args)-- 发送数据socket.write(client_fd,data)
endfunction REQUEST:login()print("login account,passwd:", self.account, self.passwd)local result = login.loginRequest(self.account, self.passwd)if result ~= 0 thenprint("kill client, client_fd:", client_fd)REQUEST:quit()endlocal loginInfo = { account = "kk", passwd = "haha"}send_data("Login.login", loginInfo)return {result = result}
endfunction REQUEST:loginTest()print("loginTest:", tableutil.tPrint(self))
endlocal function request(name, args, response)local f = assert(REQUEST[name])print("recieve request, protoName:", name, tableutil.tPrint(args))local r = f(args)if response thenreturn response(r)end
endlocal function send_package(pack)local package = string.pack(">s2", pack)socket.write(client_fd, package)
end--[[
skynet.register_protocol {name = "client",id = skynet.PTYPE_CLIENT,unpack = function (msg, sz)return host:dispatch(msg, sz)end,dispatch = function (fd, _, type, ...)assert(fd == client_fd) -- You can use fd to reply messageskynet.ignoreret() -- session is fd, don't call skynet.retskynet.trace()if type == "REQUEST" thenlocal ok, result = pcall(request, ...)if ok thenif result thensend_package(result)endelseskynet.error(result)endelseassert(type == "RESPONSE")error "This example doesn't support request client"endend
}--]]skynet.register_protocol {name = "client",id = skynet.PTYPE_CLIENT, unpack = skynet.tostring, --- 将C point 转换为lua 二进制字符串
}function CMD.start(conf)local fd = conf.clientlocal gate = conf.gateWATCHDOG = conf.watchdog-- slot 1,2 set at main.luahost = sprotoloader.load(1):host "package"send_request = host:attach(sprotoloader.load(2))skynet.fork(function()local index = 1while true do--send_package(send_request "heartbeat")index = index + 1local loginInfo = { account = "kk"..index, passwd = "haha"}send_data("Login.login", loginInfo)skynet.sleep(500)endend)client_fd = fdskynet.call(gate, "lua", "forward", fd)
endfunction CMD.disconnect()-- todo: do something before exitskynet.exit()
end--- 分发消息
local function dispatch_message(msg)--- 反序列化二进制string数据local pack_name,data = dataHelper.decode(msg) -- pack_name = c2s.testlocal sub_name = pack_name:match(".+%.(%w+)$") -- sub_name = testprint("recieve request, protoName:", pack_name, tableutil.tPrint(data))local f = REQUEST[sub_name]if f == nil thenprint("not function define handle package:", pack_name)returnendf(data)
endskynet.start(function()skynet.dispatch("lua", function(_,_, command, ...)skynet.trace()local f = CMD[command]skynet.ret(skynet.pack(f(...)))end)skynet.dispatch("client", function (session, address, msg)dispatch_message(msg)end)
end)
4、客户端测试代码
client_protobuf.lua
package.cpath = "luaclib/?.so;skynet/luaclib/?.so"
package.path = "lualib/common/?.lua;lualib/?.lua;skynet/lualib/?.lua;skynet/examples/?.lua"local tableutil = require "tableutil"if _VERSION ~= "Lua 5.4" thenerror "Use lua 5.4"
endlocal socket = require "client.socket"
local dataHelper = require "protobufDataHelper"local fd = assert(socket.connect("127.0.0.1", 8888))local function send_data(name, args)local data = dataHelper.encode(name, args)-- 发送数据socket.send(fd,data)
endlocal loginInfo = { account = "test", passwd = "haha"}
send_data("Login.login", loginInfo)
send_data("Common.heartbeat", {})local function unpack_package(text)local size = #textif size < 2 thenreturn nil, textendlocal s = text:byte(1) * 256 + text:byte(2)if size < s+2 thenreturn nil, textendreturn text:sub(3,2+s), text:sub(3+s)
endlocal function recv_package(last)local resultresult, last = unpack_package(last)if result thenreturn result, lastendlocal r = socket.recv(fd)if not r thenreturn nil, lastendif r == "" thenerror "Server closed"endreturn unpack_package(last .. r)
endlocal last = ""
local function dispatch_package()while true dolocal vv, last = recv_package(last)if not v thenbreakendlocal packName, data = dataHelper.decode(v)print("packName:", packName)print("data:", tableutil.tPrint(data) )end
endwhile true dodispatch_package()socket.usleep(1000000)
end
相关文章:
skynet 使用protobuf
一、安装protobuf 下面的操作方法都是在 centos 环境下操作 #下载 Protocol Buffers 源代码: #您可以从 Protocol Buffers 的 GitHub 仓库中获取特定版本的源代码。使用以下命令克隆仓库 git clone -b v3.20.3 https://github.com/protocolbuffers/protobuf.git#编译…...
Vue Router 4 与 Router 3 路由配置与区别
文章目录 路由安装路由配置vue-router 3.x版本写法配置路由使用路由 vue-router 4.x版本写法配置路由使用路由 Vue Router 4 与 Vue Router 3 区别 路由安装 Vue 2 (使用 Vue Router 3) :npm install vue-router3 Vue 3 (使用 Vue Router 4) :npm insta…...
python借助elasticsearch实现标签匹配计数
给定一组标签 [{“tag_id”: “1”, “value”: “西瓜”}, {“tag_id”: “1”, “value”: “苹果”}],我想精准匹配到现有的标签库中存在的标签并记录匹配成功的数量。 标签id(tag_id)标签名(tag_name)标签值(tag_name )1水果西瓜1水果苹果1水果橙子2动物老虎 …...
Yolo-world+Python-OpenCV之摄像头视频实时目标检测
上一次介绍了如何使用最基本的 Yolo-word来做检测,现在我们在加opencv来做个实时检测的例子 基本思路 1、读取离线视频流 2、将视频帧给yolo识别 3、根据识别结果 对视频进行绘制边框、加文字之类的 完整代码如下: import datetimefrom ultralytics …...
vue-treeselect 的基本使用
vue-treeselect 的基本使用 1. 效果展示2. 安装 插件3. 引入组件4. 代码 1. 效果展示 2. 安装 插件 vue-treeselect是一个树形的下拉菜单,至于到底有多少节点那就要看你的数据源有多少层了,挺方便的。下面这个这个不用多说吧,下载依赖 npm in…...
Vue(二)
文章目录 1.条件渲染1.关于js中的false的判定2.基本介绍3.v-if1.需求分析2.代码实例 4.v-show实现5.v-if与v-show比较6.课后练习 2.列表渲染1.代码实例2.课后练习 3.组件化编程1.基本介绍2.实现方式一_普通方式2.实现方式二_全局组件方式3.实现方式三_局部组件方式 4.生命周期和…...
Python基于深度学习的车辆特征分析系统
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…...
推理还原的干货
故事的递进还原 从下层故事到上层故事 设定还原 还原的逻辑 隐藏信息拼凑、因果导致果推因、规则还原现象 设计思路: 真解答 真解答的关键信息 推理逻辑链 哪些环节可以被误导 如何把关键信息变成伪解答 解释变形信息 给出识别变形信息的方法或线索 其实看似一个…...
【Redis 神秘大陆】006 灾备方案
六、Redis 灾备方案 6.1 存储方案 6.1.1 基础对比 RDB持久化AOF持久化原理周期性fork子进程生成持久化文件每次写入记录命令日志文件类型二进制dump快照文件文本appendonly日志文件触发条件默认超过300s间隔且有1s内超过1kb数据变更永久性每秒fsync一次文件位置配置文件中指…...
【Java基础】17.异常处理
文章目录 前言一、异常的概念1.异常的3种类型2.支持异常处理的关键字和类 二、Exception 类的层次三、内置异常类1.非检查性异常2.检查性异常类 四、异常处理1.捕获异常2.多重捕获块3.throws/throw 关键字1.throw 关键字2.throws 关键字 3.finally关键字 五、编译时异常处理方式…...
【python】flask结合SQLAlchemy,在视图函数中实现对数据库的增删改查
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...
APIGateway的认证
APIGateway的支持的认证如下: 我们从表格中可以看到,HTTP API 不支持资源策略的功能,另外是通过JWT的方式集成Cognito的。 对于REST API则是没有显示说明支持JWT认证,这个我们可以通过Lambda 自定义的方式来实现。 所以按照这个…...
MacOS Github Push项目 精简版步骤
大白菜教程:小白菜 macOS github提交代码-CSDN博客 步骤1:git init步骤2: touch .gitignore 创建ignore文件 open .gitignore 打开ignore文件 编写ignore文件.idea/ 是文件夹的意思.git/ 也是自动生成的文件夹 也不上传.DS_St…...
Eclipse的基本使用讲解(建项目,建包,建类,写代码(基本语法))新手入门必备
目录 一.介绍eclipse 二.操作Eclipse 1.选择工作空间 2.建项目,建包,建类 1.建项目(两种) 2.建包 3.建类 三.写代码(基本语法) 1.代码操作 2.代码规范 3.代码注释 一.介绍eclipse Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其…...
3D模型处理的并行化
今天我们将讨论如何使用 Python 多进程来处理大量3D数据。 我将讲述一些可能在手册中找到的一般信息,并分享我发现的一些小技巧,例如将 tqdm 与多处理 imap 结合使用以及并行处理存档。 那么我们为什么要诉诸并行计算呢? 使用数据有时会出现…...
盲人安全导航技巧:科技赋能让出行更自如
作为一名资深记者,长期关注并报道无障碍领域的发展动态。今日,我将聚焦盲人安全导航技巧,探讨这一主题下科技如何赋能视障人士实现更为安全、独立的出行。一款融合了实时避障、拍照识别物体及场景功能的盲人出行辅助应用叫做蝙蝠避障…...
问,由于java存在性能上,以及部分功能上的缺点,请问如何正确使用C,C++,Go,这三个语言,提升Java Web项目的性能?
拓展阅读:版本任你发,我用java8 我明白Java虽然在许多方面表现出色,但在某些特定场景下可能会遇到性能瓶颈或功能限制。为了提升Java Web项目的性能,可以考虑将C、C和Go这三种语言用于特定的组件或服务。以下是如何正确使用这些语…...
【信号与系统 - 9】傅里叶变换的性质习题
1 习题 已知 f ( t ) f(t) f(t) 的傅里叶变换为 F ( j w ) F(jw) F(jw) ,求如下信号的傅里叶变换 (1) t ⋅ f ( 3 t ) t\cdot f(3t) t⋅f(3t) 解: f ( 3 t ) ↔ 1 3 F ( j w 3 ) f(3t)\leftrightarrow \frac{1}{3}F(j\frac{w}…...
C#探索之路基础夯实篇(5):语法糖概念解析
C#探索之路基础夯实篇(5):语法糖概念解析 文章目录 C#探索之路基础夯实篇(5):语法糖概念解析1、概念定义2、Lua中的语法糖3、C#中的语法糖4、C中的语法糖5、优缺点辨析6、适用范围7、总结 从之前一开始接触lua的时候开始,开始第一次接触到语法…...
SeaTunnel 与 DataX 、Sqoop、Flume、Flink CDC 对比
产品概述 Apache SeaTunnel 是一个非常易用的超高性能分布式数据集成产品,支持海量数据的离线及实时同步。每天可稳定高效同步万亿级数据,已应用于数百家企业生产,也是首个由国人主导贡献到 Apache 基金会的数据集成顶级项目。 SeaTunnel 主要解决数据集成领域的常见问题:…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
GAN模式奔溃的探讨论文综述(一)
简介 简介:今天带来一篇关于GAN的,对于模式奔溃的一个探讨的一个问题,帮助大家更好的解决训练中遇到的一个难题。 论文题目:An in-depth review and analysis of mode collapse in GAN 期刊:Machine Learning 链接:...
Spring事务传播机制有哪些?
导语: Spring事务传播机制是后端面试中的必考知识点,特别容易出现在“项目细节挖掘”阶段。面试官通过它来判断你是否真正理解事务控制的本质与异常传播机制。本文将从实战与源码角度出发,全面剖析Spring事务传播机制,帮助你答得有…...
篇章一 论坛系统——前置知识
目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...
scan_mode设计原则
scan_mode设计原则 在进行mtp controller设计时,基本功能设计完成后,需要设计scan_mode设计。 1、在进行scan_mode设计时,需要保证mtp处于standby模式,不会有擦写、编程动作。 2、只需要固定mtp datasheet说明的接口即可…...
