Skip to content

tcp_server — TCP 服务器 ​

模块名:tcp_server

在设备上监听一个 TCP 端口,接受外部客户端连入并收发数据。适合把设备当作数据服务器,让上位机、组态软件或多台采集端主动连过来。

对象方法用冒号写法(s:send(...)),库表里也注册了同名函数,tcp_server.send(s, ...) 等价。

安全提示:本模块不提供任何认证或访问控制。任何能访问到该端口的主机都可以连接并发送数据,而回调里通常会直接写 PLC 寄存器。部署到公网或不可信网段前,需在回调里自行做身份校验,或用防火墙限制来源。

TCP 客户端 vs 服务器:谁先发起连接决定组网方案

函数总览 ​

库级函数 ​

函数作用
tcp_server.creat([port, max_sockets, buffer_size])创建服务器对象

对象方法 ​

方法作用
s:reg注册事件回调
s:start启动监听
s:send向指定客户端发送数据
s:close断开指定客户端

常量 ​

常量回调入参说明
tcp_server.CONNECTED(sock, ip)有客户端连入
tcp_server.DISCONNECTED(sock)客户端断开
tcp_server.RECVDATA(sock, data, ip, port)收到数据

tcp_server.creat ​

lua
tcp_server.creat([port, max_sockets, buffer_size]) --> server

创建 TCP 服务器对象。只创建不监听,要调 start 才开始监听。

参数

#参数类型必填默认值说明
1portinteger否5000监听端口
2max_socketsinteger否1最大同时连接数。默认只允许 1 个客户端,多客户端场景必须显式加大
3buffer_sizeinteger否1024单个连接的接收缓冲区字节数

返回值

类型说明
userdata服务器对象。必须用变量长期持有,被 GC 回收会停止监听

示例

lua
-- 监听 8080,最多 5 个客户端,缓冲区 2KB
tcps = tcp_server.creat(8080, 5, 2048)

s:reg ​

lua
s:reg(event, callback)

注册事件回调。要在 start 之前注册好。

参数

#参数类型必填默认值说明
1eventinteger是—tcp_server.CONNECTED / DISCONNECTED / RECVDATA
2callbackfunction是—回调函数。类型不是 function 会直接报错

返回值

无返回值。

示例

lua
tcps:reg(tcp_server.CONNECTED, function(sock, ip)
    print("客户端连入:", ip, "socket =", sock)
end)

tcps:reg(tcp_server.RECVDATA, function(sock, data, ip, port)
    print(string.format("来自 %s:%d 的 %d 字节", ip, port, #data))
    tcps:send(sock, "ACK\n")
end)

tcps:reg(tcp_server.DISCONNECTED, function(sock)
    print("客户端断开:", sock)
end)

说明

  • 每种事件只保留最后一次注册的回调,所有客户端共用同一个回调,靠 sock 区分是谁。

s:start ​

lua
s:start()

启动监听线程。本函数立即返回。

参数

无参数。

返回值

无返回值。


s:send ​

lua
s:send(sock, data) --> integer

向指定客户端发送数据。

参数

#参数类型必填默认值说明
1sockinteger是—目标客户端的 socket,来自回调的第一个参数
2datastring是—要发送的数据,可为二进制

返回值

类型说明
integer实际发送的字节数。data 为空串时直接返回 0

说明

  • 要广播给所有客户端需要自己维护一张 sock 列表。

s:close ​

lua
s:close(sock)

主动断开指定客户端,并从内部连接表里移除它。

参数

#参数类型必填默认值说明
1sockinteger是—要断开的客户端 socket

返回值

无返回值。


s:is_linked / s:set_timeout / s:set_transparent —— tcp_server 不提供这三个方法 ​

这三个名字只在 tcp_client 上有实现,但三个模块的函数实现各自独立——tcp_server 没有注册它们,调用会直接报错(本模块的 s:close(sock) 是另一回事,它真实存在,见上一节):

lua
tcps = tcp_server.creat(8080, 5)
tcps:is_linked()      --> 报错:attempt to call method 'is_linked' (a nil value)

参数

  • is_linked —— 不存在。服务器是被动接受连接的一方,没有单一的"连接状态"可查;哪些客户端在线要自己在 CONNECTED / DISCONNECTED 回调里维护一张 sock 表(见下方常见用法)
  • set_timeout —— 不存在。tcp_client:set_timeout 设置的是客户端底层 socket 的接收超时,服务器的收发线程由监听逻辑内部管理,不暴露这个开关。给某个客户端发包时,s:send 的返回值就是底层发送的结果:对端已断开时返回负数而不是 0,判断发包是否成功要用 == #data
  • set_transparent —— 不存在。串口透传是把客户端连接挂到串口上,服务器形态没有对应的透传实现

返回值

以 s:xxx() 形式调用这三个名字一律报错,没有返回值。

示例

lua
-- 服务器侧的"在线判断":用回调维护连接表,而不是查方法
tcps = tcp_server.creat(8080, 5)
online = {}
tcps:reg(tcp_server.CONNECTED, function(sock, ip) online[sock] = ip end)
tcps:reg(tcp_server.DISCONNECTED, function(sock) online[sock] = nil end)
tcps:start()

timer.start(2000, function()
    local n = 0
    for _ in pairs(online) do n = n + 1 end
    print("当前在线客户端:", n)
end)

常见用法 ​

多客户端服务器,维护连接表并广播

lua
tcps = tcp_server.creat(8080, 5, 2048)
clients = {}

tcps:reg(tcp_server.CONNECTED, function(sock, ip)
    clients[sock] = ip
end)

tcps:reg(tcp_server.DISCONNECTED, function(sock)
    clients[sock] = nil
end)

tcps:reg(tcp_server.RECVDATA, function(sock, data)
    if data:match("^AUTH ") then                -- 简单的接入校验
        if data ~= "AUTH secret\n" then tcps:close(sock) end
    end
end)

tcps:start()

-- 每 2 秒把温度广播给所有已连接客户端
timer.start(2000, function()
    local msg = string.format("T=%.2f\n", plc.df(100))
    for sock in pairs(clients) do
        tcps:send(sock, msg)
    end
end)

注意事项 ​

  • max_sockets 默认是 1,忘记设置会导致第二个客户端连不上。
  • 无任何认证机制,回调里如果直接写 PLC 寄存器,等于把控制权开放给能访问该端口的任何主机。
  • RECVDATA 回调参数顺序是 (sock, data, ip, port),CONNECTED 是 (sock, ip)。
  • TCP 是字节流,一次回调收到的不一定是完整一帧,自定义协议要自己分帧。
  • 服务器对象要长期持有,否则被 GC 后监听停止。

例程 ​

每个例程都是可直接下载运行的完整脚本:creat 建对象、reg 注册回调、start 启动监听,脚本注册完即结束。 API 只用本页已文档化的函数。统一模板「场景 / 完整代码 / 要点 / 验证方法 / 注意事项」。

例程 1:温度查询服务器(收到命令回温度) ​

适用:LP3201 / LP3202 / LP3203 / LP3222 / LP3232 / LP3288 / 软PLC模拟器 | 依赖模块:tcp_server plc | 难度:★☆☆

场景:上位机/组态软件主动连设备 8080 端口,发 GET_TEMP 命令,设备回当前温度。单个客户端即可。

完整代码(可直接下载运行):

lua
tcps = tcp_server.creat(8080)          -- 默认 max_sockets=1,只接受 1 个客户端
tcps:reg(tcp_server.CONNECTED, function(sock, ip)
    print("客户端连入:", ip)
end)
tcps:reg(tcp_server.RECVDATA, function(sock, data, ip, port)
    if data:match("^GET_TEMP") then
        tcps:send(sock, string.format("T=%.2f\n", plc.df(100)))
    end
end)
tcps:start()

要点:

  • creat 只创建不监听,必须 start 才开始;返回对象要长期持有。
  • RECVDATA 回调参数顺序 (sock, data, ip, port),CONNECTED 是 (sock, ip)。
  • 服务器没有 is_linked,无法"查连接状态",在线判断靠自己维护 sock 表。

验证方法:

  1. 用网络调试助手连 设备IP:8080,发 GET_TEMP,应收到 T=25.30 之类。
  2. 日志打印客户端连入 IP。

注意事项:

  • 默认 max_sockets=1,第二个客户端连不上——多客户端必须 tcp_server.creat(8080, 5)。
  • TCP 是字节流,一次回调不一定是完整一帧,自定义协议要自己分帧。

其余例程(各自一页) ​

LPPLC 工业物联网云平台 · 软硬件与云平台全栈自研