主题
tcp_server — TCP 服务器
模块名:tcp_server
在设备上监听一个 TCP 端口,接受外部客户端连入并收发数据。适合把设备当作数据服务器,让上位机、组态软件或多台采集端主动连过来。
对象方法用冒号写法(s:send(...)),库表里也注册了同名函数,tcp_server.send(s, ...) 等价。
安全提示:本模块不提供任何认证或访问控制。任何能访问到该端口的主机都可以连接并发送数据,而回调里通常会直接写 PLC 寄存器。部署到公网或不可信网段前,需在回调里自行做身份校验,或用防火墙限制来源。
函数总览
库级函数
| 函数 | 作用 |
|---|---|
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 才开始监听。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | port | integer | 否 | 5000 | 监听端口 |
| 2 | max_sockets | integer | 否 | 1 | 最大同时连接数。默认只允许 1 个客户端,多客户端场景必须显式加大 |
| 3 | buffer_size | integer | 否 | 1024 | 单个连接的接收缓冲区字节数 |
返回值
| 类型 | 说明 |
|---|---|
| userdata | 服务器对象。必须用变量长期持有,被 GC 回收会停止监听 |
示例
lua
-- 监听 8080,最多 5 个客户端,缓冲区 2KB
tcps = tcp_server.creat(8080, 5, 2048)s:reg
lua
s:reg(event, callback)注册事件回调。要在 start 之前注册好。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | event | integer | 是 | — | tcp_server.CONNECTED / DISCONNECTED / RECVDATA |
| 2 | callback | function | 是 | — | 回调函数。类型不是 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向指定客户端发送数据。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | sock | integer | 是 | — | 目标客户端的 socket,来自回调的第一个参数 |
| 2 | data | string | 是 | — | 要发送的数据,可为二进制 |
返回值
| 类型 | 说明 |
|---|---|
| integer | 实际发送的字节数。data 为空串时直接返回 0 |
说明
- 要广播给所有客户端需要自己维护一张
sock列表。
s:close
lua
s:close(sock)主动断开指定客户端,并从内部连接表里移除它。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | sock | integer | 是 | — | 要断开的客户端 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,判断发包是否成功要用== #dataset_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表。
验证方法:
- 用网络调试助手连
设备IP:8080,发GET_TEMP,应收到T=25.30之类。 - 日志打印客户端连入 IP。
注意事项:
- 默认
max_sockets=1,第二个客户端连不上——多客户端必须tcp_server.creat(8080, 5)。 - TCP 是字节流,一次回调不一定是完整一帧,自定义协议要自己分帧。