network 模块 编程使用说明

本网络模块支持:HTTP / TCP / UDP / MQTT 服务器,以及一个带自动重连的 MQTT 客户端

local network = require("network")

1. 总览

模块级函数(用 network.xxx 调用)

函数作用返回
network.create(port, cb)创建基础 HTTP 服务(旧接口)句柄对象
network.start_http_server(port, cb)启动 HTTP / WebSocket 服务器句柄对象
network.start_tcp_server(port, cb)启动 TCP 服务器句柄对象
network.start_udp_server(port, cb)启动 UDP 服务器句柄对象
network.start_mqtt_server(port, cb)启动 MQTT Broker(服务端)句柄对象
network.start_mqtt_cli(host, user, pass, port, id, will_topic, will_msg, cb)启动 MQTT 客户端(自动重连)句柄对象

句柄方法(用 handle:xxx() 调用)

上面每个函数都返回一个句柄对象,可调用下列方法:

方法适用作用
handle:poll(timeout_ms)全部驱动一次事件循环,必须在主循环里反复调用
handle:free()全部关闭连接、释放定时器与资源(见"生命周期")
handle:send(data, len)服务器向该服务器所有已连接客户端广播数据
handle:readfile(path)全部读取一个文件,返回内容字符串(失败返回 nil)
handle:mqttc_pub(topic, payload [, qos])MQTT 客户端发布消息,返回 bool 表示是否发出
handle:mqttc_sub(topic [, qos])MQTT 客户端订阅主题,返回 bool 表示是否发出
handle:mqttc_connected()MQTT 客户端是否已连接并就绪(收到 CONNACK)
handle:mqttc_ping()MQTT 客户端发送 PING,返回 bool 表示是否发出
重要:所有服务/客户端都靠 handle:poll() 驱动。创建句柄后,必须在你的主循环里
周期性调用 poll,否则不会有任何网络活动、回调也不会触发。

2. 主循环范式

所有例子都遵循同一个骨架:创建句柄 → 循环 poll → 退出时 free

local network = require("network")

local srv = network.start_tcp_server(9000, function(req)
    -- 处理逻辑,见后文
    return "ok"
end)

-- 主循环
while running do
    srv:poll(100)     -- 每次最多阻塞 100ms 处理事件
end

srv:free()            -- 退出时释放

poll(timeout_ms)timeout_ms 是本次事件处理的最长等待毫秒数。常用 10~100ms。


3. HTTP / WebSocket 服务器

创建

local http = network.start_http_server(port, callback)
  • port:监听端口(数字),内部监听 http://0.0.0.0:<port>
  • callback(req):每次收到 HTTP 请求或 WebSocket 消息时调用,返回值作为响应体

回调收到的 req

HTTP 请求时:

字段说明
req.is_httptrue
req.url请求 URI(路径部分)
req.method请求方法,如 "GET" / "POST"
req.query查询字符串(URL 里 ? 后面的部分)
req.dataquery(当前实现二者相同)

WebSocket 消息时:

字段说明
req.is_wstrue
req.data客户端发来的消息内容

回调返回的字符串:HTTP 情况下作为 200 响应正文返回;WebSocket 情况下作为文本帧回发给该客户端。返回空串时 HTTP 会回一个空白页。

内置路由说明

  • /ws:WebSocket 升级端点。客户端连到 ws://<ip>:<port>/ws 即可建立 WebSocket。
  • /admin/*:内置静态文件服务,根目录为 /admin/,带一周缓存。这类请求不会进入你的回调.
  • 其余路径:进入你的回调。

例子

local network = require("network")

local http = network.start_http_server(8080, function(req)
    if req.is_ws then
        -- WebSocket:回声
        return "echo: " .. req.data
    end

    -- 普通 HTTP
    if req.url == "/hello" then
        return "<h1>Hello from SimFAS</h1>"
    elseif req.url == "/api/status" then
        return '{"status":"ok"}'
    end
    return "<html><body>path=" .. req.url ..
           " method=" .. req.method .. "</body></html>"
end)

while true do
    http:poll(50)
end

主动向所有已连接的 WebSocket / HTTP 长连接广播(例如推送):

http:send("push message", #("push message"))

4. TCP 服务器

创建

local tcp = network.start_tcp_server(port, callback)
  • port:监听端口,内部监听 tcp://0.0.0.0:<port>
  • callback(req):收到数据时调用。req.data 为收到的字节;返回的字符串会被发回给该连接。

回调收到的 req

字段说明
req.data本次收到的数据(当前实现为该连接接收缓冲的全部内容)
注意:req.data 是接收缓冲里的累积数据。若你的协议有粘包/分包,需要在用户脚本自行按协议
切分。回调结束后接收缓冲会被清空。

例子

local network = require("network")

local tcp = network.start_tcp_server(9000, function(req)
    print("recv:", req.data)
    -- 简单回声
    return "ACK:" .. req.data
end)

while true do
    tcp:poll(50)
end

向所有 TCP 客户端广播:

local msg = "broadcast\n"
tcp:send(msg, #msg)

5. UDP 服务器

创建

local udp = network.start_udp_server(port, callback)
  • port:监听端口,内部监听 udp://0.0.0.0:<port>
  • callback(req):收到数据报时调用。req.data 为该数据报内容;返回字符串会回发给来源地址。

回调收到的 req

字段说明
req.data本次收到的数据报内容

例子

local network = require("network")

local udp = network.start_udp_server(9001, function(req)
    print("udp recv:", req.data)
    return "pong"
end)

while true do
    udp:poll(50)
end

6. MQTT 服务器(Broker)

创建

local broker = network.start_mqtt_server(port, callback)
  • port:监听端口,内部监听 mqtt://0.0.0.0:<port>
  • callback(msg):当有客户端 PUBLISH 消息时调用(消息已自动转发给订阅方,回调用于你旁路观察/记录)。

内部已实现基本的 SUBSCRIBE 管理、PUBLISH 按主题匹配转发、PINGREQ/PINGRESP 心跳。

回调收到的 msg

字段说明
msg.topic发布的主题
msg.data发布的消息内容

例子

local network = require("network")

local broker = network.start_mqtt_server(1883, function(msg)
    print(string.format("[broker] %s -> %s", msg.topic, msg.data))
    -- 这里的返回值不会作为 MQTT 响应,仅回调
end)

while true do
    broker:poll(50)
end

7. MQTT 客户端(带自动重连)

这是本模块功能最完整、最需要注意用法的部分。客户端内部有一个 5 秒重连定时器:
连接断开后会自动尝试重连,无需你手动处理。

创建

local mqttc = network.start_mqtt_cli(
    host,          -- 1) broker 主机名/IP,字符串
    user,          -- 2) 用户名,字符串(无则传 "")
    pass,          -- 3) 密码,字符串(无则传 "")
    port,          -- 4) broker 端口,数字,如 1883
    client_id,     -- 5) 客户端 ID,字符串
    will_topic,    -- 6) 遗嘱主题,字符串(无则传 "")
    will_message,  -- 7) 遗嘱消息,字符串(无则传 "")
    callback       -- 8) 事件回调函数
)
八个参数全部必填不用的字符串
参数请传空串 "",不要省略。连接使用 clean session = true,遗嘱 QoS 为 0。

回调收到的 ev

回调按连接状态变化和消息到达触发,ev.event 是事件类型字符串:

ev.event触发时机额外字段
"open"已连接并收到 CONNACK,此后才可收发
"message"收到订阅的消息ev.topic, ev.data
"close"连接关闭(之后会自动重连)
"error"连接出错
"connect"底层 TCP 连接事件
"timer"5 秒重连定时器触发且当前已连接时

收发方法

方法返回说明
mqttc:mqttc_connected()bool是否已就绪(连接存在且收到 CONNACK)
mqttc:mqttc_sub(topic [, qos])bool订阅;qos 可选(0~3,默认 1)
mqttc:mqttc_pub(topic, payload [, qos])bool发布;qos 可选(0~3,默认 1)
mqttc:mqttc_ping()bool发送 PING

关键防呆:mqttc_pub / mqttc_sub / mqttc_ping 在**未连接或未就绪时会直接返回
false 而不发送**,不会崩溃。因此:

  • 不要在连上之前每秒盲发。正确做法是收到 "open" 事件后再订阅/发布。
  • 每次调用都应检查返回值,false 表示这次没发出去(连接还没就绪)。
  • 订阅是幂等动作:在每次收到 "open" 时重新订阅即可,不需要定时轮询式地 sub。

完整例子

local network = require("network")

local mqttc  -- 前置声明,便于回调里引用

mqttc = network.start_mqtt_cli(
    "broker.example.com",  -- host
    "myuser",              -- user
    "mypass",              -- pass
    1883,                  -- port
    "sim-client-001",      -- client id
    "clients/001/will",    -- will topic
    "offline",             -- will message
    function(ev)
        if ev.event == "open" then
            print("MQTT connected, subscribing...")
            -- 连接就绪后订阅(重连后会再次收到 open,自动重订阅)
            mqttc:mqttc_sub("cmd/led", 1)
            mqttc:mqttc_sub("cmd/relay", 1)

        elseif ev.event == "message" then
            print(string.format("MSG %s = %s", ev.topic, ev.data))
            -- 举例:根据主题处理
            if ev.topic == "cmd/led" then
                -- do something with ev.data
            end

        elseif ev.event == "close" then
            print("MQTT disconnected (will auto-reconnect in 5s)")

        elseif ev.event == "error" then
            print("MQTT error")
        end
    end
)

-- 主循环
local counter = 0
while true do
    mqttc:poll(100)

    -- 周期性发布,注意判断是否就绪 / 检查返回值
    counter = counter + 1
    if counter % 10 == 0 then                 -- 大约每 1 秒(poll 100ms)
        if mqttc:mqttc_connected() then
            local ok = mqttc:mqttc_pub("telemetry/temp", "25.3", 0)
            if not ok then
                print("publish skipped: not ready")
            end
        end
    end
end

-- mqttc:free()  -- 退出前调用

8. 通用方法

handle:send(data, len)

向该句柄管理的所有连接广播:WebSocket 连接以文本帧发送,其余连接以原始字节发送。
常用于 HTTP/WebSocket 服务器主动推送、TCP 服务器群发。

local payload = "hello all"
http:send(payload, #payload)

handle:readfile(path)

读取文件内容,成功返回字符串,失败返回 nil

local content = http:readfile("/etc/config.json")
if content then
    print(content)
end

9. 生命周期与释放

  • 定时器与连接:MQTT 客户端内部的 5 秒重连定时器,以及所有连接,都由句柄持有。
    调用 handle:free()(或对象被 回收触发 __gc)时,会一并关闭连接、释放定时器,
    不会泄漏。
  • 立即停止:若依赖 GC 自动回收,定时器和连接要等到该对象被 GC 才释放,在此之前
    重连定时器仍在运行、仍会尝试连接。想立刻停掉,请显式调用 handle:free()
  • free() 可安全多次调用(内部有防二次释放保护),但释放后不要再对该句柄调用任何方法。
mqttc:free()   -- 立即断开、停止重连、释放资源

10. 常见注意事项

  1. 必须持续 poll:没有 poll 就没有网络活动。把它放进你的主循环。
  2. MQTT 发送先判就绪:未连上时 pub/sub/ping 返回 false 且不发送,别把它当成功。
  3. 订阅放在 "open" 回调里:重连后会再次收到 "open",在那里重订阅最稳,不要定时盲发 sub。
  4. 回调里持有句柄:如例子所示,先 local mqttc 前置声明,再赋值,回调里就能引用到它。
  5. 字符串参数别省略:start_mqtt_cli 的用户名/密码/遗嘱等即使不用也要传 ""
  6. send 是广播:它发给该句柄下所有连接,不是发给单个客户端。