跳转至

socket - 网络接口

作者:王城钧

一、概述

socket 库作为网络通信的核心工具,socket 库本身是异步非阻塞 api,提供了跨平台、多协议支持的标准化编程接口,能够高效实现设备间基于 TCP/UDP 协议的数据可靠传输或实时交互。与之对应的 libnet 库是在 socket 库基础上的同步阻塞 api,socket 库本身是异步非阻塞 api,开发 socket 应用程序时,强烈推荐优先使用 libnet 的同步接口,我们的 demo 也是以使用 libnet 的同步接口为主。

libnet 扩展库 api 文档链接:libnet

二、核心示例

1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;

2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;

3、更加完整和详细的 demo,请参考 LuatOS 仓库 中各个产品目录下的 demo/socket/client/long_connection

注意:如下演示的为 TCP 的 client 端 demo

-- 关于本库的一些说明:本文中的描述内容,使用了网卡和网络适配器这两种概念,这两种概念所表达的意思完全一样;
-- 本库用于网络通信, 支持TCP, UDP, 也支持TLS加密传输;
-- 支持加密传输版本有 TLS 1.0/1.1/1.2/1.3, DTLS 1.0/1.2, 当前不支持TLS 1.3;
-- 不支持 SSL 3.0, 该协议已经被废弃, 也不安全;
-- 支持的加密算法有 RSA, ECC, AES, 3DES, SHA1, SHA256, MD5 等等;
-- 完整的加密套件列表, 可通过 crypto.cipher_suites() 获取;

-- 本库的函数, 除非特别说明, 都是立即返回的非阻塞函数;
-- 这意味着, 函数调用成功, 并不代表网络操作成功, 只代表网络操作已经开始;

PROJECT = "SOCKET_CONNECTION"
VERSION = "001.000.000"

local libnet = require "libnet"

local SERVER_ADDR = "112.125.89.8" -- 根据实际修改
local SERVER_PORT = 47826     -- 根据实际修改
local TASK_NAME = "socket_connect_task"
local send_queue = {}
local TCP_SENDER_TASK_NAME = "tcp_send_task"
local recv_buff = nil

local function send_data_req_proc_func(tag, data, cb)
    table.insert(send_queue, {data = "send from "..tag..": "..data, cb = cb})
    sys.sendMsg(TCP_SENDER_TASK_NAME, socket.EVENT, 0)
end

function tcp_clientc_sender_proc(task_name, socket_client)
    while #send_queue > 0 do
        local send_item = table.remove(send_queue, 1)

        -- 发送这条数据,超时时间15秒钟
        local result = libnet.tx(task_name, 15000, socket_client, send_item.data)
        -- 发送失败
        if not result then
            log.error("tcp_client_sender.proc", "libnet.tx error")
            -- 如果当前发送的数据有用户回调函数,则执行用户回调函数
            if send_item.cb and send_item.cb.func then
                send_item.cb.func(false, send_item.cb.para)
            end
            return false
        end
        log.info("tcp_client_sender.proc", "send success")
        -- 发送成功,如果当前发送的数据有用户回调函数,则执行用户回调函数
        if send_item.cb and send_item.cb.func then
            send_item.cb.func(true, send_item.cb.para)
        end
    end

    return true
end

function tcp_client_sender_exception_proc()
    while #send_queue > 0 do
        local send_item = table.remove(send_queue, 1)
        if send_item.cb and send_item.cb.func then
            send_item.cb.func(false, send_item.cb.para)
        end
    end
end

sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)

function tcp_client_receiver_proc(socket_client)
    -- 如果socket数据接收缓冲区还没有申请过空间,则先申请内存空间
    if recv_buff == nil then
        recv_buff = zbuff.create(1024)
    end

    while true do
        local result = socket.rx(socket_client, recv_buff)

        if not result then
            log.error("tcp_client_receiver_proc", "socket.rx error")
            return false
        end

        -- 如果读取到了数据, used()就必然大于0, 进行处理
        if recv_buff:used() > 0 then
            log.info("tcp_client_receiver_proc", "recv data len", recv_buff:used())

            -- 读取socket数据接收缓冲区中的数据,赋值给data
            local data = recv_buff:query()

            -- 将数据data通过"RECV_DATA_FROM_SERVER"消息publish出去,给其他应用模块处理
            sys.publish("RECV_DATA_FROM_SERVER", "recv from tcp server: ", data)

            recv_buff:del()
        else
            break
        end
    end

    return true
end

local function tcp_client_main_task_func()
    local socket_client
    local result, para1, para2

    while true do
        -- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
        while not socket.adapter(socket.dft()) do
            log.warn("tcp_client_main_task_func", "wait IP_READY", socket.dft())
            sys.waitUntil("IP_READY", 1000)
        end

        -- 检测到了IP_READY消息
        log.info("tcp_client_main_task_func", "recv IP_READY", socket.dft())

        -- 创建socket client对象
        socket_client = socket.create(nil, TASK_NAME)
        -- 如果创建socket client对象失败
        if not socket_client then
            log.error("tcp_client_main_task_func", "socket.create error")
            goto EXCEPTION_PROC
        end

        -- 配置socket client对象为tcp client
        result = socket.config(socket_client)
        -- 如果配置失败
        if not result then
            log.error("tcp_client_main_task_func", "socket.config error")
            goto EXCEPTION_PROC
        end

        -- 连接server
        result = libnet.connect(TASK_NAME, 15000, socket_client, SERVER_ADDR, SERVER_PORT)
        -- 如果连接server失败
        if not result then
            log.error("tcp_client_main_task_func", "libnet.connect error")
            goto EXCEPTION_PROC
        end

        log.info("tcp_client_main_task_func", "libnet.connect success")

        while true do
            if not tcp_client_receiver_proc(socket_client) then
                log.error("tcp_client_main_task_func", "tcp_client_receiver_proc error")
                break
            end

            if not tcp_clientc_sender_proc(TASK_NAME, socket_client) then
                log.error("tcp_client_main_task_func", "tcp_client_sender_proc error")
                break
            end

            result, para1, para2 = libnet.wait(TASK_NAME, 15000, socket_client)
            log.info("tcp_client_main_task_func", "libnet.wait", result, para1, para2)

            if not result then
                log.warn("tcp_client_main_task_func", "connection exception")
                break
            end
        end

        -- 出现异常
        ::EXCEPTION_PROC::

        -- 数据发送应用模块对来不及发送的数据做清空和通知失败处理
        tcp_client_sender_exception_proc()

        -- 如果存在socket client对象
        if socket_client then
            -- 关闭socket client连接
            libnet.close(TASK_NAME, 5000, socket_client)

            -- 释放socket client对象
            socket.release(socket_client)
            socket_client = nil
        end

        sys.wait(5000)
    end
end

sys.taskInitEx(tcp_client_main_task_func, TASK_NAME)

sys.run()

三、常量详解

核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;

关于网络适配器类型的常量:

注意:目前除 8101 系列产品默认网络适配器为 socket.LWIP_STA,其他主流模组默认网络适配器为 socket.LWIP_GP。

socket.LWIP_GP

常量含义:4G网卡
         LWIP是指:传输层和网络层使用的是LuatOS内核固件中的LwIP协议栈
         GP是GPRS的缩写GPRS是2G网络时代的分组数据网络,此处用来代指移动蜂窝数据网络,例如4G网络
数据类型:number
取值范围:1
示例代码:socket.localIP(socket.LWIP_GP)

socket.LWIP_STA

常量含义:WiFi设备模式网卡
         LWIP是指:传输层和网络层使用的是LuatOS内核固件中的LwIP协议栈
         STA是STATION的缩写,表示WiFi设备模式,需要连接WiFi热点才能上网
数据类型:number
取值范围:2
示例代码:socket.localIP(socket.LWIP_STA)

socket.LWIP_AP

常量含义:WiFi热点模式网卡
         LWIP是指:传输层和网络层使用的是LuatOS内核固件中的LwIP协议栈
         AP是Access Ponit的缩写,意思是WiFi热点,供其他WiFi设备接入上网
数据类型:number
取值范围:3
示例代码:socket.localIP(socket.LWIP_AP)

socket.LWIP_ETH

常量含义:使用LwIP协议栈的以太网卡
         LWIP是指:传输层和网络层使用的是LuatOS内核固件中的LwIP协议栈
         ETH是Ethernet的缩写,意思是以太网;
数据类型:number
取值范围:4
示例代码:socket.localIP(socket.LWIP_ETH)

socket.ETH0

常量含义:使用硬件协议栈的以太网卡;
         ETH是Ethernet的缩写,意思是以太网,ETH0表示编号为0的硬件协议栈以太网卡
数据类型:number
取值范围:16
示例代码:socket.localIP(socket.ETH0)

socket.USB

常量含义:USB接口的以太网卡
         常见的USB以太网卡又可以分为 USB RNDIS以太网卡  USB ECM以太网卡两种
数据类型:number
取值范围:17
示例代码:socket.localIP(socket.USB)

socket.LWIP_USER0

常量含义:使用LWIP协议栈的自定义网卡0
数据类型:number
取值范围:7
示例代码:socket.localIP(socket.LWIP_USER0)

socket.LWIP_USER1

常量含义:使用LWIP协议栈的自定义网卡1
数据类型:number
取值范围:8
示例代码:socket.localIP(socket.LWIP_USER1)

socket.LWIP_USER2

常量含义:使用LWIP协议栈的自定义网卡2
数据类型:number
取值范围:9
示例代码:socket.localIP(socket.LWIP_USER2)

socket.LWIP_USER3

常量含义:使用LWIP协议栈的自定义网卡3
数据类型:number
取值范围:10
示例代码:socket.localIP(socket.LWIP_USER3)

socket.LWIP_USER4

常量含义:使用LWIP协议栈的自定义网卡4
数据类型:number
取值范围:11
示例代码:socket.localIP(socket.LWIP_USER4)

socket.LWIP_USER5

常量含义:使用LWIP协议栈的自定义网卡5
数据类型:number
取值范围:12
示例代码:socket.localIP(socket.LWIP_USER5)

socket.LWIP_USER6

常量含义:使用LWIP协议栈的自定义网卡6
数据类型:number
取值范围:13
示例代码:socket.localIP(socket.LWIP_USER6)

socket.LWIP_USER7

常量含义:使用LWIP协议栈的自定义网卡7
数据类型:number
取值范围:14
示例代码:socket.localIP(socket.LWIP_USER7)

socket.LWIP_GP_GW

常量含义:4G代理网关
数据类型:number
取值范围:暂无;
示例代码:netdrv.setup(socket.LWIP_GP_GW, netdrv.WHALE)

关于网络事件的常量:

常量含义:表示socket的物理链路连接状态事件
         当设备与网络的物理连接(如4G模块的基站连接)建立或断开时触发;
数据类型:number
取值范围:在不同种类的的内核固件中或者同一种内核固件的不同版本中,这个常量的具体数值可能会发生变化;
         在编程时,如果用到这个常量,直接使用socket.LINK,不要使用它具体的number数值
示例代码:-- 创建socket client对象
         socket_client = socket.create(nil, "tcp_client")

         -- 判断socket_client绑定的网卡是否准备就绪
         local succ, result = socket.linkup(socket_client)

         -- 调用接口socket.linkup失败
         if not succ then
             return false
         end

         -- 调用接口socket.linkup接口成功,此时通过第二个返回值result来判断网卡是否准备就绪
         -- result为true,表示网卡准备就绪
         if result then
             return true
         end

         -- result为false,表示网卡还没有准备就绪,此时需要通过异步消息socket.LINK来监测结果
         if not result then
             result = sys.waitMsg("tcp_client", socket.LINK, timeout)
         end

         -- 收到了socket.LINK消息,并且携带的第一个参数为0表示成功
         if type(result) == 'table' and result[2] == 0 then
             return true
         -- 超时没有收到socket.LINK消息,或者收到了socket.LINK消息但是携带的第一个参数非0表示失败
         else
             return false
         end

socket.ON_LINE

常量含义:表示socket的网络就绪状态事件
         设备成功接入网络时触发;
         client使用时,表示某一个socket client是否成功连接server
         server使用时,表示某一个socket server是否成功连接client
数据类型:number
取值范围:在不同种类的的内核固件中或者同一种内核固件的不同版本中,这个常量的具体数值可能会发生变化;
         在编程时,如果用到这个常量,直接使用socket.ON_LINE,不要使用它具体的number数值
示例代码:-- 创建socket client对象
         socket_client = socket.create(nil, "tcp_client")

         -- 判断socket_client绑定的网卡是否准备就绪
         local succ, result = socket.connect(socket_client, "112.125.89.8", 46946)

         -- 调用接口socket.connect失败
         if not succ then
             return false
         end

         -- 调用接口socket.connect接口成功,此时通过第二个返回值result来判断是否连接成功
         -- result为true,表示连接成功
         if result then
             return true
         end

         -- result为false,表示连接中,还没有连接成功,此时需要通过异步消息socket.ON_LINE来监测结果
         if not result then
             result = sys.waitMsg("tcp_client", socket.ON_LINE, timeout)
         end

         -- 收到了socket.ON_LINE消息,并且携带的第一个参数为0表示成功
         if type(result) == 'table' and result[2] == 0 then
             return true
         -- 超时没有收到socket.ON_LINE消息,或者收到了socket.ON_LINE消息但是携带的第一个参数非0表示失败
         else
             return false
         end

socket.EVENT

常量含义:表示socket对象和对端连接成功之后,出现的通用事件消息,以下几种业务逻辑会产生此消息:
         1socket对象和对端之间的连接出现异常(例如对端主动断开,网络环境出现异常等),
            此时在内核固件中会发送消息socket.EVENT
         2socket对象接收到对端发送过来的数据,此时在内核固件中会发送消息socket.EVENT
         3、在socket应用脚本程序中,根据自己的项目需求,发送消息socket.EVENT
            例如socket client需要发送数据到server,可以通过消息socket.EVENT通知socket client主task处理
数据类型:number
取值范围:暂无;
示例代码:-- 数据收发以及网络连接异常事件总处理逻辑
         while true do
             -- 数据接收处理(接收处理必须写在libnet.wait之前,因为老版本的内核固件要求必须这样,新版本的内核固件没这个要                                                                                                                                                                                                                                       -- 求,为了不出问题,写在libnet.wait之前就行了)
             -- 如果处理失败,则退出循环
             if not tcp_client_receiver.proc(socket_client) then
                 log.error("tcp_client_main_task_func", "tcp_client_receiver.proc error")
                 break
             end

             -- 数据发送处理
             -- 如果处理失败,则退出循环
             if not tcp_client_sender.proc(TASK_NAME, socket_client) then
                 log.error("tcp_client_main_task_func", "tcp_client_sender.proc error")
                 break
             end

             -- 阻塞等待socket.EVENT事件或者15秒钟超时
             -- 以下三种业务逻辑会发布事件:
             -- 1、socket client和server之间的连接出现异常(例如server主动断开,网络环境出现异常等),此时在内核固件中会发                                        布事件socket.EVENT
             -- 2、socket client接收到server发送过来的数据,此时在内核固件中会发布事件socket.EVENT
             -- 3、socket client需要发送数据到server, 在tcp_client_sender.lua中会发布事件socket.EVENT
             result, para1, para2 = libnet.wait(TASK_NAME, 15000, socket_client)
             log.info("tcp_client_main_task_func", "libnet.wait", result, para1, para2)

             -- 如果连接异常,则退出循环
             if not result then
                 log.warn("tcp_client_main_task_func", "connection exception")
                 break
             end
         end

socket.TX_OK

常量含义:表示socket对象的应用数据发送结果消息
数据类型:number
取值范围:在不同种类的的内核固件中或者同一种内核固件的不同版本中,这个常量的具体数值可能会发生变化;
         在编程时,如果用到这个常量,直接使用socket.TX_OK,不要使用它具体的number数值
示例代码:- 创建socket client对象
         socket_client = socket.create(nil, TASK_NAME)
         -- 如果创建socket client对象失败
         if not socket_client then
             log.error("tcp_client_main_task_func", "socket.create error")
             goto EXCEPTION_PROC
         end

         -- 配置socket client对象为tcp client
         result = socket.config(socket_client)
         -- 如果配置失败
         if not result then
             log.error("tcp_client_main_task_func", "socket.config error")
             goto EXCEPTION_PROC
         end

         -- 连接server
         result = libnet.connect(TASK_NAME, 15000, socket_client, SERVER_ADDR, SERVER_PORT)
         -- 如果连接server失败
         if not result then
             log.error("tcp_client_main_task_func", "libnet.connect error")
             goto EXCEPTION_PROC
         end

         log.info("tcp_client_main_task_func", "libnet.connect success")

         -- 给server发送数据"123456"
         local succ, is_full, result = socket.tx(socket_client, "123456")

         -- succ为false,表示socket.tx接口调用失败
         if not succ then
             log.error("tcp_client_main_task_func", "socket.tx error")
             goto EXCEPTION_PROC
         end

         -- succ为true,表示socket.tx接口调用成功
         -- is_full为true,表示内核固件中的发送缓冲区满了,本次数据发送没有执行
         -- 需要等待
         if is_full then
             log.warn("tcp_client_main_task_func", "socket.tx send buff is full")
             goto EXCEPTION_PROC
         end

         -- succ为true,表示socket.tx接口调用成功
         -- is_full为false,表示内核固件中的发送缓冲区正常,本次数据已经交给协议栈去发送
         -- result为false,表示正在发送中,此时需要通过异步消息socket.TX_OK来监测发送结果
         if not result then
             result = sys.waitMsg(TASK_NAME, socket.TX_OK, timeout)
         -- succ为true,表示socket.tx接口调用成功
         -- is_full为false,表示内核固件中的发送缓冲区正常,本次数据已经交给协议栈去发送
         -- result为true,表示数据发送成功
         else
             log.info("tcp_client_main_task_func", "send success")
             goto EXCEPTION_PROC
         end

         -- 收到了socket.TX_OK消息,并且携带的第一个参数为0表示成功
         if type(result) == 'table' and result[2] == 0 then
             log.info("tcp_client_main_task_func", "send success")
             goto EXCEPTION_PROC
         -- 超时没有收到socket.TX_OK消息,或者收到了socket.TX_OK消息但是携带的第一个参数非0表示失败
         else
             log.error("tcp_client_main_task_func", "send error")
             goto EXCEPTION_PROC
         end

socket.CLOSED

常量含义:表示socket连接已关闭事件
         当连接因主动关闭、故障中断或服务器断开时触发;
数据类型:number
取值范围:在不同种类的的内核固件中或者同一种内核固件的不同版本中,这个常量的具体数值可能会发生变化;
         在编程时,如果用到这个常量,直接使用socket.TX_OK,不要使用它具体的number数值
示例代码:-- 创建socket client对象
         socket_client = socket.create(nil, TASK_NAME)
         -- 如果创建socket client对象失败
         if not socket_client then
             log.error("tcp_client_main_task_func", "socket.create error")
             goto EXCEPTION_PROC
         end

         -- 配置socket client对象为tcp client
         result = socket.config(socket_client)
         -- 如果配置失败
         if not result then
             log.error("tcp_client_main_task_func", "socket.config error")
             goto EXCEPTION_PROC
         end

         -- 连接server
         result = libnet.connect(TASK_NAME, 15000, socket_client, SERVER_ADDR, SERVER_PORT)
         -- 如果连接server失败
         if not result then
             log.error("tcp_client_main_task_func", "libnet.connect error")
             goto EXCEPTION_PROC
         end

         log.info("tcp_client_main_task_func", "libnet.connect success")

         -- 主动关闭socket 
         socket.discon(socket_client)

         -- 阻塞等待关闭结果或者超时退出
         sys.waitMsg(TASK_NAME, socket.CLOSED, timeout)

四、函数详解

socket.localIP(adapter)

功能

获取本地 ip

参数

adapter

参数含义:表示网卡编号;
         adapter是number类型时,表示某一种网卡;
         adapter是nil类型时,表示:从第一个网卡(socket.LWIP_GPnumber值为1)开始,
         遍历全部网卡,遍历过程中如果发现IP_READY的网卡,则终止遍历;
数据类型:number或者nil
取值范围:number类型时,本文常量详解章节中的所有网络适配器编号常量;
是否必选:可选传入此参数,如果没有传入此参数,会使用当前时刻的默认网卡;
注意事项:暂无;
参数示例:例如socket.LWIP_STA表示内置的WiFi设备模式网卡

返回值

local ip, netmask, gateway = socket.localIP()

有三个返回值 ip, netmask, gateway

ip

含义说明:返回本地IP地址
数据类型:string
取值范围:暂无;
注意事项:只有网卡准备就绪后,才能读取到这个值;
返回示例:例如4G网卡准备就绪后,读取到的本地ip为"10.80.235.107"

netmask

含义说明:返回网络掩码;
数据类型:string
取值范围:暂无;
注意事项:只有网卡准备就绪后,才能读取到这个值;
返回示例:"255.255.255.255"

gateway

含义说明:返回网关IP
数据类型:string
取值范围:暂无;
注意事项:只有网卡准备就绪后,才能读取到这个值;
返回示例:例如4G网卡准备就绪后,读取到的网关ip为" 0.0.0.0"

示例

local function get_4g_local_ip()
    log.info("socket.localIP(socket.LWIP_GP)", socket.localIP(socket.LWIP_GP))
end

local function get_wifi_local_ip()
    log.info("socket.localIP(socket.LWIP_STA)", socket.localIP(socket.LWIP_STA))
end

sys.timerLoopStart(get_4g_local_ip, 1000)
sys.timerLoopStart(get_wifi_local_ip, 1000)

-- 4G网卡如果还没有准备就绪,4G网卡的ip地址信息打印日志如下:
-- socket.localIP(socket.LWIP_GP)

-- 4G网卡如果准备就绪,4G网卡的ip地址信息打印日志如下:
-- socket.localIP(socket.LWIP_GP) 10.80.235.107 255.255.255.255 0.0.0.0

-- WiFi设备模式网卡如果还没有准备就绪,WiFi设备模式网卡的ip地址信息打印日志如下:
-- socket.localIP(socket.LWIP_STA) 0.0.0.0 0.0.0.0 0.0.0.0

-- WiFi设备模式网卡如果准备就绪,WiFi设备模式网卡的ip地址信息打印日志如下:
-- socket.localIP(socket.LWIP_STA) 192.168.31.156 255.255.255.0 192.168.31.1

socket.create(adapter, task_name)

功能

在指定网卡上创建一个socket对象;

在ram资源足够的情况下: 1、Air780系列/Air8000系列的模组,允许创建的同时存在的socket对象数量为64个; 2、Air6101系列/Air8101系列的模组,允许创建的同时存在的socket对象数量为32个;

参数

adapter

参数含义:上网使用的网卡ID
数据类型:number或者nil
取值范围:number类型时,本文常量详解章节中的所有网络适配器编号常量;
是否必选:可选传入此参数;
注意事项:如果没有传入此参数,内核固件会自动选择当前时间点其他功能模块设置的默认网卡;
         除非你socket请求时,一定要使用某一种网卡,才设置此参数;
         如果没什么特别要求,不要设置此参数,使用系统中设置的默认网卡即可;
         一般来说,LuatOS的网络应用demo中都会有netdrv_device功能模块设置默认网卡
         所以建议不要设置此参数,直接使用netdrv_device设置的默认网卡就行
参数示例:socket.LWIP_GP表示使用4G网卡

task_name

参数含义:表示socket对象绑定的task的名称
        socket的所有异步消息,都是内核固件通过定向消息发送,所以要指定一个task名称,才能处理定向消息;
数据类型:string
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:"socket_client_task"

返回值

local socket_ctrl = socket.create(nil, "Mysocket")

socket_ctrl

含义说明:创建的socket对象;如果是userdata类型表示创建成功,如果是nil表示创建失败
数据类型:userdata或者nil
取值范围:暂无;
注意事项:在ram资源足够的情况下
         1Air780系列/Air8000系列的模组,允许创建的同时存在的socket对象数量为64个
         2Air6101系列/Air8101系列的模组,允许创建的同时存在的socket对象数量为32个
         如果同时存在的socket对象数量超过了最大限制,再次创建就会返回失败;
         这种失败会在日志中打印:adapter no more ctrl!
返回示例:非nil值的一个userdata类型,可以直接使用if语句做逻辑判断

示例

-- 创建socket client对象
socket_client = socket.create(nil, "socket_client_task")
-- 如果创建socket client对象失败
if not socket_client then
    log.error("socket_client_task", "socket.create error")
end

socket.debug(ctrl, onoff)

功能

配置是否打开内核固件中network的debug日志信息;

一般来说,当需要抓取更多的日志给合宙技术人员分析时,可以打开此开关; 如果打开debug开关,使用Luatools抓日志时,在主界面窗口会出现类似于下面的日志信息(出现很多network开头的日志):

参数

ctrl

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:必须先创建socket对象,才能使用此接口控制debug信息开关
参数示例:暂无;

onoff

参数含义:表示是否打开debug信息
数据类型:boolean
取值范围:true打开debug开关false关闭debug开关
是否必选:可选传入此参数,若不填此参数,默认为false
注意事项:暂无;
参数示例:truefalse

返回值

nil

示例

-- 创建socket client对象
socket_client = socket.create(nil, TASK_NAME)
-- 如果创建socket client对象失败
if not socket_client then
    log.error("tcp_ssl_ca_main_task_func", "socket.create error")
    return
end

-- 在socket_client上打开内核固件的debug信息开关
socket.debug(socket_client, true)

socket.config(ctrl, local_port, is_udp, is_tls, keep_idle, keep_interval, keep_cnt, server_cert, client_cert, client_key, client_password)

功能

配置socket对象的参数(本地端口号,TCP/UDP/TLS协议,TCP keep alive心跳参数,证书认证信息

参数

ctrl

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:必须先创建socket对象,才能使用此接口配置参数;
参数示例:暂无;

local_port

参数含义:本地端口号;
数据类型:number或者nil
取值范围:端口号需要小于60000
是否必选:可选传入此参数,如果没有传入此参数,则内核固件会自动分配一个端口号;
注意事项:暂无;
参数示例:80

is_udp

参数含义:是否是UDPtrue表示UDPfalse或者nil表示TCP
数据类型:boolean或者nil
取值范围:true或false
是否必选:可选传入此参数,默认为TCP
注意事项:暂无;
参数示例:true

is_tls

参数含义:是否是加密传输;
数据类型:boolean或者nil
取值范围:true或false
是否必选:可选传入此参数;
注意事项:支持TLS 1.0/1.1/1.2, DTLS 1.0/1.2, 当前不支持TLS 1.3
         不支持 SSL 3.0, 该协议已经被废弃, 也不安全;
         支持的加密算法有 RSA, ECC, AES, 3DES, SHA1, SHA256, MD5等等
         完整的加密套件列表, 可通过 crypto.cipher_suites() 获取,获取代码如下:
         local suites = crypto.cipher_suites()
         if suites then
             log.info("crypto", "ciphers suites", json.encode(suites))
         end
参数示例:true

keep_idle

参数含义:连接空闲多长时间后,开始发送第一个 keepalive 探针报文;
         即允许的持续空闲时长,单位为秒;
数据类型:number或者nil
取值范围:number类型时,大于0的正整数;
是否必选:可选传入此参数;如果不传入此参数,表示不启用keepalive机制
注意事项:如果是不支持标准posix接口的网卡(比如W5500),则为心跳间隔;
参数示例:300

keep_interval

参数含义:发送第一个探针后,如果没收到ACK回复,间隔多久再发送下一个探针,单位为秒;
数据类型:number或者nil
取值范围:number类型时,大于0的正整数;
是否必选:可选传入此参数;
注意事项:如果keep_idle传入了number类型的有效参数,则keep_interval也必须是number类型
参数示例:10

keep_cnt

参数含义:总共发送多少次探针后,如果依然没有回复,则判定连接已断开;
数据类型:number或者nil
取值范围:number类型时,大于0的正整数;
是否必选:可选传入此参数;
注意事项:如果keep_idle传入了number类型的有效参数,则keep_cnt也必须是number类型
参数示例:3

server_cert

参数含义:TCP模式下的服务器ca证书数据UDP模式下的PSK
数据类型:string或者nil
取值范围:无特别限制;
是否必选:可选传入此参数;
注意事项:如果客户端不需要验证服务器证书,此参数可以为空,也可以为nil
         当客户端需要验证服务器证书时,需要此参数,
         如果证书数据在一个文件中,要把文件内容读出来,赋值给server_ca_cert
参数示例:例如通过Luatools烧录了server_ca.crt文件,就可以通过io.readFile("/luadb/server_ca.crt")读出文件内容赋值给赋值给server_ca_cert

client_cert

参数含义:TCP模式下的客户端证书数据UDP模式下的PSK-ID
         TCP模式下如果不需要验证客户端证书时,忽略,一般不需要验证客户端证书;
数据类型:string或者nil
取值范围:无特别限制;
是否必选:可选传入此参数;
注意事项:当服务器需要验证客户端证书时,需要此参数,如果证书数据在一个文件中,要把文件内容读出来,赋值给client_cert
参数示例:例如通过Luatools烧录了clinet.crt文件,就可以通过io.readFile("/luadb/clinet.crt")读出文件内容赋值给赋值给client_cert

client_key

参数含义:TCP模式下的客户端私钥加密数据
数据类型:string或者nil
取值范围:无特别限制;
是否必选:可选传入此参数;
注意事项:当服务器需要验证客户端证书时,需要此参数,如果加密后的私钥数据在一个文件中,要把文件内容读出来,赋值给client_key
参数示例:例如通过Luatools烧录了clinet.key文件,就可以通过io.readFile("/luadb/clinet.key")读出文件内容赋值给client.key

client_password

参数含义:TCP模式下的客户端私钥加密数据
数据类型:string或者nil
取值范围:无特别限制;
是否必选:可选传入此参数;
注意事项:当服务器需要验证客户端证书时,需要此参数,如果加密后的私钥数据在一个文件中,要把文件内容读出来,赋值给client_password
参数示例:例如通过Luatools烧录了clinet.password文件,就可以通过io.readFile("/luadb/clinet.password")读出文件内容赋值给client_password

返回值

local netc = socket.create(nil, netCB)

netc

含义说明:成功返回true,失败返回false
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:true

示例

-- 创建socket client对象
socket_client = socket.create(nil,"tcp_client_task")
-- 如果创建socket client对象失败
if not socket_client then
    log.error("tcp_client_task", "socket.create error")
    return
end

-- 配置socket client对象为tcp client
result = socket.config(socket_client)
-- 如果配置失败
if not result then
    log.error("tcp_client_task", "socket.config error")
    return
end

socket.linkup(ctrl)

功能

等待网卡 linkup(此函数给 libnet 扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用)

参数

ctrl

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

返回值

local succ, result = socket.linkup(ctrl)

succ

含义说明:表示函数调用的同步结果,true没有异常发生false失败了,如果false则不需要看下一个返回值了
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

result

含义说明:true已经linkupfalse没有linkup,之后需要接收socket.LINK消息
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

示例

--此函数给libnet扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用,不再举例。

socket.connect(ctrl, ip, remote_port, need_ipv6_dns)

功能

作为客户端连接服务器(此函数给 libnet 扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用)

参数

ctrl

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

ip

参数含义:ip地址 或者 int ip或者域名,如果是IPV4,可以是大端格式的number值
数据类型:string或者number
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

remote_port

参数含义:服务器端口号,小端格式;
数据类型:number
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

need_ipv6_dns

参数含义:域名解析是否要IPV6true要false不要,默认false不要,只有支持IPV6的协议栈才有效果
数据类型:boolean
取值范围:true或false
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:暂无;

返回值

local succ, result = socket.connect(ctrl, ip, remote_port)

succ

含义说明:true没有异常发生false失败了,如果false则不需要看下一个返回值了,如果有异常,后续要close
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

result

含义说明:true已经connectfalse没有connect,之后需要接收socket.ON_LINE消息
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

示例

--此函数给libnet扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用,不再举例。

--[[
常见的连接失败的code值, 会在日志中显示
-1 底层内存不足
-3 超时
-8 端口已经被占用
-11 链接未建立
-13 模块主动断开连接
-14 服务器主动断开连接
]]

socket.discon(ctrl)

功能

作为客户端断开连接(此函数给 libnet 扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用)

参数

ctrl

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

返回值

local succ, result = socket.discon(ctrl)

succ

含义说明:true没有异常发生false失败了,如果false则不需要看下一个返回值了
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

result

含义说明:true已经断开false没有断开,之后需要接收socket.CLOSED消息
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

示例

--此函数给libnet扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用,不再举例。

socket.close(ctrl)

功能

强制关闭 socket(此函数给 libnet 扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用)

参数

ctrl

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

返回值

nil

示例

--此函数给libnet扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用,不再举例。

socket.tx(ctrl, data, ip, port, flag)

功能

发送数据给对端,UDP 单次发送不要超过 1460 字节,否则很容易失败(此函数给 libnet 扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用)

参数

ctrl

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

data

参数含义:待发送的数据 或者 userdata zbuff 要发送的数据;
数据类型:string/zbuff
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

ip

参数含义:对端IP,如果是TCP应用则忽略
         如果是UDP,如果留空则用connect时候的参数
         如果是IPV4,可以是大端格式的number值
数据类型:string或者number
取值范围:暂无;
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:暂无;

port

参数含义:对端端口号,小端格式,如果是TCP应用则忽略,如果是UDP,如果留空则用connect时候的参数
数据类型:number
取值范围:暂无;
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:暂无;

flag

参数含义:发送参数,目前预留,不起作用;
数据类型:number
取值范围:暂无;
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:暂无;

返回值

local succ, full, result = socket.tx(ctrl, "123456", "xxx.xxx.xxx.xxx", xxxx)

succ

含义说明:true没有异常发生false失败了,如果false则不需要看下一个返回值了
         如果false,后续要close
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

full

含义说明:true缓冲区满了false没有满
         如果true,则需要等待一段时间或者等到socket.TX_OK消息后再尝试发送,同时忽略下一个返回值;
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

result

含义说明:true已经收到应答false没有收到应答
         之后需要接收socket.TX_OK消息 也可以忽略继续发送,直到full==true
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

示例

--此函数给libnet扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用,不再举例。

socket.rx(ctrl, buff, flag, limit)

功能

接收对端发送过来的数据,注意数据已经缓存在内核固件底层,使用本函数只是读取出来; TCP模式下,对端发送过来的一包应用数据长度,没有特别限制; UDP模式下,对端发送过来的一包应用数据长度,不要超过1460字节数据;

参数

ctrl

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:必须先创建socket对象,才能使用此接口配置参数;
参数示例:暂无;

buff

参数含义:zbuff 存放接收的数据,如果缓冲区不够大会自动扩容;
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:一个zbuff对象

flag

参数含义:接收参数,目前预留,不起作用;
数据类型:number或者nil
取值范围:暂无;
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:暂无;

limit

参数含义:接收数据长度限制,如果指定了,则只取前limit个字节
数据类型:number或者nil
取值范围:暂无;
是否必选:可选传入此参数,如果没有传入才参数,表示读取全部数据;
注意事项:暂无;
参数示例:暂无;

返回值

local succ, data_len, remote_ip, remote_port = socket.rx(ctrl)

succ

含义说明:读取结果;true表示读取成功false表示读取失败
         读取失败的情况下,后面的三个返回值没有任何意义;
         读取失败的情况下,要主动的调用libnet.close接口关闭socket,再调用socket.release释放资源
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:true

data_len

含义说明:本次读取到数据长度;第一个返回值succ为true的情况下data_len这个返回值才有意义
数据类型:number
取值范围:大于0的整数;
注意事项:暂无;
返回示例:1460

remote_ip

含义说明:对端的IP地址;第一个返回值succ为true的情况下remote_ip这个返回值才有意义
         TCP模式下,此返回值没有意义,一直为nil
         UDP模式下,此返回值才有意义,如果是IPV4,数据格式为:1byte 0x00 + 4byte地址
         如果是IPV6,数据格式为:1byte 0x01 + 16byte地址
数据类型:string或者nil
取值范围:暂无;
注意事项:暂无;
返回示例:如果是IPV4,以返回的hex值"00707D5908"为例,
         此返回值共有5个字节,其中第一个字节固定为0x00,第二个字节0x70,第三个字节0x7D,第四个字节0X59,第五个字节0x08
         转化为由4个点分十进制数组成的"ip地址"就为112.125.89.8

remote_port

含义说明:对端的端口号;第一个返回值succ为true的情况下remote_port这个返回值才有意义
         TCP模式下,此返回值没有意义,一直为0
         UDP模式下,此返回值才有意义;
数据类型:number
取值范围:暂无;
注意事项:暂无;
返回示例:12354

示例

function udp_client_receiver.proc(socket_client)
    -- 如果socket数据接收缓冲区还没有申请过空间,则先申请内存空间
    if recv_buff==nil then
        recv_buff = zbuff.create(1024)
    end

    while true do
        -- 从内核的缓冲区中读取数据到recv_buff中
        -- 如果recv_buff的存储空间不足,会自动扩容
        local result = socket.rx(socket_client, recv_buff)

        if not result then
            log.error("udp_client_receiver.proc", "socket.rx error")
            return false
        end

        -- 如果读取到了数据, used()就必然大于0, 进行处理
        if recv_buff:used() > 0 then
            log.info("udp_client_receiver.proc", "recv data len", recv_buff:used())

            -- 读取socket数据接收缓冲区中的数据,赋值给data
            local data = recv_buff:query()

            -- 将数据data通过"RECV_DATA_FROM_SERVER"消息publish出去,给其他应用模块处理
            sys.publish("RECV_DATA_FROM_SERVER", "recv from udp server: ", data)

            -- 接收到数据,通知网络环境检测看门狗功能模块进行喂狗
            sys.publish("FEED_NETWORK_WATCHDOG")

            -- 清空socket数据接收缓冲区中的数据
            recv_buff:del()
        -- 读取成功,但是读出来的数据为空,表示已经没有数据可读,可以退出循环了
        else
            break
        end
    end

    return true
end

socket.read(netc, len)

功能

读取数据(非 zbuff 版本)

netc

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

len

参数含义:限制读取数据长度,可选,不传就是读出全部;
数据类型:number
取值范围:暂无;
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:暂无;

返回值

local success, data, ip, port = socket.read(netc, 1024)

success

含义说明:true表示读取成功false表示读取失败
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

data

含义说明:读取的数据,仅当读取成功时有效;
数据类型:string
取值范围:暂无;
注意事项:暂无;
返回示例:暂无;

ip

含义说明:对方IP地址,仅当读取成功且UDP通信时有效
数据类型:string
取值范围:暂无;
注意事项:暂无;
返回示例:暂无;

port

含义说明:对方端口,仅当读取成功且UDP通信时有效
数据类型:number
取值范围:暂无;
注意事项:暂无;
返回示例:暂无;

示例

-- 本函数于2024.4.8添加, 用于简易读取不大的数据
-- 请优先使用socket.rx函数, 本函数主要用于固件不含zbuff库时的变通调用
local ok, data = socket.read(netc, 1500)
if ok and data > 0 then
    log.info("读取到的数据", data)
end

socket.wait(ctrl)

功能

等待新的 socket 消息,在连接成功和发送数据成功后,使用一次将 network 状态转换到接收新数据

(此函数给 libnet 扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用)

参数

ctrl

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

返回值

local succ, result = socket.wait(ctrl)

succ

含义说明:true没有异常发生false失败了
         如果false则不需要看下一个返回值了,如果false,后续要close
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

result

含义说明:true有新的数据需要接收false没有数据,之后需要接收socket.EVENT消息
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

示例

--此函数给libnet扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用,不再举例。

socket.listen(ctrl)

功能

作为服务端开始监听(此函数给 libnet 扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用)

参数

ctrl

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

返回值

local succ, result = socket.listen(ctrl)

succ

含义说明:true没有异常发生false失败了
         如果false则不需要看下一个返回值了,如果false,后续要close
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

result

含义说明:true已经connectfalse没有connect,之后需要接收socket.ON_LINE消息
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

示例

--此函数给libnet扩展库使用,用户不用深入了解,也不建议在应用脚本中去使用,不再举例。

socket.accept(ctrl)

功能

作为服务端接收到一个新的客户端,注意,如果是类似 W5500 的硬件协议栈不支持 1 对多,则不需要第二个参数

参数

ctrl

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

返回值

local succ, new_netc = socket.accept(ctrl, cb)

succ

含义说明:true没有异常发生false失败了
         如果false则不需要看下一个返回值了,如果false,后续要close
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

new_netc

含义说明:或者 nil 如果支持1对多,则会返回新的ctrl,自动create,如果不支持则返回nil
数据类型:userdata
取值范围:暂无;
注意事项:暂无;
返回示例:暂无;

示例

function ipv6task(d1Name, txqueue, rxtopic)
    -- 本地监听的端口
    local port = 14000

    local rx_buff = zbuff.create(1024)
    local tx_buff = zbuff.create(4 * 1024)
    local netc = socket.create(nil, d1Name)
    socket.config(netc, 14000)
    log.info("任务id", d1Name)

    while true do
        log.info("socket", "开始监控")
        local result = libnet.listen(d1Name, 0, netc)
        if result then
            log.info("socket", "监听成功")
            result = socket.accept(netc, nil)    --只支持1对1
            if result then
                log.info("客户端连上了")
            end
        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("客户端断开了", succ, param, ip, port)
                break
            end
            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)
            log.info("libnet", "wait", result, param, param2)
            if not result then
                -- 网络异常了
                log.info("socket", "客户端断开了", result, param)
                break
            elseif #txqueue > 0 then
                local force_close = false
                while #txqueue > 0 do
                    local data = table.remove(txqueue, 1)
                    if not data then
                        break
                    end
                    log.info("socket", "上行数据长度", #data)
                    if data == "close" then
                        --sys.wait(1000)
                        force_close = true
                        break
                    end
                    tx_buff:del()
                    tx_buff:copy(nil, data)
                    result,param = libnet.tx(d1Name, 15000, netc, tx_buff)
                    log.info("libnet", "发送数据的结果", result, param)
                    if not result then
                        log.info("socket", "数据发送异常", result, param)
                        break
                    end
                end
                if force_close then
                    break
                end
            end
        end
        log.info("socket", "连接已断开,继续下一个循环")
        libnet.close(d1Name, 5000, netc)
        -- log.info(rtos.meminfo("sys"))
        sys.wait(50)
    end
end

sys.taskInit(ipv6test)

socket.state(ctrl)

功能

获取 socket 当前状态

参数

ctrl

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

返回值

local state, str = socket.state(ctrl)

state

含义说明:如果支持1对多,则会返回新的ctrl,自动create,如果不支持则返回nil
数据类型:number或者nil
取值范围:暂无;
注意事项:暂无;
返回示例:暂无;

_str _

含义说明:输入参数正确的情况下,返回状态的中文描述,否则返回nil
数据类型:string/nil
取值范围:暂无;
注意事项:暂无;
返回示例:暂无;

示例

local state, str = socket.state(ctrl)
log.info("state", state, str)
state    0    "硬件离线",
        1    "离线",
        2    "等待DNS",
        3    "正在连接",
        4    "正在TLS握手",
        5    "在线",
        6    "在监听",
        7    "正在离线",
        8    "未知"

socket.release(ctrl)

功能

主动释放掉 socket_ctrl

参数

ctrl

参数含义:使用socket.create接口创建的socket对象
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:必须先创建socket对象,才能使用此接口配置参数;
参数示例:暂无;

返回值

nil

示例

-- 释放后就不能再使用了
        -- 如果存在socket client对象
        if socket_client then
            -- 关闭socket client连接
            libnet.close(TASK_NAME, 5000, socket_client)

            -- 释放socket client对象,释放后就不能再使用了
            socket.release(socket_client)
            socket_client = nil
        end

socket.setDNS(adapter_index, dns_index, ip)

功能

设置某个网卡使用的DNS服务器IP地址;

内核固件中有4个位置可以存储DNS服务器IP地址; 对应的位置索引为本接口的dns_index参数,取值范围为1到4; 所以最多同时存在4个DNS服务器IP地址;

参数

adapter_index

参数含义:适配器序号,前面章节网络适配器常量里面的适配器,
         如果不填,会选择最后一个注册的适配器;
数据类型:number或者nil
取值范围:本文常量详解章节中的所有网络适配器编号常量,例如:socket.LWIP_GP
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:socket.LWIP_GP

dns_index

参数含义:dns服务器序号,从1开始;
数据类型:number
取值范围:14
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:1表示存储dns服务器ip地址的第一个位置

ip

参数含义:dns服务器ip地址
数据类型:string
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:"114.114.114.114"

返回值

local result = socket.setDNS(adapter_index, dns_index, ip)

result

含义说明:成功返回true,失败返回false
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:暂无;

示例

local function ip_ready_func(ip, adapter)    
    -- 在位置1和2设置自定义的DNS服务器ip地址:
    -- "223.5.5.5",这个DNS服务器IP地址是阿里云提供的DNS服务器IP地址;
    -- "114.114.114.114",这个DNS服务器IP地址是国内通用的DNS服务器IP地址;
    -- 可以加上以下两行代码,在自动获取的DNS服务器工作不稳定的情况下,这两个新增的DNS服务器会使DNS服务更加稳定可靠;
    -- 如果使用专网卡,不要使用这两行代码;
    -- 如果使用国外的网络,不要使用这两行代码;
    socket.setDNS(adapter, 1, "223.5.5.5")
    socket.setDNS(adapter, 2, "114.114.114.114")

    if adapter == socket.LWIP_GP then
        log.info("netdrv_4g.ip_ready_func", "IP_READY", socket.localIP(socket.LWIP_GP))
    end
end

sys.subscribe("IP_READY", ip_ready_func)

socket.sslLog(log_level)

功能

设置 SSL 的 log 等级

参数

log_level

参数含义:内核固件中的ssl功能的log等级
         0:不打印
         1:只打印错误和警告
         2:打印大部分日志信息
         33以上:打印最详细的日志信息
数据类型:number
取值范围:大于等于0的整数;
是否必选:必须传入此参数;
注意事项:使用位置无特别要求,实时生效,如果要抓取完整的日志,可以在使用socket网络应用之前进行设置
         打印过多信息会造成内存碎片化,仅在调试时有需要再打开,量产软件中不要打开;
参数示例:3

返回值

nil

示例

--[[
SSL/TLS log级别说明
0不打印
1只打印错误和警
2大部分info
3及3以上详细的debug

过多的信息可能会造成内存碎片化
]]
-- 打印大部分info日志
socket.sslLog(2)

socket.adapter(index)

功能

查看网卡适配器的联网状态

参数

index

参数含义:表示网卡编号;
         adapter_id是number类型时,表示某一种网卡;
         adapter_id是nil类型时,表示:从第一个网卡(socket.LWIP_GPnumber值为1)开始,
         遍历全部网卡,遍历过程中如果发现IP_READY的网卡,则终止遍历;
数据类型:number或者nil
取值范围:number类型时,本文常量详解章节中的所有网络适配器编号常量;
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:例如socket.LWIP_STA表示内置的WiFi设备模式网卡

返回值

local isReady,index = socket.adapter()

isReady

含义说明:被查看的适配器是否IP READY,true表示已经准备好可以联网了,false暂时不可以联网
数据类型:boolean
取值范围:true或false
注意事项:暂无;
返回示例:true或者false

index

含义说明:当查询结束后,最后一个被查看的适配器序号;
数据类型:number
取值范围:本文常量详解章节中的所有网络适配器编号常量;
注意事项:暂无;
返回示例:例如socket.LWIP_GP表示内置的4G网卡

示例

-- 以Air8000为例;
-- 如果Air8000当前状态下,4G网卡socket.LWIP_GP和WiFi设备模式网卡socket.LWIP_STA都已经准备就绪
-- 则下面这一行代码
-- 返回值is_ready为true
-- 返回值index为socket.LWIP_STA
local is_ready, index = socket.adapter(socket.LWIP_STA)


-- 以Air8000为例;
-- 如果Air8000当前状态下,4G网卡socket.LWIP_GP和WiFi设备模式网卡socket.LWIP_STA都已经准备就绪
-- 则下面这一行代码
-- 返回值is_ready为true
-- 返回值index为socket.LWIP_GP
-- 因为socket.LWIP_GP是遍历的第一个网卡,而且这个网卡已经准备就绪,所有遍历到socket.LWIP_GP提前结束
local is_ready, index = socket.adapter()

socket.remoteIP(ctrl)

功能

获取对端 ip

参数

ctrl

参数含义:socket.create得到的ctrl
数据类型:userdata
取值范围:暂无;
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:暂无;

返回值

local ip1,ip2,ip3,ip4 = socket.remoteIP(ctrl)

ip1

含义说明:IP1,如果为nil,则表示没有获取到IP地址
数据类型:string
取值范围:暂无;
注意事项:暂无;
返回示例:"10.39.154.32"

ip2

含义说明:IP2,如果为nil,则表示没有IP2
数据类型:string
取值范围:暂无;
注意事项:暂无;
返回示例:"10.39.154.32"

ip3

含义说明:IP3,如果为nil,则表示没有IP3
数据类型:string
取值范围:暂无;
注意事项:暂无;
返回示例:"10.39.154.32"

ip4

含义说明:IP4,如果为nil,则表示没有IP4
数据类型:string
取值范围:暂无;
注意事项:暂无;
返回示例:"10.39.154.32"

示例

-- 注意: ,必须在接收到socket.ON_LINE消息之后才可能获取到,最多返回4个IP。
-- socket.connect里如果remote_port设置成0,则当DNS完成时就返回socket.ON_LINE消息
local ip1,ip2,ip3,ip4 = socket.remoteIP(ctrl)

socket.dft(id)

功能

读取或者设置当前使用的默认网卡;

参数

id

参数含义:表示网卡编号;
         adapter_id是number类型时,表示设置默认网卡为adapter_id所表示的网卡
         adapter_id是nil类型时,表示读取当前使用的默认网卡;
数据类型:number或者nil
取值范围:number类型时,本文常量详解章节中的所有网络适配器编号常量;
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:例如socket.LWIP_STA表示内置的WiFi设备模式网卡

返回值

local id, last_id = socket.dft()

id

含义说明:默认适配器编号;
数据类型:number
取值范围:本文常量详解章节中的所有网络适配器编号常量;
注意事项:暂无;
返回示例:例如socket.LWIP_STA表示内置的WiFi设备模式网卡

last_id

含义说明:表示最后一个注册的网卡对应的编号;
数据类型:number
取值范围:本文常量详解章节中的所有网络适配器编号常量;
注意事项:暂无;
返回示例:例如socket.LWIP_AP表示内置的WiFi路由模式网卡

示例

-- 本函数于 2025.1.6 新增
-- 获取当前默认适配器编号
local id = socket.dft()

-- 设置默认适配器编号
socket.dft(socket.LWIP_ETH)

-- 获取当前默认适配器编号, 及最后一个注册的适配器编号
local id, last_id = socket.dft()
log.info("当前默认适配器编号", id, "最后一个注册的适配器编号", last_id)

-- 1. 当前的默认网卡, 可以获取, 可以设置, 就是socket.dft(id)的第一个参数, 也是第一个返回值
-- 2. 最后注册的网卡, 可以获取, 不支持设置, 就是socket.dft()的第二个返回值

socket.sntp(sntp_server,index)

功能

sntp 时间同步

参数

sntp_server

参数含义:sntp服务器地址 选填;
数据类型:string或者table
取值范围:暂无;
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:暂无;

index

参数含义:上网使用的网卡ID
数据类型:number或者nil
取值范围:number类型时,取值范围参考socket api中的常量详解
是否必选:可选传入此参数;
注意事项:如果没有传入此参数,内核固件会自动选择当前时间点其他功能模块设置的默认网卡;
          除非你socket请求时,一定要使用某一种网卡,才设置此参数;
         如果没什么特别要求,不要设置此参数,使用系统中设置的默认网卡即可 
          一般来说,LuatOS的网络应用demo中都会有netdrv_device功能模块设置默认网卡
         所以建议不要设置此参数,直接使用netdrv_device设置的默认网卡就行
参数示例:socket.LWIP_GP表示使用4G网卡

返回值

nil

示例

socket.sntp()
--socket.sntp("ntp.aliyun.com") --自定义sntp服务器地址
--socket.sntp({"ntp.aliyun.com","ntp1.aliyun.com","ntp2.aliyun.com"}) --sntp自定义服务器地址
--socket.sntp(nil, socket.ETH0) --sntp自定义适配器序号
sys.subscribe("NTP_UPDATE", function()
    log.info("sntp", "time", os.date())
end)
sys.subscribe("NTP_ERROR", function()
    log.info("socket", "sntp error")
    socket.sntp()
end)

socket.ntptm()

功能

网络对时后的时间戳(ms 级别)

参数

nil

返回值

local tm = socket.ntptm()

table类型,表示包含时间信息的数据;

此参数的内容格式说明如下

{
    "sms":0,   -- number类型
              -- 系统启动时刻与1900.1.1 0:0:0的毫秒偏移量
    "tms":365,  -- number类型
                -- 当前毫秒数
    "tsec":0,  -- number类型
               -- 当前秒数,从1900.1.1 0:0:0 开始算, UTC时间
    "lms":365, -- number类型
               -- 本地毫秒数计数器,基于mcu.tick64()
    "ndeley":0, -- number类型
                -- 网络延时平均值,单位毫秒
    "lsec":0, -- number类型
              -- 本地秒数计数器,基于mcu.tick64()
    "ssec":0 -- number类型
               -- 系统启动时刻与1900.1.1 0:0:0的秒数偏移量     
}

示例

-- 本API于 2023.11.15 新增
-- 注意, 本函数在执行socket.sntp()且获取到NTP时间后才有效
-- 而且是2次sntp之后才是比较准确的值
-- 网络波动越小, 该时间戳越稳定
local tm = socket.ntptm()

-- 对应的table包含多个数据, 均为整数值

-- 标准数据
-- tsec 当前秒数,从1900.1.1 0:0:0 开始算, UTC时间
-- tms  当前毫秒数
-- vaild 是否有效, true 或者 nil

-- 调试数据, 调试用,一般用户不用管
-- ndelay 网络延时平均值,单位毫秒
-- ssec 系统启动时刻与1900.1.1 0:0:0的秒数偏移量
-- sms 系统启动时刻与1900.1.1 0:0:0的毫秒偏移量
-- lsec 本地秒数计数器,基于mcu.tick64()
-- lms 本地毫秒数计数器,基于mcu.tick64()

log.info("tm数据", json.encode(tm))
log.info("时间戳", string.format("%u.%03d", tm.tsec, tm.tms))

socket.sntp_port(port)

功能

设置 SNTP 服务器的端口号

参数

Port

参数含义:port 端口号, 默认123
数据类型:number
取值范围:暂无;
是否必选:可选传入此参数;
注意事项:暂无;
参数示例:123

返回值

含义说明:返回当前的端口号;
数据类型:number
取值范围:暂无;
注意事项:暂无;
返回示例:暂无;

示例

-- 本函数于2024.5.17新增
-- 大部分情况下不需要设置NTP服务器的端口号,默认123即可
local current_port = socket.sntp_port(123)
log.info("Current SNTP port:", current_port)

五、产品支持说明

支持 LuatOS 开发的所有产品都支持 socket 核心库。