基于Lua脚本的zeromq应用开发

    在上一篇博客文章中笔者记录了使用openwrt编译一个运行于嵌入式ARM/Linux的“最小Lua开发环境”的过程,当时的制作的工具包没有上传,通过该文笔者尝试对上一篇博客文章进行补充,并将工具包上传;同时分享一些笔者基于Lua脚本进行ZeroMQ的开发的学习方法。虽然笔者此时仅对zeromq有浅薄的了解,但希望以后能够较深入地理解并应用该强大的线程间、进程间、远程通信的网络库。对于嵌入式应用软件的开发,可以说ZeroMQ可以较大程度上替代传统的网络编程方法,并有Lua/Python/NODEJS等等脚本语言的编程接口,从而增加软件的复用,加速应用开发。

    除此之外,笔者还在GNU/Linux环境下编译了zeromq及其Lua的模块,这样就可以编写Lua脚本进行zeromq开发,让嵌入式设备与主机通信。下图是笔者在设备和主机上使用Lua解析器加载zeromq模块的结果:

    笔者使用了一个简单的td.dump来导出zeromq的Lua主模块导出的变量及函数库,zmq表中共有194个元素:

    下图是在笔者的旧手机上的运行结果:

    上图与在主机上的结果没有什么差异。

    为了测试zeromq的功能,笔者编写了下面的reply-server.lua,运行于主机上用作REP服务器:

#!/usr/bin/env lua

-- Created by xiaoqzye@qq.com
-- Simple ZeroMQ reply Server
-- 2020/08/09

-- load Lua modules
local zmq = require 'lzmq'
local czmq = require 'czmq'

-- global variables
local mcnt = 0
local zctx, zskt = nil, nil
local rok, errMsg = nil, nil

-- register signal handler
if not czmq.signal(czmq) then os.exit(1) end

-- create ZeroMQ context
rok, errMsg = zmq.context()
if not rok then
	print(czmq.errMsg(errMsg))
	czmq.exit(zctx, zskt, 2)
end
zctx = rok
-- set the maximum number of sockets
rok, errMsg = zctx:set(zmq.MAX_SOCKETS, 3)
if not rok then
	print(czmq.errMsg(errMsg))
	czmq.exit(zctx, zskt, 3)
end

-- create ZeroMQ socket
rok, errMsg = zctx:socket(zmq.REP)
if not rok then
	print(czmq.errMsg(errMsg))
	czmq.exit(zctx, zskt, 4)
end
zskt = rok
-- set receive/send high water marks
rok, errMsg = zskt:set_rcvhwm(32)
if rok then rok, errMsg = zskt:set_sndhwm(32) end
if not rok then
	print(czmq.errMsg(errMsg))
	czmq.exit(zctx, zskt, 5)
end

-- bind socket to a specific address
rok, errMsg = zskt:bind("tcp://*:4321")
if not rok then
	print(czmq.errMsg(errMsg))
	czmq.exit(zctx, zskt, 6)
end

while not czmq.signaled(czmq) do
	rok, errMsg = zskt:recv()
	if not rok then
		if not czmq.signaled(czmq) then print(czmq.errMsg(errMsg)) end
		break
	end
	print(string.format("Received message [%08x]: %s", mcnt, rok))
	local smsg = string.format("Hello there! [%08x]", mcnt)
	rok, errMsg = zskt:send(smsg)
	if not rok then
		print(czmq.errMsg(errMsg))
		break
	end
	mcnt = mcnt + 1
end
czmq.exit(zctx, zskt, 0)

    此外也编写了request-client.lua,复制到手机上用作客户端:

#!/usr/bin/env lua

-- Created by xiaoqzye@qq.com
-- Simple ZeroMQ request client
-- 2020/08/09

-- load Lua modules
local zmq = require 'lzmq'
local czmq = require 'czmq'
local posix = require 'posix'

-- global variables
local mcnt, cid = 0, nil
local zctx, zskt = nil, nil
local rok, errMsg = nil, nil

-- register signal handler
if not czmq.signal(czmq) then os.exit(1) end

-- construct client ID
rok, errMsg = posix.getpid()
if not rok then
	print(czmq.errMsg(errMsg))
	czmq.exit(zctx, zskt, 1)
end
cid = string.format("REQPID-%d", type(rok) == "table" and rok.pid or rok)

-- create ZeroMQ context
rok, errMsg = zmq.context()
if not rok then
	print(czmq.errMsg(errMsg))
	czmq.exit(zctx, zskt, 2)
end
zctx = rok
-- set the maximum number of sockets
rok, errMsg = zctx:set(zmq.MAX_SOCKETS, 3)
if not rok then
	print(czmq.errMsg(errMsg))
	czmq.exit(zctx, zskt, 3)
end

-- create ZeroMQ socket
rok, errMsg = zctx:socket(zmq.REQ)
if not rok then
	print(czmq.errMsg(errMsg))
	czmq.exit(zctx, zskt, 4)
end
zskt = rok
-- set receive/send high water marks
rok, errMsg = zskt:set_rcvhwm(32)
if rok then rok, errMsg = zskt:set_sndhwm(32) end
if not rok then
	print(czmq.errMsg(errMsg))
	czmq.exit(zctx, zskt, 5)
end

-- connect to server
rok, errMsg = zskt:connect(arg[1] or "tcp://127.0.0.1:4321")
if not rok then
	print(czmq.errMsg(errMsg))
	czmq.exit(zctx, zskt, 6)
end

local function recvMsg(what)
	local rmsg = zskt:recv(zmq.DONTWAIT)
	if not rmsg then return nil end
	while rmsg do
		print(string.format("[%s] received: %s", what, rmsg))
		rmsg = zskt:recv(zmq.DONTWAIT)
	end
	return nil
end

while not czmq.signaled(czmq) do
	local smsg = string.format("Hello from %s, count: %08x", cid, mcnt)
	rok, errMsg = zskt:send(smsg)
	if not rok then
		print(czmq.errMsg(errMsg))
		break
	end
	mcnt = mcnt + 1; recvMsg(cid)
	posix.nanosleep{tv_sec = 1, tv_nsec = 0}
	recvMsg(cid)
end
czmq.exit(zctx, zskt, 0)

    以上两个Lua脚本分别运行在主机和手机上,二者均依赖一个czmq.lua的公共脚本,其内容如下:

#!/usr/bin/env lua

-- Created by xiaoqzye@qq.com
-- Common routines for ZeroMQ
-- 2020/08/09

local czmq = {}
local posix = require 'posix'

-- get default error message
czmq.errMsg = function (errmsg)
	local typn = type(errmsg)
	local unknown = "unknown error"
	if typn ~= "string" or #errmsg == 0 then
		return unknown
	end
	return errmsg
end

czmq.exit = function (zmqCtx, zmqSkt, eval)
	if not eval then eval = 0 end
	if zmqSkt then
		zmqSkt:close()
		zmqSkt = nil
	end
	if zmqCtx then
		zmqCtx:destroy()
		zmqCtx = nil
	end
	os.exit(eval)
end

-- local variable to signal if SIGINT has been delivered
czmq.sigint = false

-- return whether signal handler has been received
czmq.signaled = function (dzmq)
	local typn = type(dzmq)
	if typn ~= "table" then
		print(string.format("Error, invalid argument type: %s", typn))
		return false
	end
	return dzmq.sigint and true or false
end

-- register signal handler
czmq.signal = function (dzmq, signo)
	local typn = type(dzmq)
	if typn ~= "table" then
		io.stderr:write(string.format("Error, invalid argument type: %s", typn))
		return false
	end
	typn = type(dzmq.sigint)
	if typn ~= "boolean" then
		io.stderr:write(string.format("Error, invalid element type: %s", typn))
		return false
	end
	if not signo then signo = posix.SIGINT end
	local rok, errMsg = posix.signal(signo, function (sigNo)
		local typn = type(sigNo)
		if typn ~= "number" then
			print(string.format("Error, invalid signal type: %s", typn))
			return false
		end
		dzmq.sigint = true
		return true
	end)
	if not rok then
		print(czmq.errMsg(errMsg))
		return false
	end
	return true
end

-- return the module
return czmq

    下图是主机上运行reply-server.lua脚本的输出结果:

    注意到上图中每一行的信息对应一个客户端发送的一条信息;而REQPID-XXXX用于区分该行消息是哪一个客户端发送的消息。笔者在手机上依次运行了三个request-client.lua的脚本进程(每开始运行一个脚本之间有几秒到十机秒的间隔),之后三个进程同时运行了一段时间,其运行结果分别如下:

    第一个request-client.lua的部分运行结果:

/data/user # lua 00-request-client.lua 'tcp://192.168.1.104:4321'
[REQPID-5508] received: Hello there! [00000000]
[REQPID-5508] received: Hello there! [00000001]
[REQPID-5508] received: Hello there! [00000002]
[REQPID-5508] received: Hello there! [00000003]
[REQPID-5508] received: Hello there! [00000004]
[REQPID-5508] received: Hello there! [00000005]
[REQPID-5508] received: Hello there! [00000006]
[REQPID-5508] received: Hello there! [00000008]
[REQPID-5508] received: Hello there! [0000000a]
[REQPID-5508] received: Hello there! [0000000c]
[REQPID-5508] received: Hello there! [0000000e]
[REQPID-5508] received: Hello there! [00000010]
[REQPID-5508] received: Hello there! [00000013]
[REQPID-5508] received: Hello there! [00000016]
[REQPID-5508] received: Hello there! [00000019]
[REQPID-5508] received: Hello there! [0000001c]
[REQPID-5508] received: Hello there! [0000001f]
[REQPID-5508] received: Hello there! [00000022]
[REQPID-5508] received: Hello there! [00000025]
[REQPID-5508] received: Hello there! [00000028]
[REQPID-5508] received: Hello there! [0000002b]
[REQPID-5508] received: Hello there! [0000002e]
[REQPID-5508] received: Hello there! [00000031]
[REQPID-5508] received: Hello there! [00000034]
[REQPID-5508] received: Hello there! [00000037]
[REQPID-5508] received: Hello there! [0000003a]
[REQPID-5508] received: Hello there! [0000003d]
[REQPID-5508] received: Hello there! [00000040]

    第二个request-client.lua的部分运行结果:

data/user # lua 00-request-client.lua 'tcp://192.168.1.104:4321'
[REQPID-5511] received: Hello there! [00000007]
[REQPID-5511] received: Hello there! [00000009]
[REQPID-5511] received: Hello there! [0000000b]
[REQPID-5511] received: Hello there! [0000000d]
[REQPID-5511] received: Hello there! [0000000f]
[REQPID-5511] received: Hello there! [00000011]
[REQPID-5511] received: Hello there! [00000014]
[REQPID-5511] received: Hello there! [00000017]
[REQPID-5511] received: Hello there! [0000001b]
[REQPID-5511] received: Hello there! [0000001d]
[REQPID-5511] received: Hello there! [00000020]
[REQPID-5511] received: Hello there! [00000023]
[REQPID-5511] received: Hello there! [00000026]
[REQPID-5511] received: Hello there! [0000002a]
[REQPID-5511] received: Hello there! [0000002d]
[REQPID-5511] received: Hello there! [0000002f]
[REQPID-5511] received: Hello there! [00000032]
[REQPID-5511] received: Hello there! [00000036]
[REQPID-5511] received: Hello there! [00000039]
[REQPID-5511] received: Hello there! [0000003b]
[REQPID-5511] received: Hello there! [0000003e]
[REQPID-5511] received: Hello there! [00000041]
[REQPID-5511] received: Hello there! [00000045]
[REQPID-5511] received: Hello there! [00000048]
[REQPID-5511] received: Hello there! [0000004a]
[REQPID-5511] received: Hello there! [0000004d]
[REQPID-5511] received: Hello there! [00000050]

    第三个request-client.lua的部分运行结果:

/data/user # lua 00-request-client.lua 'tcp://192.168.1.104:4321'
[REQPID-5515] received: Hello there! [00000012]
[REQPID-5515] received: Hello there! [00000015]
[REQPID-5515] received: Hello there! [00000018]
[REQPID-5515] received: Hello there! [0000001a]
[REQPID-5515] received: Hello there! [0000001e]
[REQPID-5515] received: Hello there! [00000021]
[REQPID-5515] received: Hello there! [00000024]
[REQPID-5515] received: Hello there! [00000027]
[REQPID-5515] received: Hello there! [00000029]
[REQPID-5515] received: Hello there! [0000002c]
[REQPID-5515] received: Hello there! [00000030]
[REQPID-5515] received: Hello there! [00000033]
[REQPID-5515] received: Hello there! [00000035]
[REQPID-5515] received: Hello there! [00000038]
[REQPID-5515] received: Hello there! [0000003c]
[REQPID-5515] received: Hello there! [0000003f]
[REQPID-5515] received: Hello there! [00000042]
[REQPID-5515] received: Hello there! [00000044]
[REQPID-5515] received: Hello there! [00000047]
[REQPID-5515] received: Hello there! [0000004b]
[REQPID-5515] received: Hello there! [0000004e]
[REQPID-5515] received: Hello there! [00000051]
[REQPID-5515] received: Hello there! [00000053]
[REQPID-5515] received: Hello there! [00000057]
[REQPID-5515] received: Hello there! [0000005a]
[REQPID-5515] received: Hello there! [0000005d]
[REQPID-5515] received: Hello there! [00000060]
[REQPID-5515] received: Hello there! [00000062]

    仔细观察运行结果可以得到一些有趣的现象,比如说对于REP/REQ协议,服务端发送的一条消息只有一个客户端能接收到,这说明其中有一个消息分发的机制。以上代码的编写及运行,就是笔者开始使用Lua脚本进行zeromq开发的开端了。ZeroMQ的官网提供了很多资料,不过zguide的Lua脚本几乎不能运行(请参考:http://zguide.zeromq.org/lua:all);但相关的说明文档仍是很有参考价值的。此外,我还建议经常查看ZeroMQ的Lua模块源码,可以在github上访问:https://github.com/zeromq/lzmq,该仓库中的zcontex.c/zsocket.c源码可以加深我们对lzmq的理解从而更好地使用它;该库中也有一些示例和文档,分别在examples/doc目录下。

  为了进一步了解并使用C/C++开发zeromq的应用,就需要参考ZeroMQ的API,可以在http://api.zeromq.org/4-1:_start 查询。

注:LuaARM.tar.xz可在笔者的下载区获得;其在嵌入式ARM/Linux上的安装操作为:

   mkdir -p /system ; cd /system ; xzcat path/to/LuaARM.tar.xz | tar -x -f - ; source ./LuaARM/envsetup.sh

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值