主题
tcp_client — TCP 客户端
模块名:tcp_client
主动连接远端 TCP 服务器并收发数据。start 之后内部线程负责连接与断线重连,脚本只管在回调里处理数据。多网卡机型可以指定出口网卡。
对象方法用冒号写法即可(c:send(...)),库表里也注册了同名函数,tcp_client.send(c, ...) 等价。
函数总览
库级函数
| 函数 | 作用 |
|---|---|
tcp_client.creat(ip, port, [ip_self]) | 创建客户端对象 |
对象方法
| 方法 | 作用 |
|---|---|
c:reg | 注册事件回调 |
c:start | 启动连接线程(自动重连) |
c:send | 发送数据 |
c:set_timeout | 设置接收超时 |
c:is_linked | 查询连接状态 |
c:close | 关闭连接并释放线程 |
c:set_transparent | 把该连接置为串口透传通道 |
常量
| 常量 | 回调入参 | 说明 |
|---|---|---|
tcp_client.CONNECTED | (sock) | 连接建立 |
tcp_client.DISCONNECTED | (sock) | 连接断开 |
tcp_client.RECVDATA | (data, sock) | 收到数据。注意 data 在前、sock 在后 |
tcp_client.creat
lua
tcp_client.creat(ip, port, [ip_self]) --> client创建 TCP 客户端对象。只创建不连接,要调 start 才开始连。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | ip | string | 是 | — | 服务器地址 |
| 2 | port | integer | 是 | — | 服务器端口 |
| 3 | ip_self | string | 否 | "" | 出口网卡的本地 IP,多网卡时用来强制走某块网卡 |
返回值
| 类型 | 说明 |
|---|---|
| userdata | 客户端对象。必须用变量长期持有,被 GC 回收会断开 |
示例
lua
tcpc = tcp_client.creat("192.168.1.100", 8080)c:reg
lua
c:reg(event, callback)注册事件回调。要在 start 之前注册好。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | event | integer | 是 | — | tcp_client.CONNECTED / DISCONNECTED / RECVDATA |
| 2 | callback | function | 是 | — | 回调函数。类型不是 function 会直接报错 |
返回值
无返回值。
示例
lua
tcpc:reg(tcp_client.CONNECTED, function(sock)
print("已连接,socket =", sock)
end)
tcpc:reg(tcp_client.RECVDATA, function(data, sock)
print("收到", #data, "字节")
end)
tcpc:reg(tcp_client.DISCONNECTED, function(sock)
print("连接断开,将自动重连")
end)说明
- 每种事件只保留最后一次注册的回调。
- 回调在 Lua 主循环里执行,不要做长阻塞操作。
c:start
lua
c:start()启动连接线程。本函数立即返回,连接建立在后台完成;断线后会自动重连。
参数
无参数(冒号语法自动传入对象)。
返回值
无返回值。
c:send
lua
c:send(data) --> integer发送数据。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | data | string | 是 | — | 要发送的数据,可为二进制 |
返回值
| 类型 | 说明 |
|---|---|
| integer | 实际发送的字节数。传空串时直接返回 0,不调用底层 |
示例
lua
local n = tcpc:send("\x01\x03\x00\x00\x00\x0A")c:set_timeout
lua
c:set_timeout(ms)设置底层 socket 的接收超时。
参数
| # | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
| 1 | ms | integer | 是 | — | 超时毫秒数 |
返回值
无返回值。
c:is_linked
lua
c:is_linked() --> integer查询连接状态。
参数
无参数。
返回值
| 类型 | 说明 |
|---|---|
| integer | 1 已连接,0 未连接。是整数,不是 boolean |
说明
- 判断连接必须写
== 1。直接if c:is_linked() then在未连接时也成立。
c:close
lua
c:close()关闭 socket 并释放连接线程。
参数
无参数。
返回值
无返回值。
说明
- 关闭后需要重新
start才会再次连接。
c:set_transparent
lua
c:set_transparent()把这条 TCP 连接置为串口透传通道,此后串口收到的数据会直接从这条连接发出、连接收到的数据直接送到串口。
参数
无参数。
返回值
无返回值。
说明
- 串口侧还要调用
com.set_transparent建立对应关系。免脚本的整套透传配置见 远程串口透传。
例程
每个例程都是可直接下载运行的完整脚本:注册完回调与定时器后脚本就结束,固件自动进入消息泵。 API 只用本页已文档化的函数;统一模板「场景 / 完整代码 / 要点 / 验证方法 / 注意事项」。
例程 1:连后端服务器,登录握手 + 心跳保活 + 在线状态
适用:LP3201 / LP3202 / LP3203 / LP3222 / LP3232 / LP3288 / 软PLC模拟器 | 依赖模块:tcp_client timer plc | 难度:★☆☆
场景:设备主动连上位采集服务,连上先发一行登录,之后每 3 秒发一条心跳并带上温度;网络断了不用管 —— 内部线程会自动重连,重成功后要在 CONNECTED 回调里重新登录(很多现场协议在重连后必须重新注册,不然服务端当它没上线)。连接状态镜像到 M110,给梯形图/触摸屏当指示灯。
完整代码(可直接下载运行):
lua
-- 登录握手 + 心跳保活 + 在线状态镜像;断线自动重连后重新登录
tcpc = tcp_client.creat("192.168.1.100", 8080) -- 必须长期持有(全局)
tcpc:set_timeout(3000)
plc.m(110, 0)
tcpc:reg(tcp_client.CONNECTED, function()
plc.m(110, 1)
tcpc:send("LOGIN " .. plc.plc_name() .. "\n") -- 每次(重)连都要重新登录
print("已连接后端")
end)
tcpc:reg(tcp_client.DISCONNECTED, function()
plc.m(110, 0)
print("已断开,等待自动重连")
end)
tcpc:reg(tcp_client.RECVDATA, function(data)
local v = tonumber(data:match("^SET (%d+)")) -- 服务端下发 "SET 1234"
if v then
plc.d(200, v)
print("设定值写入 D200 =", v)
end
end)
tcpc:start() -- 立即返回,后台负责连接与重连
timer.start(3000, function()
if tcpc:is_linked() == 1 then -- is_linked 返回整数,必须 == 1
tcpc:send(string.format("T=%.2f\n", plc.df(100)))
end
end)要点:
start()之后不再需要自己写重连逻辑,但应用层登录/注册必须在CONNECTED回调里重做,否则重连后服务端不认。is_linked()返回整数,== 1才是真的连着;直接if tcpc:is_linked() then永远成立。- 心跳周期别小于 1000ms:回调都在同一个线程里串行执行,太密会挤掉别的活(定时器最小周期 10ms 是技术下限,不代表适合业务)。
tcpc用全局变量:对象被 GC 回收,连接随之断开,表现为「跑一阵子就不动了」。RECVDATA的参数顺序是(data, sock),只关心内容时写单参即可,别按(sock, data)去解。
验证方法:
- PC 上起一个 TCP 服务端(例如
ncat -l 8080)当"后端"。 - 下载脚本并重启设备:服务端应收到
LOGIN <设备名>一行,之后每 3 秒一条T=25.30。 - 上位机「在线监视」里 M110 为 ON。
- 服务端发一行
SET 1234:设备日志打印设定值写入 D200 = 1234,D200 变成 1234。 - 直接关掉服务端再重开:日志出现「已断开」→「已连接后端」,且第二次也能收到 LOGIN(证明重登录后仍然可用)。
注意事项:
- 以为「TCP 自动重连 = 什么都不用做」:应用层会话(登录、订阅、注册)必须自己在
CONNECTED里重新建立。 send返回的是实际发出字节数,成功发送不等于对方收到;需要确认的应用层应答要自己在RECVDATA里处理。- 别用
while true do system.sleep(3000) ... end做心跳:sleep不泵消息队列,会让RECVDATA回调永不触发。
其余例程(各自一页)
- TCP 客户端例程 2:按换行分帧解析 JSON 指令(处理粘包 / 半包)
- TCP 客户端例程 3:串口 ↔ TCP 透传(把现场串口设备搬到远端)
- TCP 客户端例程 4:现场常用脚本
网口透传.LPLUA(原文)
注意事项
RECVDATA回调的参数顺序是(data, sock),不是(sock, data)。is_linked返回整数,判断连接必须== 1。- 客户端对象要长期持有(全局变量或模块级 local),否则被 GC 后连接断开。
- TCP 是字节流,一次
RECVDATA收到的不一定是完整的一帧。自定义协议要自己做分帧(长度前缀或分隔符)。