合宙Air780EG开发板系列教程—07 TCP客户端(基于luatos的开发)

前言

socket客户端演示

包括但不限于以下模组

  1. EC618系列 – Air780E/Air780EG/Air600E/Air700E
  2. ESP32系列 – ESP32C3/ESP32S3/ESP32C2
  3. Air105搭配W5500
  4. 其他适配了socket层的bsp

支持的协议有: TCP/UDP/TLS-TCP/DTLS, 更高层级的协议,如http有单独的库

提示:

  1. socket支持多个连接的, 通常最多支持8个, 可通过不同的taskName进行区分
  2. 支持与http/mqtt/websocket/ftp库同时使用, 互不干扰
  3. 支持IP和域名, 域名是自动解析的, 但解析域名也需要耗时
  4. 加密连接(TLS/SSL)需要更多内存, 这意味着能容纳的连接数会小很多, 同时也更慢

对于多个网络出口的场景, 例如Air780E+W5500组成4G+以太网:

  1. 在socket.create函数设置网络适配器的id
  2. 请到同级目录查阅更细致的demo

如需使用ipv6, 请查阅 demo/ipv6,demo地址:https://gitee.com/openLuat/LuatOS/blob/master/demo/socket/client/main.lua, 本demo只涉及ipv4

一、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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值