前言
socket客户端演示
包括但不限于以下模组
- EC618系列 – Air780E/Air780EG/Air600E/Air700E
- ESP32系列 – ESP32C3/ESP32S3/ESP32C2
- Air105搭配W5500
- 其他适配了socket层的bsp
支持的协议有: TCP/UDP/TLS-TCP/DTLS, 更高层级的协议,如http有单独的库
提示:
- socket支持多个连接的, 通常最多支持8个, 可通过不同的taskName进行区分
- 支持与http/mqtt/websocket/ftp库同时使用, 互不干扰
- 支持IP和域名, 域名是自动解析的, 但解析域名也需要耗时
- 加密连接(TLS/SSL)需要更多内存, 这意味着能容纳的连接数会小很多, 同时也更慢
对于多个网络出口的场景, 例如Air780E+W5500组成4G+以太网:
- 在socket.create函数设置网络适配器的id
- 请到同级目录查阅更细致的demo
如需使用ipv6, 请查阅 demo/ipv6,demo地址:https://gitee.com/openLuat/LuatOS/blob/master/demo/socket/client/main.lua, 本demo只涉及ipv4
- socket官接口函数点击:LuatOS-SOC接口文档
一、TCP客户端
1、打开tcp服务器
官方网站有LuatOS的网络测试工具,直接用官方的好了,点击LuatOS网络测试工具。
打开tcp,然后记住ip地址和端口号
2、写 TCP 客户端
代码:
-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "tcp_client"
VERSION = "1.0.0"
log.info("main", PROJECT, VERSION)
-- 一定要添加sys.lua !!!!
sys = require("sys")
sysplus = require("sysplus")
libnet = require "libnet"
--=============================================================
-- 测试网站 https://netlab.luatos.com/ 点击 打开TCP 获取测试端口号
-- 要按实际情况修改
local host = "112.125.89.8"
local port = 45982
--=============================================================
-- 处理未识别的网络消息
local function netCB(msg)
log.info("未处理消息", msg[1], msg[2], msg[3], msg[4])
end
-- 统一联网函数
sys.taskInit(function()
if socket then
-- 适配了socket库也OK, 就当1秒联网吧
sys.timerStart(sys.publish, 1000, "IP_READY")
end
-- 默认都等到联网成功
sys.waitUntil("IP_READY")
sys.publish("net_ready")
end)
-- 演示task
local function sockettest()
-- 等待联网
sys.waitUntil("net_ready")
socket.sntp() -- sntp时间同步
-- 开始正在的逻辑, 发起socket链接,等待数据/上报心跳
local taskName = "sc"
local topic = taskName .. "_txrx"
log.info("topic", topic)
local txqueue = {}
sysplus.taskInitEx(sockettask, taskName, netCB, taskName, txqueue, topic)
while 1 do
local result, tp, data = sys.waitUntil(topic, 30000)
log.info("event", result, tp, data)
if not result then
-- 等很久了,没数据上传/下发, 发个日期心跳包吧
table.insert(txqueue, string.char(0x01,0x02))
sys_send(taskName, socket.EVENT, 0)
elseif tp == "uplink" then
-- 上行数据, 主动上报的数据,那就发送呀
table.insert(txqueue, data)
sys_send(taskName, socket.EVENT, 0)
elseif tp == "downlink" then
-- 下行数据,接收的数据, 从ipv6task来的
-- 其他代码可以通过 sys.publish()
log.info("socket", "收到下发的数据了")
end
end
end
function sockettask(d1Name, txqueue, rxtopic)
-- 准备好所需要的接收缓冲区
local rx_buff = zbuff.create(1024)
local netc = socket.create(nil, d1Name)
socket.config(netc)
log.info("任务id", d1Name)
while true do
-- 连接服务器, 15秒超时
log.info("socket", "开始连接服务器")
sysplus.cleanMsg(d1Name)
local result = libnet.connect(d1Name, 15000, netc, host, port)
if result then
log.info("socket", "服务器连上了")
libnet.tx(d1Name, 0, netc, "hello,cheney")
else
log.info("socket", "服务器没连上了!!!")
end
while result do
-- 连接成功之后, 先尝试接收
-- log.info("socket", "调用rx接收数据")
local succ, param = socket.rx(netc, rx_buff)
if not succ then
log.info("服务器断开了",ip, port)
break
end
-- 如果服务器有下发数据, used()就必然大于0, 进行处理
if rx_buff:used() > 0 then
log.info("socket", "收到服务器数据,长度:", rx_buff:used())
local data = rx_buff:query() -- 获取数据
sys.publish(rxtopic, "downlink", data)
rx_buff:del()
end
-- log.info("libnet", "调用wait开始等待消息")
-- 等待事件, 例如: 服务器下发数据, 有数据准备上报, 服务器断开连接
result, param, param2 = libnet.wait(d1Name, 15000, netc)
if not result then
-- 网络异常了, 那就断开了, 执行清理工作
log.info("socket", "服务器断开了", result, param)
break
elseif #txqueue > 0 then
-- 有待上报的数据,处理之
while #txqueue > 0 do
local data = table.remove(txqueue, 1)
if not data then
break
end
result,param = libnet.tx(d1Name, 15000, netc,data)
log.info("libnet", "发送数据的结果", result, param)
if not result then
log.info("socket", "数据发送异常")
break
end
end
end
-- 循环尾部, 继续下一轮循环
end
-- 能到这里, 要么服务器断开连接, 要么上报(tx)失败, 或者是主动退出
libnet.close(d1Name, 5000, netc)
-- log.info(rtos.meminfo("sys"))
sys.wait(30000) -- 这是重连时长, 自行调整
end
end
sys.taskInit(sockettest)
-- 定时上报数据
sys.taskInit(function()
sys.wait(5000)
while 1 do
sys.publish("sc_txrx", "uplink", os.date())
sys.wait(3000)
end
end)
-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
测试结果:
二、TCP 和 串口
代码:
-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "tcp_client"
VERSION = "1.0.0"
log.info("main", PROJECT, VERSION)
-- 一定要添加sys.lua !!!!
sys = require("sys")
sysplus = require("sysplus")
libnet = require "libnet"
--=============================================================
-- 测试网站 https://netlab.luatos.com/ 点击 打开TCP 获取测试端口号
-- 要按实际情况修改
local host = "112.125.89.8"
local port = 45982
--=============================================================
-- 处理未识别的网络消息
local function netCB(msg)
log.info("未处理消息", msg[1], msg[2], msg[3], msg[4])
end
-- 统一联网函数
sys.taskInit(function()
if socket then
-- 适配了socket库也OK, 就当1秒联网吧
sys.timerStart(sys.publish, 1000, "IP_READY")
end
-- 默认都等到联网成功
sys.waitUntil("IP_READY")
sys.publish("net_ready")
end)
-- 演示task
local function sockettest()
-- 等待联网
sys.waitUntil("net_ready")
socket.sntp() -- sntp时间同步
-- 开始正在的逻辑, 发起socket链接,等待数据/上报心跳
local taskName = "sc"
local topic = taskName .. "_txrx"
log.info("topic", topic)
local txqueue = {}
sysplus.taskInitEx(sockettask, taskName, netCB, taskName, txqueue, topic)
while 1 do
local result, tp, data = sys.waitUntil(topic, 30000)
log.info("event", result, tp, data)
if not result then
-- 等很久了,没数据上传/下发, 发个日期心跳包吧
table.insert(txqueue, string.char(0x01,0x02))
sys_send(taskName, socket.EVENT, 0)
elseif tp == "uplink" then
-- 上行数据, 主动上报的数据,那就发送呀
table.insert(txqueue, data)
sys_send(taskName, socket.EVENT, 0)
elseif tp == "downlink" then
-- 下行数据,接收的数据, 从ipv6task来的
-- 其他代码可以通过 sys.publish()
log.info("socket", "收到下发的数据了")
end
end
end
function sockettask(d1Name, txqueue, rxtopic)
-- 准备好所需要的接收缓冲区
local rx_buff = zbuff.create(1024)
local netc = socket.create(nil, d1Name)
socket.config(netc)
log.info("任务id", d1Name)
while true do
-- 连接服务器, 15秒超时
log.info("socket", "开始连接服务器")
sysplus.cleanMsg(d1Name)
local result = libnet.connect(d1Name, 15000, netc, host, port)
if result then
log.info("socket", "服务器连上了")
libnet.tx(d1Name, 0, netc, "hello,cheney")
else
log.info("socket", "服务器没连上了!!!")
end
while result do
-- 连接成功之后, 先尝试接收
-- log.info("socket", "调用rx接收数据")
local succ, param = socket.rx(netc, rx_buff)
if not succ then
log.info("服务器断开了",ip, port)
break
end
-- 如果服务器有下发数据, used()就必然大于0, 进行处理
if rx_buff:used() > 0 then
log.info("socket", "收到服务器数据,长度:", rx_buff:used())
local data = rx_buff:query() -- 获取数据
sys.publish(rxtopic, "downlink", data)
-- ***
uart.write(1,data) -- 服务器下发数据到串口
-- ***
rx_buff:del()
end
-- log.info("libnet", "调用wait开始等待消息")
-- 等待事件, 例如: 服务器下发数据, 有数据准备上报, 服务器断开连接
result, param, param2 = libnet.wait(d1Name, 15000, netc)
if not result then
-- 网络异常了, 那就断开了, 执行清理工作
log.info("socket", "服务器断开了", result, param)
break
elseif #txqueue > 0 then
-- 有待上报的数据,处理之
while #txqueue > 0 do
local data = table.remove(txqueue, 1)
if not data then
break
end
result,param = libnet.tx(d1Name, 15000, netc,data)
log.info("libnet", "发送数据的结果", result, param)
if not result then
log.info("socket", "数据发送异常")
break
end
end
end
-- 循环尾部, 继续下一轮循环
end
-- 能到这里, 要么服务器断开连接, 要么上报(tx)失败, 或者是主动退出
libnet.close(d1Name, 5000, netc)
-- log.info(rtos.meminfo("sys"))
sys.wait(30000) -- 这是重连时长, 自行调整
end
end
sys.taskInit(sockettest)
-- 演示uart数据上报
uart.setup(1, 115200) -- 注意, 是UART1, 不是虚拟串口, 演示目的
uart.on(1, "receive", function(id, len)
while 1 do
local s = uart.read(id, 1024)
if #s == 0 then
break
end
sys.publish("sc_txrx", "uplink", s)
if #s == len then
break
end
end
end)
-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
测试结果:
服务器下发数据
附:
libnet.lua,这个文件在串口调试工具那个文件夹里面有
--[[
@module libnet
@summary libnet 在socket库基础上的同步阻塞api,socket库本身是异步非阻塞api
@version 1.0
@date 2023.03.16
@author lisiqi
]]
local libnet = {}
--[[
阻塞等待网卡的网络连接上,只能用于sysplus.taskInitEx创建的任务函数中
@api libnet.waitLink(taskName,timeout,...)
@string 任务标志
@int 超时时间,如果==0或者空,则没有超时一致等待
@... 其他参数和socket.linkup一致
@return boolean 失败或者超时返回false 成功返回true
]]
function libnet.waitLink(taskName, timeout, ...)
local succ, result = socket.linkup(...)
if not succ then
return false
end
if not result then
result = sys_wait(taskName, socket.LINK, timeout)
else
return true
end
if type(result) == 'table' and result[2] == 0 then
return true
else
return false
end
end
--[[
阻塞等待IP或者域名连接上,如果加密连接还要等握手完成,只能用于sysplus.taskInitEx创建的任务函数中
@api libnet.connect(taskName,timeout,...)
@string 任务标志
@int 超时时间,如果==0或者空,则没有超时一致等待
@... 其他参数和socket.connect一致
@return boolean 失败或者超时返回false 成功返回true
]]
function libnet.connect(taskName,timeout, ... )
local succ, result = socket.connect(...)
if not succ then
return false
end
if not result then
result = sys_wait(taskName, socket.ON_LINE, timeout)
else
return true
end
if type(result) == 'table' and result[2] == 0 then
return true
else
return false
end
end
--[[
阻塞等待客户端连接上,只能用于sysplus.taskInitEx创建的任务函数中
@api libnet.listen(taskName,timeout,...)
@string 任务标志
@int 超时时间,如果==0或者空,则没有超时一致等待
@... 其他参数和socket.listen一致
@return boolean 失败或者超时返回false 成功返回true
]]
function libnet.listen(taskName,timeout, ... )
local succ, result = socket.listen(...)
if not succ then
return false
end
if not result then
result = sys_wait(taskName, socket.ON_LINE, timeout)
else
return true
end
if type(result) == 'table' and result[2] == 0 then
return true
else
return false
end
end
--[[
阻塞等待数据发送完成,只能用于sysplus.taskInitEx创建的任务函数中
@api libnet.tx(taskName,timeout,...)
@string 任务标志
@int 超时时间,如果==0或者空,则没有超时一直等待
@... 其他参数和socket.tx一致
@return boolean 失败或者超时返回false,缓冲区满了或者成功返回true
@return boolean 缓存区是否满了
]]
function libnet.tx(taskName,timeout, ...)
local succ, is_full, result = socket.tx(...)
if not succ then
return false, is_full
end
if is_full then
return true, true
end
if not result then
result = sys_wait(taskName, socket.TX_OK, timeout)
else
return true, is_full
end
if type(result) == 'table' and result[2] == 0 then
return true, false
else
return false, is_full
end
end
--[[
阻塞等待新的网络事件,只能用于sysplus.taskInitEx创建的任务函数中,可以通过sysplus.sendMsg(taskName,socket.EVENT,0)或者sys_send(taskName,socket.EVENT,0)强制退出
@api libnet.wait(taskName,timeout, netc)
@string 任务标志
@int 超时时间,如果==0或者空,则没有超时一致等待
@userdata socket.create返回的netc
@return boolean 网络异常返回false,其他返回true
@return boolean 超时返回false,有新的网络事件到返回true
]]
function libnet.wait(taskName,timeout, netc)
local succ, result = socket.wait(netc)
if not succ then
return false,false
end
if not result then
result = sys_wait(taskName, socket.EVENT, timeout)
else
return true,true
end
if type(result) == 'table' then
if result[2] == 0 then
return true, true
else
return false, false
end
else
return true, false
end
end
--[[
阻塞等待网络断开连接,只能用于sysplus.taskInitEx创建的任务函数中
@api libnet.close(taskName,timeout, netc)
@string 任务标志
@int 超时时间,如果==0或者空,则没有超时一致等待
@userdata socket.create返回的netc
]]
function libnet.close(taskName,timeout, netc)
local succ, result = socket.discon(netc)
if not succ then
socket.close(netc)
return
end
if not result then
result = sys_wait(taskName, socket.CLOSED, timeout)
else
socket.close(netc)
return
end
socket.close(netc)
end
return libnet