1570 lines
56 KiB
Lua
1570 lines
56 KiB
Lua
|
|
--- 模块功能:数据链路激活、socketCh395管理(创建、连接、数据收发、状态维护)
|
|||
|
|
-- @module socketCh395
|
|||
|
|
-- @author openLuat
|
|||
|
|
-- @license MIT
|
|||
|
|
-- @copyright openLuat
|
|||
|
|
-- @release 2017.9.25
|
|||
|
|
require "link"
|
|||
|
|
require "utils"
|
|||
|
|
module(..., package.seeall)
|
|||
|
|
|
|||
|
|
local sockets = {}
|
|||
|
|
|
|||
|
|
|
|||
|
|
-- 单次发送数据最大值
|
|||
|
|
local SENDSIZE = 1024
|
|||
|
|
-- 缓冲区最大下标
|
|||
|
|
local INDEX_MAX = 2048
|
|||
|
|
-- 是否有socket正处在链接
|
|||
|
|
local socketsConnected = 0
|
|||
|
|
|
|||
|
|
-- CH395配置信息
|
|||
|
|
local configurationCh395 = nil
|
|||
|
|
-- 以太网模块供电设置函数
|
|||
|
|
local powerCbFnc = nil
|
|||
|
|
-- 获取芯片版本(使用0x44以上版本)
|
|||
|
|
local ch395Version = nil
|
|||
|
|
-- 芯片MAC地址
|
|||
|
|
local CH395MAC = nil
|
|||
|
|
local resvnum = 0
|
|||
|
|
|
|||
|
|
TCPCLIENT = 0
|
|||
|
|
UDPCLIENT = 1
|
|||
|
|
RAW = 3
|
|||
|
|
TCPSERVER = 4
|
|||
|
|
|
|||
|
|
local function str2short(str)
|
|||
|
|
local f, n = pack.unpack(str, '<H')
|
|||
|
|
return f and f > 0 and n or 0
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
local function shorts(n)
|
|||
|
|
return pack.pack('<H', n)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
local function sub_z(data, num)
|
|||
|
|
return string.sub(data, num + 1, data:len())
|
|||
|
|
end
|
|||
|
|
local function ip2num(str)
|
|||
|
|
if (not (str and #str == 4)) then
|
|||
|
|
return ''
|
|||
|
|
end
|
|||
|
|
local t = {pack.unpack(str, 'bbbb')}
|
|||
|
|
return string.format('%d.%d.%d.%d', t[2], t[3], t[4], t[5])
|
|||
|
|
end
|
|||
|
|
local function iptostr(ip)
|
|||
|
|
local d1, d2, d3, d4 = string.match(ip, '(%d+)%.(%d+)%.(%d+).(%d+)')
|
|||
|
|
return {string.sub(shorts(d1), 1, 1), string.sub(shorts(d2), 1, 1), string.sub(shorts(d3), 1, 1),
|
|||
|
|
string.sub(shorts(d4), 1, 1)}
|
|||
|
|
end
|
|||
|
|
------------
|
|||
|
|
local function spiSend(id, data)
|
|||
|
|
--log.info('send SPI data:', data:toHex())
|
|||
|
|
local dataSpi
|
|||
|
|
if configurationCh395['spiCs'] then
|
|||
|
|
local setSpi_CS = pins.setup(configurationCh395['spiCs'], 0)
|
|||
|
|
dataSpi = spi.send_recv(id, data)
|
|||
|
|
setSpi_CS(1)
|
|||
|
|
else
|
|||
|
|
dataSpi = spi.send_recv(id, data)
|
|||
|
|
end
|
|||
|
|
-- log.info('recv SPI data', dataSpi:toHex())
|
|||
|
|
return dataSpi
|
|||
|
|
end
|
|||
|
|
-- 命令执行查询
|
|||
|
|
local function inquire2c(time)
|
|||
|
|
local num = 0
|
|||
|
|
while true do
|
|||
|
|
sys.wait(time or 10)
|
|||
|
|
local data = string.sub(spiSend(spi.SPI_1, '\x2c\xff'), 2, 2)
|
|||
|
|
if data ~= '\x10' then
|
|||
|
|
return data
|
|||
|
|
end
|
|||
|
|
num = num + 1
|
|||
|
|
if num > 100 then
|
|||
|
|
return data
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
-- 获取MAC地址
|
|||
|
|
function getMac()
|
|||
|
|
return CH395MAC
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- 本地端口,初始化默认端口
|
|||
|
|
local socketLocalPort = 0
|
|||
|
|
-- 动态获取本地端口号,每次获取累加
|
|||
|
|
local function socketTcpClientPort()
|
|||
|
|
-- socketLocalPort = socketLocalPort + 123
|
|||
|
|
-- if socketLocalPort > 65535 then
|
|||
|
|
-- socketLocalPort = 1000
|
|||
|
|
-- end
|
|||
|
|
-- return socketLocalPort
|
|||
|
|
local time = string.sub(rtos.tick(), 3)
|
|||
|
|
if socketLocalPort < 8 then
|
|||
|
|
socketLocalPort = socketLocalPort + 1
|
|||
|
|
else
|
|||
|
|
socketLocalPort = 0
|
|||
|
|
end
|
|||
|
|
return (socketLocalPort + time) * 5
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- 初始化socket
|
|||
|
|
local function SocketClientInit(type, socketId, soalAddress, soalPort, localPort)
|
|||
|
|
localPort = localPort or socketTcpClientPort()
|
|||
|
|
local addr = iptostr(soalAddress)
|
|||
|
|
-- 设置协议类型
|
|||
|
|
if type == TCPCLIENT then
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x34, socketId, 3))
|
|||
|
|
elseif type == UDPCLIENT then
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x34, socketId, 2))
|
|||
|
|
end
|
|||
|
|
-- 设置本地端口
|
|||
|
|
spiSend(spi.SPI_1, '\x33' .. string.char(socketId) .. shorts(tonumber(localPort)))
|
|||
|
|
-- 设置目的端口号
|
|||
|
|
spiSend(spi.SPI_1, '\x32' .. string.char(socketId) .. shorts(tonumber(soalPort)))
|
|||
|
|
-- 设置目的地址
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x31, socketId) .. addr[1] .. addr[2] .. addr[3] .. addr[4])
|
|||
|
|
-- 打开socket
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x35, socketId))
|
|||
|
|
-- 查询执行状态
|
|||
|
|
local state = inquire2c(50)
|
|||
|
|
log.info('socketCh395', 'open socket' .. socketId .. state:toHex())
|
|||
|
|
if state ~= '\x00' or type == UDPCLIENT then
|
|||
|
|
if type ~= UDPCLIENT then
|
|||
|
|
log.info('socketCh395', 'open socket err', state:toHex())
|
|||
|
|
else
|
|||
|
|
sys.timerStart(function()
|
|||
|
|
sys.publish('SOCK_CONN_CNF', {
|
|||
|
|
['socket_index'] = socketId,
|
|||
|
|
['id'] = 34,
|
|||
|
|
['result'] = 0
|
|||
|
|
})
|
|||
|
|
end, 200)
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
return state
|
|||
|
|
end
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x37, socketId))
|
|||
|
|
state = inquire2c()
|
|||
|
|
if state == '\x00' then
|
|||
|
|
--sys.publish('SOCK_CONN_CNF', {
|
|||
|
|
-- ['socket_index'] = socketId,
|
|||
|
|
-- ['id'] = 34,
|
|||
|
|
-- ['result'] = 0
|
|||
|
|
--})
|
|||
|
|
else
|
|||
|
|
log.info('socketCh395', 'open socket' .. socketId, state:toHex())
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
return state
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- 配置8路socket收发缓存
|
|||
|
|
local function socketBufferConfig()
|
|||
|
|
-- 只有硬件版本大于47才有4-7路
|
|||
|
|
local b = 0
|
|||
|
|
for i = 0, 7 do
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x52, i, b, 4))
|
|||
|
|
b = b + 4
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x53, i, b, 2))
|
|||
|
|
b = b + 2
|
|||
|
|
sys.wait(500)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
-- 关闭套接字连接
|
|||
|
|
local function sockectCh395Close(socketId)
|
|||
|
|
if sockets[socketId] == nil or sockets[socketId] == true then
|
|||
|
|
log.info('socketCh395', 'close err, not socket ' .. socketId)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
if sockets[socketId] == true or sockets[socketId].protocol == 'TCPSERVERCLIENT' then
|
|||
|
|
-- server客户端断开连接
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x38, socketId))
|
|||
|
|
local state = inquire2c()
|
|||
|
|
log.info('socketCh395', 'close server socket ' .. socketId)
|
|||
|
|
sys.publish('SOCK_CLOSE_CNF', {
|
|||
|
|
['socket_index'] = socketId,
|
|||
|
|
['id'] = 33,
|
|||
|
|
['result'] = 0
|
|||
|
|
})
|
|||
|
|
elseif sockets[socketId].protocol == 'TCPSERVER' then
|
|||
|
|
-- server关闭,断开连接,关闭所有server通道
|
|||
|
|
log.info('socketCh395', 'close server ' .. socketId)
|
|||
|
|
for i, v in ipairs(sockets) do
|
|||
|
|
if sockets[i] ~= true and not sockets[i] and sockets[i].protocol == 'TCPSERVERCLIENT' then
|
|||
|
|
local id = sockets[i].id
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x38, sockets[i].id))
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x3D, socketId))
|
|||
|
|
sockets[i] = nil
|
|||
|
|
log.info('socketCh395', 'close server socket ' .. id)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x3D, socketId))
|
|||
|
|
log.info('socketCh395', 'close server ' .. socketId)
|
|||
|
|
local state = inquire2c()
|
|||
|
|
sys.publish('SOCK_CLOSE_CNF', {
|
|||
|
|
['socket_index'] = socketId,
|
|||
|
|
['id'] = 33,
|
|||
|
|
['result'] = 0
|
|||
|
|
})
|
|||
|
|
else
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x3D, socketId))
|
|||
|
|
local stateClose = inquire2c(10)
|
|||
|
|
log.info('sockectCh395', 'close socket' .. socketId .. 'true' .. stateClose:toHex())
|
|||
|
|
sys.publish('SOCK_CLOSE_CNF', {
|
|||
|
|
['socket_index'] = socketId,
|
|||
|
|
['id'] = 33,
|
|||
|
|
['result'] = 0
|
|||
|
|
})
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
local function ipError()
|
|||
|
|
|
|||
|
|
if link.isReady() and link.getNetwork() == link.CH395 then
|
|||
|
|
link.setReady(link.CH395, false)
|
|||
|
|
sys.publish('IP_ERROR_IND')
|
|||
|
|
log.info('socketCh395', 'IP_ERROR_IND')
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
-- 处理中断
|
|||
|
|
local function interruptProcess()
|
|||
|
|
local intAll = string.sub(spiSend(spi.SPI_1, '\x19\xff\xff'), 2, -1)
|
|||
|
|
-- log.info('INT30', intAll:toHex())
|
|||
|
|
intAll = str2short(intAll)
|
|||
|
|
if intAll == 0 or intAll == 65535 then
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
-- 处理PHY中断
|
|||
|
|
if bit.isset(intAll, 2) then
|
|||
|
|
local statePhy = string.sub(spiSend(spi.SPI_1, '\x26\xff'), 2, -1)
|
|||
|
|
if statePhy == '\x01' then
|
|||
|
|
sys.publish("PHY_RESULT", false)
|
|||
|
|
log.info('socketCh395', 'phy false')
|
|||
|
|
ipError()
|
|||
|
|
elseif statePhy == '\x08' then
|
|||
|
|
sys.publish("PHY_RESULT", true)
|
|||
|
|
log.info('socketCh395', 'phy true')
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
-- 处理DHCP中断
|
|||
|
|
if bit.isset(intAll, 3) then
|
|||
|
|
-- DHCP中断
|
|||
|
|
local stateDhcp = string.sub(spiSend(spi.SPI_1, '\x42\xff'), 2, -1)
|
|||
|
|
if stateDhcp == '\x00' then
|
|||
|
|
log.info('socketCh395', 'DHCP true')
|
|||
|
|
elseif stateDhcp == '\x01' then
|
|||
|
|
log.info('socketCh395', 'DHCP false')
|
|||
|
|
ipError()
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
-- 处理不可达中断
|
|||
|
|
if bit.isset(intAll, 0) then
|
|||
|
|
-- 读取不可达信息?
|
|||
|
|
log.info('socketCh395', 'Unreachable interrupt')
|
|||
|
|
-- getUnreachIpport()
|
|||
|
|
end
|
|||
|
|
-- 处理IP冲突中断
|
|||
|
|
if bit.isset(intAll, 1) then
|
|||
|
|
log.info("IP", 'IP conflict')
|
|||
|
|
-- 建议重新修改本地IP,并初始化芯片(未加)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- socket0-7中断
|
|||
|
|
local socketIntId = {}
|
|||
|
|
for i = 4, 11 do
|
|||
|
|
if bit.isset(intAll, i) then
|
|||
|
|
table.insert(socketIntId, (i - 4))
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
local socketCloss = {}
|
|||
|
|
-- 逐个socket查询中断并处理
|
|||
|
|
for q = 1, #socketIntId do
|
|||
|
|
local socketState = string.sub(spiSend(spi.SPI_1, string.char(0x30, socketIntId[q], 0xff, 0xff)), 3, -1)
|
|||
|
|
-- log.info('INT19:' .. socketIntId[q], socketState:toHex())
|
|||
|
|
socketState = str2short(socketState)
|
|||
|
|
-- TCP连接
|
|||
|
|
if bit.isset(socketState, 3) then
|
|||
|
|
-- sockets[socketIntId[q]].connected = true
|
|||
|
|
if sockets[socketIntId[q]] == true then
|
|||
|
|
local data = string.sub(spiSend(spi.SPI_1,
|
|||
|
|
string.char(0x2d, socketIntId[q], 0xff, 0xff, 0xff, 0xff, 0xff, 0xff)), 3, 8)
|
|||
|
|
-- server
|
|||
|
|
local socketData = {
|
|||
|
|
id = socketIntId[q],
|
|||
|
|
ip = ip2num(string.sub(data, 1, 4)),
|
|||
|
|
port = str2short(string.sub(data, 5, 6))
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
sys.publish('tcpServer', socketData)
|
|||
|
|
else
|
|||
|
|
sys.publish('SOCK_CONN_CNF', {
|
|||
|
|
['socket_index'] = socketIntId[q],
|
|||
|
|
['id'] = 34,
|
|||
|
|
['result'] = 0
|
|||
|
|
})
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
-- 接收缓冲区非空
|
|||
|
|
if bit.isset(socketState, 2) then
|
|||
|
|
local data_leng
|
|||
|
|
local data
|
|||
|
|
data_leng = string.sub(spiSend(spi.SPI_1, string.char(0x3b, socketIntId[q], 0xff, 0xff)), 3, -1)
|
|||
|
|
-- if data_leng == '\x00\x00' then
|
|||
|
|
-- break
|
|||
|
|
-- end
|
|||
|
|
data = string.sub(spiSend(spi.SPI_1, string.char(0x3c, socketIntId[q]) .. data_leng ..
|
|||
|
|
string.rep('\xff', str2short(data_leng))), 5, -1)
|
|||
|
|
sys.timerStart(function()
|
|||
|
|
sys.publish('MSG_SOCK_RECV_IND', {
|
|||
|
|
['socket_index'] = socketIntId[q],
|
|||
|
|
['id'] = 31,
|
|||
|
|
['result'] = 0,
|
|||
|
|
['recv_len'] = str2short(data_leng),
|
|||
|
|
['recv_data'] = data
|
|||
|
|
})
|
|||
|
|
end, 10)
|
|||
|
|
|
|||
|
|
-- data_leng = string.sub(spiSend(spi.SPI_1, string.char(0x3b, socketIntId[q], 0xff, 0xff)), 3, -1)
|
|||
|
|
-- data = string.sub(spiSend(spi.SPI_1, string.char(0x3c, socketIntId[q]) .. data_leng ..
|
|||
|
|
-- string.rep('\xff', str2short(data_leng))), 5, -1)
|
|||
|
|
-- sys.publish('MSG_SOCK_RECV_IND', {
|
|||
|
|
-- ['socket_index'] = socketIntId[q],
|
|||
|
|
-- ['id'] = 31,
|
|||
|
|
-- ['result'] = 0,
|
|||
|
|
-- ['recv_len'] = str2short(data_leng),
|
|||
|
|
-- ['recv_data'] = data
|
|||
|
|
-- })
|
|||
|
|
end
|
|||
|
|
-- 发送缓冲区空闲
|
|||
|
|
if bit.isset(socketState, 0) then
|
|||
|
|
if sockets[socketIntId[q]] and sockets[socketIntId[q]] ~= true then
|
|||
|
|
sockets[socketIntId[q]].sendstate = true
|
|||
|
|
else
|
|||
|
|
log.info('socketCh395', 'sendstate err:' .. socketIntId[q])
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
-- 发送成功
|
|||
|
|
if bit.isset(socketState, 1) then
|
|||
|
|
|
|||
|
|
sys.publish('SOCK_SEND_CNF', {
|
|||
|
|
['socket_index'] = socketIntId[q],
|
|||
|
|
['id'] = 32,
|
|||
|
|
['result'] = 0
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- TCP断开
|
|||
|
|
if bit.isset(socketState, 4) then
|
|||
|
|
table.insert(socketCloss, {
|
|||
|
|
['socket_index'] = socketIntId[q],
|
|||
|
|
['id'] = 33,
|
|||
|
|
['result'] = 0
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
-- 超时
|
|||
|
|
if bit.isset(socketState, 6) then
|
|||
|
|
-- 超时处理,重新开启重新连接(未加)
|
|||
|
|
-- 打开socket
|
|||
|
|
log.info('socketCh395', 'socket conn timeout' .. socketIntId[q], sockets[socketIntId[q]], socketIntId[q])
|
|||
|
|
if not sockets[socketIntId[q]] then
|
|||
|
|
sockectCh395Close(socketIntId[q])
|
|||
|
|
elseif sockets[socketIntId[q]] ~= 0 then
|
|||
|
|
local ty = sockets[socketIntId[q]].protocol
|
|||
|
|
local soalAddress = sockets[socketIntId[q]].address
|
|||
|
|
local soalPort = sockets[socketIntId[q]].port
|
|||
|
|
local localPort = sockets[socketIntId[q]].localPort
|
|||
|
|
sockectCh395Close(socketIntId[q])
|
|||
|
|
--if ty == 'TCP' then
|
|||
|
|
--ty = TCPCLIENT
|
|||
|
|
--elseif ty == 'UDP' then
|
|||
|
|
--ty = UDPCLIENT
|
|||
|
|
--end
|
|||
|
|
--SocketClientInit(ty, socketIntId[q], soalAddress, soalPort, localPort)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
for i = 0, 7 do
|
|||
|
|
if sockets[i] then
|
|||
|
|
local data_leng
|
|||
|
|
local data
|
|||
|
|
data_leng = string.sub(spiSend(spi.SPI_1, string.char(0x3b, i, 0xff, 0xff)), 3, -1)
|
|||
|
|
if data_leng ~= "\x00\x00" then
|
|||
|
|
data = string.sub(spiSend(spi.SPI_1,
|
|||
|
|
string.char(0x3c, i) .. data_leng .. string.rep('\xff', str2short(data_leng))), 5, -1)
|
|||
|
|
sys.timerStart(function()
|
|||
|
|
sys.publish('MSG_SOCK_RECV_IND', {
|
|||
|
|
['socket_index'] = i,
|
|||
|
|
['id'] = 31,
|
|||
|
|
['result'] = 0,
|
|||
|
|
['recv_len'] = str2short(data_leng),
|
|||
|
|
['recv_data'] = data
|
|||
|
|
})
|
|||
|
|
end, 10)
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
for w = 1, #socketCloss do
|
|||
|
|
local data =
|
|||
|
|
string.sub(spiSend(spi.SPI_1, string.char(0x2F, socketCloss[w]['socket_index'], 0xff, 0xff)), 3, -1)
|
|||
|
|
local data1 = string.sub(data, 1, 1)
|
|||
|
|
local data2 = string.sub(data, 2, 2)
|
|||
|
|
-- if data1 == '\x00' then
|
|||
|
|
if not sockets[socketCloss[w]['socket_index']] or sockets[socketCloss[w]['socket_index']] == true then
|
|||
|
|
sys.timerStart(function(...)
|
|||
|
|
log.info('socketCh395', 'socket ' .. socketCloss[w]['socket_index'] .. 'state' .. data:toHex())
|
|||
|
|
sys.publish('MSG_SOCK_CLOSE_IND', socketCloss[w])
|
|||
|
|
log.info('socketCh395', 'Disconnection socket ', socketCloss[w]['socket_index'])
|
|||
|
|
end, 200)
|
|||
|
|
|
|||
|
|
elseif sockets[socketCloss[w]['socket_index']].protocol == 'TCPSERVERCLIENT' then
|
|||
|
|
-- sockets[socketCloss[w]['socket_index']]=true
|
|||
|
|
sys.timerStart(function(...)
|
|||
|
|
log.info('socketCh395', 'socket ' .. socketCloss[w]['socket_index'] .. 'state' .. data:toHex())
|
|||
|
|
sys.publish('MSG_SOCK_CLOSE_IND', socketCloss[w])
|
|||
|
|
log.info('socketCh395', 'Disconnection socket ', socketCloss[w]['socket_index'])
|
|||
|
|
end, 200)
|
|||
|
|
else
|
|||
|
|
sys.timerStart(function(...)
|
|||
|
|
log.info('socketCh395', 'socket ' .. socketCloss[w]['socket_index'] .. 'state' .. data:toHex())
|
|||
|
|
sys.publish('MSG_SOCK_CLOSE_IND', socketCloss[w])
|
|||
|
|
log.info('socketCh395', 'Disconnection socket ', socketCloss[w]['socket_index'])
|
|||
|
|
end, 200)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- end
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
sys.wait(10)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
local number = 0
|
|||
|
|
sys.taskInit(function()
|
|||
|
|
while true do
|
|||
|
|
if number > 0 or sys.waitUntil("interruptProcess") then
|
|||
|
|
interruptProcess()
|
|||
|
|
number = number - 1
|
|||
|
|
-- sys.wait(5)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end)
|
|||
|
|
|
|||
|
|
-- 配置中断检测
|
|||
|
|
local function setInt(state)
|
|||
|
|
if state then
|
|||
|
|
pio.pin.setdebounce(5)
|
|||
|
|
pins.setup(configurationCh395['intPin'], function(a)
|
|||
|
|
if a == 2 then
|
|||
|
|
number = number + 2
|
|||
|
|
sys.publish("interruptProcess")
|
|||
|
|
else
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
end)
|
|||
|
|
pio.pin.setdebounce(20)
|
|||
|
|
else
|
|||
|
|
pins.close(configurationCh395['intPin'])
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- 初始化CH395模块
|
|||
|
|
-- @table para 取值如下:
|
|||
|
|
-- para为table类型,表示以太网的配置参数,参数结构如下:
|
|||
|
|
-- {
|
|||
|
|
-- mode = 1, --1表示客户端;2表示服务器;默认为1
|
|||
|
|
-- intPin = pio.P0_22, --以太网芯片中断通知引脚
|
|||
|
|
-- rstPin = pio.P0_23, --复位以太网芯片引脚
|
|||
|
|
-- clientNum = 6, --server可连路数
|
|||
|
|
-- spiCs = pio.P0_23, --SPI片选
|
|||
|
|
-- CH395MAC = "84C2E4A82950", --MAC地址
|
|||
|
|
-- localAddr = "192.168.1.112", --本机的地址
|
|||
|
|
-- localSubnetMas = "255.255.255.0", --子网掩码
|
|||
|
|
-- localPort = 1888, --server本机的端口
|
|||
|
|
-- spiCs=pio.P0_7, --spi片选
|
|||
|
|
-- localGateway = "192.168.1.1", --本机的网关地址
|
|||
|
|
-- func=function(id, msg, dat)end, --中断处理函数,处理server中断事件
|
|||
|
|
-- powerFunc=function(state) end --控制以太网模块的供电开关函数,ret为true开启供电,false关闭供电
|
|||
|
|
-- spi = {spi.SPI_1,0,0,8,800000}, --SPI通道参数,id,cpha,cpol,dataBits,clock,默认spi.SPI_1,0,0,8,800000
|
|||
|
|
-- }
|
|||
|
|
-- @return result 数据接收结果
|
|||
|
|
-- true表示成功
|
|||
|
|
-- false表示失败
|
|||
|
|
-- @usage
|
|||
|
|
-- socketCh395.open(para)
|
|||
|
|
function open(para)
|
|||
|
|
configurationCh395 = para
|
|||
|
|
powerCbFnc = nil or para['powerFunc']
|
|||
|
|
-- 供电及其他前置条件
|
|||
|
|
if powerCbFnc then
|
|||
|
|
powerCbFnc(true)
|
|||
|
|
end
|
|||
|
|
-- 复位芯片
|
|||
|
|
spiSend(spi.SPI_1, '\x05')
|
|||
|
|
-- 开启SPI
|
|||
|
|
if not spi.setup(para['spi'][1], para['spi'][2], para['spi'][3], para['spi'][4], para['spi'][5], 1) == 1 then
|
|||
|
|
log.info('socketCh395', 'open spi err')
|
|||
|
|
return ''
|
|||
|
|
end
|
|||
|
|
-- 设置中断
|
|||
|
|
setInt(true)
|
|||
|
|
-- 芯片进入SPI模式
|
|||
|
|
local setGpioFnc_RSTI = pins.setup(para['rstPin'], 0)
|
|||
|
|
setGpioFnc_RSTI(0)
|
|||
|
|
sys.wait(100)
|
|||
|
|
setGpioFnc_RSTI(1)
|
|||
|
|
sys.wait(200)
|
|||
|
|
-- 测试是否成功建立通讯
|
|||
|
|
local state06 = string.sub(spiSend(spi.SPI_1, '\x06\x55\xff'), 3, 3)
|
|||
|
|
state06 = state06 == '\xaa'
|
|||
|
|
log.info('CH395 state', state06)
|
|||
|
|
if not state06 then
|
|||
|
|
log.info('socketCh395', 'err cannot work')
|
|||
|
|
return ''
|
|||
|
|
end
|
|||
|
|
-- server多连接设置
|
|||
|
|
if para['mode'] == 2 then
|
|||
|
|
spiSend(spi.SPI_1, '\x55\x02')
|
|||
|
|
local stateInit = inquire2c(50)
|
|||
|
|
if stateInit ~= '\x00' then
|
|||
|
|
log.info('open server')
|
|||
|
|
else
|
|||
|
|
log.info('open server err', stateInit:toHex())
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- 获取芯片版本
|
|||
|
|
ch395Version = string.sub(spiSend(spi.SPI_1, '\x01\xff'), 2, 2)
|
|||
|
|
|
|||
|
|
-- 配置芯片信息
|
|||
|
|
if para['localAddr'] and para['localGateway'] then
|
|||
|
|
-- 配置本地信息
|
|||
|
|
local gateway = iptostr(para['localGateway'])
|
|||
|
|
local addr = iptostr(para['localAddr'])
|
|||
|
|
|
|||
|
|
spiSend(spi.SPI_1, '\x22' .. addr[1] .. addr[2] .. addr[3] .. addr[4])
|
|||
|
|
spiSend(spi.SPI_1, '\x23' .. gateway[1] .. gateway[2] .. gateway[3] .. gateway[4])
|
|||
|
|
if para['localSubnetMas'] then
|
|||
|
|
local subnetMas = iptostr(para['localSubnetMas'])
|
|||
|
|
spiSend(spi.SPI_1, '\x24' .. subnetMas[1] .. subnetMas[2] .. subnetMas[3] .. subnetMas[4])
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
-- 设置收发缓存(默认8路)
|
|||
|
|
socketBufferConfig()
|
|||
|
|
-- 模块初始化
|
|||
|
|
spiSend(spi.SPI_1, '\x27')
|
|||
|
|
local stateInit = inquire2c(50)
|
|||
|
|
if stateInit ~= '\x00' then
|
|||
|
|
log.info('socketCh395', 'ch395 INIT err' .. stateInit:toHex())
|
|||
|
|
return ''
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
local result, result2 = sys.waitUntil("PHY_RESULT", 60000)
|
|||
|
|
if not result or not result2 then
|
|||
|
|
return ''
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- 设置MAC地址
|
|||
|
|
if para['CH395MAC'] and string.len(para['CH395MAC']) == 12 then
|
|||
|
|
spiSend(spi.SPI_1, '\x21' .. string.fromHex(para['CH395MAC']))
|
|||
|
|
sys.wait(200)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- 获取芯片MAC
|
|||
|
|
CH395MAC = string.sub(spiSend(spi.SPI_1, string.fromHex('40FFFFFFFFFFFF2cff')), 2, 7)
|
|||
|
|
if CH395MAC == string.char(0, 0, 0, 0, 0, 0) then
|
|||
|
|
CH395MAC = nil
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- DHCP
|
|||
|
|
if not (para['localAddr'] and para['localGateway']) then
|
|||
|
|
spiSend(spi.SPI_1, '\x41\x01')
|
|||
|
|
local stateDhcp = inquire2c(30)
|
|||
|
|
if stateDhcp ~= '\x00' then
|
|||
|
|
log.info('socketCh395', 'ch395 INIT err' .. stateDhcp:toHex())
|
|||
|
|
return ''
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
-- 获取本地IP
|
|||
|
|
for q = 1, 20 do
|
|||
|
|
sys.wait(500)
|
|||
|
|
local dataIp = string.sub(spiSend(spi.SPI_1, string.fromHex('43ffffffffffffffffffffffffffffffffffffffff')), 2,
|
|||
|
|
-1)
|
|||
|
|
if link.getNetwork() == link.CH395 and string.sub(dataIp, 1, 4) ~= '\xff\xff\xff\xff' and
|
|||
|
|
string.sub(dataIp, 1, 4) ~= '\x00\x00\x00\x00' then
|
|||
|
|
local ch395Ip = ip2num(string.sub(dataIp, 1, 4))
|
|||
|
|
log.info('ip', ch395Ip)
|
|||
|
|
link.setReady(link.CH395, true)
|
|||
|
|
sys.publish('IP_READY_IND')
|
|||
|
|
return ch395Ip
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
return ''
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- 开关模块
|
|||
|
|
function close()
|
|||
|
|
link.setReady(link.CH395, false)
|
|||
|
|
sys.publish('IP_ERROR_IND')
|
|||
|
|
-- 关闭所有socket
|
|||
|
|
for i = 0, 7 do
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x3D, i))
|
|||
|
|
sockets[i] = nil
|
|||
|
|
end
|
|||
|
|
-- 关闭中断检测
|
|||
|
|
setInt(false)
|
|||
|
|
-- 关闭SPI
|
|||
|
|
if not spi.close(configurationCh395['spi'][1]) == 1 then
|
|||
|
|
log.info('socketCh395 close spi err', configurationCh395['spi'][1], configurationCh395['spi'][2],
|
|||
|
|
configurationCh395['spi'][3], configurationCh395['spi'][4], configurationCh395['spi'][5], 1)
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- 关闭模块
|
|||
|
|
if powerCbFnc then
|
|||
|
|
powerCbFnc(false)
|
|||
|
|
end
|
|||
|
|
return true
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
---建立套接字连接
|
|||
|
|
local function sockectCh395Conn(type, addr, port, lcoalPort, data)
|
|||
|
|
local socketId = nil
|
|||
|
|
|
|||
|
|
for i = 0, 7 do
|
|||
|
|
if not sockets[i] then
|
|||
|
|
socketId = i
|
|||
|
|
data.id=i
|
|||
|
|
sockets[i] = data
|
|||
|
|
break
|
|||
|
|
end
|
|||
|
|
if i == 7 then
|
|||
|
|
log.info('socketCh395', 'No socket available ')
|
|||
|
|
return nil
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
if type == TCPCLIENT or type == UDPCLIENT then
|
|||
|
|
local tcpClientState = SocketClientInit(type, socketId, addr, port, lcoalPort)
|
|||
|
|
if tcpClientState == '\x00' then
|
|||
|
|
return socketId
|
|||
|
|
else
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x3D, socketId))
|
|||
|
|
local stateClose = inquire2c(50)
|
|||
|
|
if stateClose == '\x00' then
|
|||
|
|
log.info('sockectCh395', 'close socket' .. socketId .. 'true')
|
|||
|
|
end
|
|||
|
|
sockets[socketId] = nil
|
|||
|
|
return nil
|
|||
|
|
end
|
|||
|
|
elseif type == TCPSERVER then
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- 发送数据
|
|||
|
|
local function sockectCh395Send(socketId, data)
|
|||
|
|
for i = 1, 10 do
|
|||
|
|
if sockets[socketId] and sockets[socketId] ~= true and sockets[socketId].sendstate then
|
|||
|
|
spiSend(spi.SPI_1, '\x39' .. string.char(socketId) .. shorts(#data) .. data)
|
|||
|
|
local state = inquire2c(50)
|
|||
|
|
if state == '\x00' then
|
|||
|
|
sys.publish('SOCK_SEND_CNF', {
|
|||
|
|
['socket_index'] = socketId,
|
|||
|
|
['id'] = 32,
|
|||
|
|
['result'] = 0
|
|||
|
|
})
|
|||
|
|
else
|
|||
|
|
sys.publish('SOCK_SEND_CNF', {
|
|||
|
|
['socket_index'] = socketId,
|
|||
|
|
['id'] = 32,
|
|||
|
|
['result'] = 1
|
|||
|
|
})
|
|||
|
|
end
|
|||
|
|
log.info('socketCh395', socketId .. 'send state' .. state:toHex())
|
|||
|
|
break
|
|||
|
|
elseif not sockets[socketId] or sockets[socketId] == true then
|
|||
|
|
-- log.info('socketCh395', 'send err, not socket ' .. socketId)
|
|||
|
|
else
|
|||
|
|
sys.wait(100)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- 接收数据
|
|||
|
|
local function sockectCh395Recv(socketId, buffLen)
|
|||
|
|
local data_leng = string.sub(spiSend(spi.SPI_1, string.char(0x3b, socketId, 0xff, 0xff)), 3, -1)
|
|||
|
|
local data = string.sub(spiSend(spi.SPI_1, string.char(0x3c, socketId) .. data_leng ..
|
|||
|
|
string.rep('\xff', str2short(data_leng))), 5, -1)
|
|||
|
|
return data
|
|||
|
|
end
|
|||
|
|
-- table 深拷贝
|
|||
|
|
local function clone(tb)
|
|||
|
|
local ret = {}
|
|||
|
|
for k, v in pairs(tb) do
|
|||
|
|
ret[k] = type(v) == "table" and clone(v) or v
|
|||
|
|
end
|
|||
|
|
return ret
|
|||
|
|
end
|
|||
|
|
-- 创建server
|
|||
|
|
local function initServer(port, num, data)
|
|||
|
|
if num > 7 then
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
local dd = true
|
|||
|
|
local serverTable = {}
|
|||
|
|
local serverid = nil
|
|||
|
|
for i = 0, 7 do
|
|||
|
|
if not sockets[i] then
|
|||
|
|
table.insert(serverTable, i)
|
|||
|
|
if dd then
|
|||
|
|
sockets[i] = data
|
|||
|
|
dd = false
|
|||
|
|
else
|
|||
|
|
|
|||
|
|
sockets[i] = clone(data)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
if #serverTable == num + 1 then
|
|||
|
|
break
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
if i == 7 then
|
|||
|
|
log.info('socketCh395', 'No socket available ')
|
|||
|
|
for q = 1, #serverTable do
|
|||
|
|
sockets[serverTable[q]] = nil
|
|||
|
|
end
|
|||
|
|
return nil
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
for i = 1, #serverTable do
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x34, serverTable[i], 3))
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x33, serverTable[i]) .. shorts(port))
|
|||
|
|
if i == 1 then
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x35, serverTable[i]))
|
|||
|
|
sockets[i].id = serverTable[i]
|
|||
|
|
serverid = serverTable[i]
|
|||
|
|
local state = inquire2c(50)
|
|||
|
|
if state == '\x00' then
|
|||
|
|
sockets[serverTable[i]].id = serverTable[i]
|
|||
|
|
sockets[serverTable[i]].protocol = 'TCPSERVER'
|
|||
|
|
else
|
|||
|
|
log.info('socket open err', state:toHex())
|
|||
|
|
log.info('socketCh395', 'socket open err')
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
else
|
|||
|
|
local state = inquire2c(50)
|
|||
|
|
if state == '\x00' then
|
|||
|
|
log.info('socketCh395', 'server open' .. serverTable[i])
|
|||
|
|
|
|||
|
|
sockets[serverTable[i]].protocol = 'TCPSERVERCLIENT' .. serverTable[i]
|
|||
|
|
sockets[serverTable[i]] = true
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
if i == num + 1 then
|
|||
|
|
spiSend(spi.SPI_1, string.char(0x36, serverid))
|
|||
|
|
local state = inquire2c(50)
|
|||
|
|
if state == '\x00' then
|
|||
|
|
return serverid
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
------------------------
|
|||
|
|
local function errorInd(error)
|
|||
|
|
local coSuspended = {}
|
|||
|
|
|
|||
|
|
for _, c in pairs(sockets) do -- IP状态出错时,通知所有已连接的socket
|
|||
|
|
if c ~= true then
|
|||
|
|
c.error = error
|
|||
|
|
-- 不能打开如下3行代码,IP出错时,会通知每个socket,socket会主动close
|
|||
|
|
-- 如果设置了connected=false,则主动close时,直接退出,不会执行close动作,导致core中的socket资源没释放
|
|||
|
|
-- 会引发core中socket耗尽以及socket id重复的问题
|
|||
|
|
-- c.connected = false
|
|||
|
|
-- socketsConnected = c.connected or socketsConnected
|
|||
|
|
-- if error == 'CLOSED' then sys.publish("SOCKET_ACTIVE", socketsConnected) end
|
|||
|
|
if c.co and coroutine.status(c.co) == "suspended" then
|
|||
|
|
-- coroutine.resume(c.co, false)
|
|||
|
|
table.insert(coSuspended, c.co)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
for k, v in pairs(coSuspended) do
|
|||
|
|
if v and coroutine.status(v) == "suspended" then
|
|||
|
|
coroutine.resume(v, false, error)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
sys.subscribe("IP_ERROR_IND", function()
|
|||
|
|
errorInd('IP_ERROR_IND')
|
|||
|
|
end)
|
|||
|
|
-- sys.subscribe('IP_SHUT_IND', function()errorInd('CLOSED') end)
|
|||
|
|
-- 创建socket函数
|
|||
|
|
local mt = {}
|
|||
|
|
mt.__index = mt
|
|||
|
|
local function socket(protocol, cert, tCoreExtPara)
|
|||
|
|
local ssl = protocol:match("SSL")
|
|||
|
|
local co = coroutine.running()
|
|||
|
|
if not co then
|
|||
|
|
log.warn("socket.socket: socket must be called in coroutine")
|
|||
|
|
return nil
|
|||
|
|
end
|
|||
|
|
-- if protocol == 'TCPSERVER' and type(tCoreExtPara) == 'table' then
|
|||
|
|
-- protocol
|
|||
|
|
-- end
|
|||
|
|
-- 实例的属性参数表
|
|||
|
|
local o = {
|
|||
|
|
id = nil,
|
|||
|
|
protocol = protocol,
|
|||
|
|
tCoreExtPara = tCoreExtPara,
|
|||
|
|
ssl = ssl,
|
|||
|
|
cert = cert,
|
|||
|
|
co = co,
|
|||
|
|
input = {},
|
|||
|
|
output = {},
|
|||
|
|
wait = "",
|
|||
|
|
connected = false,
|
|||
|
|
iSubscribe = false,
|
|||
|
|
subMessage = nil,
|
|||
|
|
isBlock = false,
|
|||
|
|
msg = nil,
|
|||
|
|
rcvProcFnc = nil,
|
|||
|
|
sendstate = true, -- 发送缓冲区状态,true为空闲,可发送数据
|
|||
|
|
localPort = nil -- 本地端口
|
|||
|
|
}
|
|||
|
|
if tCoreExtPara and tCoreExtPara['localport'] and type(tCoreExtPara['localport']) == 'number' then
|
|||
|
|
o.localPort = tCoreExtPara['port']
|
|||
|
|
end
|
|||
|
|
if protocol == 'TCPSERVER' and tCoreExtPara['type']== 'TCPSERVER' then
|
|||
|
|
o.port = configurationCh395["localPort"]
|
|||
|
|
local id = initServer(configurationCh395["localPort"], configurationCh395["clientNum"], setmetatable(o, mt))
|
|||
|
|
if not id then
|
|||
|
|
log.info('socketCh395', 'server socket err')
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
elseif protocol == 'TCPSERVER' and tCoreExtPara['type']~= 'TCPSERVER' then
|
|||
|
|
o.address = tCoreExtPara['ip']
|
|||
|
|
o.port = tCoreExtPara['port']
|
|||
|
|
o.id = tCoreExtPara['id']
|
|||
|
|
o.protocol = 'TCPSERVERCLIENT'
|
|||
|
|
sockets[tCoreExtPara['id']] = setmetatable(o, mt)
|
|||
|
|
end
|
|||
|
|
return setmetatable(o, mt)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- 创建基于TCP的socket对象
|
|||
|
|
-- @bool[opt=nil] ssl 是否为ssl连接,true表示是,其余表示否
|
|||
|
|
-- @table[opt=nil] cert 保留参数,ssl功能还未实现。
|
|||
|
|
-- @table[opt=nil] tCoreExtPara 建立链接扩展参数
|
|||
|
|
-- {
|
|||
|
|
-- id =0, --server socket索引ID
|
|||
|
|
-- ip ="192.168.1.1", --server socket client ip
|
|||
|
|
-- port ="8000", --server socket client port
|
|||
|
|
-- type ="TCPSERVER", --server socket type
|
|||
|
|
-- localport ="8000", -- socket client port
|
|||
|
|
-- }
|
|||
|
|
-- @return client,创建成功返回socket客户端对象;创建失败返回nil
|
|||
|
|
|
|||
|
|
function tcp(ssl, cert, tCoreExtPara)
|
|||
|
|
if tCoreExtPara and tCoreExtPara['type'] == "TCPSERVER" then
|
|||
|
|
return socket("TCPSERVER", nil, tCoreExtPara)
|
|||
|
|
elseif tCoreExtPara and tCoreExtPara['type'] ~= "TCPSERVER" then
|
|||
|
|
return socket("TCPSERVER", nil, tCoreExtPara)
|
|||
|
|
end
|
|||
|
|
return socket("TCP" .. (ssl == true and "SSL" or ""), (ssl == true) and cert or nil, tCoreExtPara)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- 创建基于UDP的socket对象
|
|||
|
|
-- @return client,创建成功返回socket客户端对象;创建失败返回nil
|
|||
|
|
-- @usage c = socketCh395.udp()
|
|||
|
|
function udp(localPort)
|
|||
|
|
return socket("UDP", nil, localPort)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- 连接服务器
|
|||
|
|
-- @string address 服务器地址,支持ip和域名
|
|||
|
|
-- @param port string或者number类型,服务器端口
|
|||
|
|
-- @number[opt=120] timeout 可选参数,连接超时时间,单位秒
|
|||
|
|
-- @return bool result true - 成功,false - 失败
|
|||
|
|
-- @return string ,id '0' -- '8' ,返回通道ID编号
|
|||
|
|
-- @usage
|
|||
|
|
-- socketClient = socketCh395.tcp()
|
|||
|
|
-- socketClient:connect("www.baidu.com","80")
|
|||
|
|
function mt:connect(address, port, timeout)
|
|||
|
|
assert(self.co == coroutine.running(), "socket:connect: coroutine mismatch")
|
|||
|
|
log.info('socketCh395', 'socket data' .. self.protocol, address, port)
|
|||
|
|
if not link.isReady() then
|
|||
|
|
log.info("socket.connect: ip not ready")
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
self.address = address
|
|||
|
|
self.port = port
|
|||
|
|
local tCoreExtPara = self.tCoreExtPara or {}
|
|||
|
|
-- 默认缓冲区大小
|
|||
|
|
local rcvBufferSize = tCoreExtPara.rcvBufferSize or 0
|
|||
|
|
local d1, d2, d3, d4 = string.match(address, '(%d+)%.(%d+)%.(%d+).(%d+)')
|
|||
|
|
if not (d1 == nil or d2 == nil or d3 == nil or d4 == nil) then
|
|||
|
|
if self.protocol == 'TCP' then
|
|||
|
|
self.id = sockectCh395Conn(TCPCLIENT, address, port, self.localPort, self)
|
|||
|
|
|
|||
|
|
elseif self.protocol == 'TCPSSL' then
|
|||
|
|
-- 未实现
|
|||
|
|
log.info("socketCh395", "no ssl")
|
|||
|
|
else
|
|||
|
|
self.id = sockectCh395Conn(UDPCLIENT, address, port, self.localPort, self)
|
|||
|
|
end
|
|||
|
|
else
|
|||
|
|
self.id = -2
|
|||
|
|
end
|
|||
|
|
if type(socketcore.sock_conn_ext) == "function" then
|
|||
|
|
if not self.id or self.id < 0 then
|
|||
|
|
if self.id == -2 then
|
|||
|
|
require "http"
|
|||
|
|
-- 请求腾讯云免费HttpDns解析
|
|||
|
|
http.request("GET", "119.29.29.29/d?dn=" .. address, nil, nil, nil, 40000,
|
|||
|
|
function(result, statusCode, head, body)
|
|||
|
|
log.info("socket.httpDnsCb", result, statusCode, head, body)
|
|||
|
|
sys.publish("SOCKET_HTTPDNS_RESULT_" .. address .. "_" .. port, result, statusCode, head, body)
|
|||
|
|
end)
|
|||
|
|
local _, result, statusCode, head, body = sys.waitUntil(
|
|||
|
|
"SOCKET_HTTPDNS_RESULT_" .. address .. "_" .. port)
|
|||
|
|
|
|||
|
|
-- DNS解析成功
|
|||
|
|
if result and statusCode == "200" and body and body:match("^[%d%.]+") then
|
|||
|
|
return self:connect(body:match("^([%d%.]+)"), port, timeout)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
self.id = nil
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
if not self.id then
|
|||
|
|
log.info("socket:connect: core sock conn error", self.protocol, address, port, self.cert)
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
log.info("socket:connect-coreid,prot,addr,port,cert,timeout", self.id, self.protocol, address, port, self.cert,
|
|||
|
|
timeout or 120)
|
|||
|
|
sockets[self.id] = self
|
|||
|
|
self.wait = "SOCKET_CONNECT"
|
|||
|
|
self.timerId = sys.timerStart(coroutine.resume, (timeout or 120) * 1000, self.co, false, "TIMEOUT")
|
|||
|
|
local result, reason = coroutine.yield()
|
|||
|
|
if not result and reason == 'server close' then
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
if self.timerId and reason ~= "TIMEOUT" then
|
|||
|
|
sys.timerStop(self.timerId)
|
|||
|
|
end
|
|||
|
|
if not result then
|
|||
|
|
log.info("socket:connect: connect fail" .. self.id, reason)
|
|||
|
|
if reason == "RESPONSE" then
|
|||
|
|
sockets[self.id] = nil
|
|||
|
|
self.id = nil
|
|||
|
|
end
|
|||
|
|
sys.publish("LIB_SOCKET_CONNECT_FAIL_IND", self.ssl, self.protocol, address, port)
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
log.info("socket:connect: connect ok")
|
|||
|
|
|
|||
|
|
if not self.connected then
|
|||
|
|
self.connected = true
|
|||
|
|
socketsConnected = socketsConnected + 1
|
|||
|
|
sys.publish("SOCKET_ACTIVE", socketsConnected > 0)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
return true, self.id
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
function mt:serverClose()
|
|||
|
|
if not self then
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
if self.error then
|
|||
|
|
log.warn('socket.client:asyncSelect', 'error', self.error)
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
coroutine.resume(self.co, false, 'server close')
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- server发送数据
|
|||
|
|
-- @number[opt=nil] keepAlive 服务器和客户端最大通信间隔时间,也叫心跳包最大时间,单位秒
|
|||
|
|
-- @string[opt=nil] pingreq 心跳包的字符串
|
|||
|
|
-- @return boole,false 失败,true 表示成功
|
|||
|
|
-- @usage
|
|||
|
|
-- socketClient = socketCh395.tcp()
|
|||
|
|
-- socketClient:connect("www.baidu.com","80")
|
|||
|
|
-- while socketClient:serverSelect() do end
|
|||
|
|
function mt:serverSelect(keepAlive, pingreq)
|
|||
|
|
assert(self.co == coroutine.running(), "socket:asyncSelect: coroutine mismatch")
|
|||
|
|
if self.error then
|
|||
|
|
log.warn('socket.client:asyncSelect', 'error', self.error)
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
self.wait = "SOCKET_SEND"
|
|||
|
|
local dataLen = 0
|
|||
|
|
-- log.info("socket.asyncSelect #self.output",#self.output)
|
|||
|
|
while #self.output ~= 0 do
|
|||
|
|
local data = table.concat(self.output)
|
|||
|
|
dataLen = string.len(data)
|
|||
|
|
self.output = {}
|
|||
|
|
local sendSize = SENDSIZE
|
|||
|
|
for i = 1, dataLen, sendSize do
|
|||
|
|
-- 按最大MTU单元对data分包
|
|||
|
|
sockectCh395Send(self.id, data:sub(i, i + sendSize - 1))
|
|||
|
|
if self.timeout then
|
|||
|
|
self.timerId = sys.timerStart(coroutine.resume, self.timeout * 1000, self.co, false, "TIMEOUT")
|
|||
|
|
end
|
|||
|
|
-- log.info("socket.asyncSelect self.timeout",self.timeout)
|
|||
|
|
local result, reason = coroutine.yield()
|
|||
|
|
if not result and reason == 'server close' then
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
log.info('发送结果', result, reason)
|
|||
|
|
if self.timerId and reason ~= "TIMEOUT" then
|
|||
|
|
sys.timerStop(self.timerId)
|
|||
|
|
end
|
|||
|
|
sys.publish("SOCKET_ASYNC_SEND", result)
|
|||
|
|
if not result then
|
|||
|
|
sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
|
|||
|
|
log.warn('socket.asyncSelect', 'send error', data:sub(i, i + sendSize - 1))
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
self.wait = "SOCKET_WAIT"
|
|||
|
|
-- log.info("socket.asyncSelect",dataLen,self.id)
|
|||
|
|
if dataLen > 0 then
|
|||
|
|
sys.publish("SOCKET_SEND", self.id, true)
|
|||
|
|
end
|
|||
|
|
if keepAlive and keepAlive ~= 0 then
|
|||
|
|
if type(pingreq) == "function" then
|
|||
|
|
sys.timerStart(pingreq, keepAlive * 1000)
|
|||
|
|
else
|
|||
|
|
sys.timerStart(self.asyncSend, keepAlive * 1000, self, pingreq or "\0")
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
local result, reason = coroutine.yield()
|
|||
|
|
if not result and reason == 'server close' then
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
return result, reason
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- 异步发送数据
|
|||
|
|
-- @number[opt=nil] keepAlive 服务器和客户端最大通信间隔时间,也叫心跳包最大时间,单位秒
|
|||
|
|
-- @string[opt=nil] pingreq 心跳包的字符串
|
|||
|
|
-- @return boole,false 失败,true 表示成功
|
|||
|
|
-- @usage
|
|||
|
|
-- socketClient = socketCh395.tcp()
|
|||
|
|
-- socketClient:connect("www.baidu.com","80")
|
|||
|
|
-- while socketClient:asyncSelect() do end
|
|||
|
|
function mt:asyncSelect(keepAlive, pingreq)
|
|||
|
|
assert(self.co == coroutine.running(), "socket:asyncSelect: coroutine mismatch")
|
|||
|
|
if self.error then
|
|||
|
|
log.warn('socket.client:asyncSelect', 'error', self.error)
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
self.wait = "SOCKET_SEND"
|
|||
|
|
local dataLen = 0
|
|||
|
|
-- log.info("socket.asyncSelect #self.output",#self.output)
|
|||
|
|
while #self.output ~= 0 do
|
|||
|
|
local data = table.concat(self.output)
|
|||
|
|
dataLen = string.len(data)
|
|||
|
|
self.output = {}
|
|||
|
|
local sendSize = self.protocol == "UDP" and 1472 or SENDSIZE
|
|||
|
|
for i = 1, dataLen, sendSize do
|
|||
|
|
-- 按最大MTU单元对data分包
|
|||
|
|
-- socketcore.sock_send(self.id, data:sub(i, i + sendSize - 1))
|
|||
|
|
sockectCh395Send(self.id, data:sub(i, i + sendSize - 1))
|
|||
|
|
if self.timeout then
|
|||
|
|
self.timerId = sys.timerStart(coroutine.resume, self.timeout * 1000, self.co, false, "TIMEOUT")
|
|||
|
|
end
|
|||
|
|
-- log.info("socket.asyncSelect self.timeout",self.timeout)
|
|||
|
|
local result, reason = coroutine.yield()
|
|||
|
|
if not result and reason == 'server close' then
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
if self.timerId and reason ~= "TIMEOUT" then
|
|||
|
|
sys.timerStop(self.timerId)
|
|||
|
|
end
|
|||
|
|
sys.publish("SOCKET_ASYNC_SEND", result)
|
|||
|
|
if not result then
|
|||
|
|
sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
|
|||
|
|
-- log.warn('socket.asyncSelect', 'send error')
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
self.wait = "SOCKET_WAIT"
|
|||
|
|
-- log.info("socket.asyncSelect",dataLen,self.id)
|
|||
|
|
if dataLen > 0 then
|
|||
|
|
sys.publish("SOCKET_SEND", self.id, true)
|
|||
|
|
end
|
|||
|
|
if keepAlive and keepAlive ~= 0 then
|
|||
|
|
if type(pingreq) == "function" then
|
|||
|
|
sys.timerStart(pingreq, keepAlive * 1000)
|
|||
|
|
else
|
|||
|
|
sys.timerStart(self.asyncSend, keepAlive * 1000, self, pingreq or "\0")
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
local result, reason = coroutine.yield()
|
|||
|
|
if not result and reason == 'server close' then
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
return result, reason
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
function mt:getAsyncSend()
|
|||
|
|
if self.error then
|
|||
|
|
return 0
|
|||
|
|
end
|
|||
|
|
return #(self.output)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- server缓存待发送的数据
|
|||
|
|
-- @string data 数据
|
|||
|
|
-- @number[opt=nil] timeout 可选参数,发送超时时间,单位秒;为nil时表示不支持timeout
|
|||
|
|
-- @return result true - 成功,false - 失败
|
|||
|
|
-- @usage
|
|||
|
|
-- socketClient = socketCh395.tcp()
|
|||
|
|
-- socketClient:connect("www.baidu.com","80")
|
|||
|
|
-- socketClient:serverSend("12345678")
|
|||
|
|
function mt:serverSend(data, timeout)
|
|||
|
|
if self.error then
|
|||
|
|
log.warn('socket.client:asyncSend', 'error', self.error)
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
self.timeout = timeout
|
|||
|
|
table.insert(self.output, data or "")
|
|||
|
|
-- log.info("socket.asyncSend",self.wait)
|
|||
|
|
if self.wait == "SOCKET_WAIT" then
|
|||
|
|
coroutine.resume(self.co, true)
|
|||
|
|
end
|
|||
|
|
return true
|
|||
|
|
end
|
|||
|
|
--- server接收数据
|
|||
|
|
-- @return data 表示接收到的数据(如果是UDP,返回最新的一包数据;如果是TCP,返回所有收到的数据)
|
|||
|
|
-- ""表示未收到数据
|
|||
|
|
-- @usage
|
|||
|
|
-- socketClient = socketCh395.tcp()
|
|||
|
|
-- socketClient:connect("www.baidu.com","80")
|
|||
|
|
-- data = socketClient:serverRecv()
|
|||
|
|
function mt:serverRecv()
|
|||
|
|
if #self.input == 0 then
|
|||
|
|
return ""
|
|||
|
|
end
|
|||
|
|
if self.protocol == "UDP" then
|
|||
|
|
return table.remove(self.input)
|
|||
|
|
else
|
|||
|
|
local s = table.concat(self.input)
|
|||
|
|
self.input = {}
|
|||
|
|
if self.isBlock then
|
|||
|
|
table.insert(self.input, recv(self.msg.socket_index, self.msg.recv_len))
|
|||
|
|
end
|
|||
|
|
return s
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- 异步缓存待发送的数据
|
|||
|
|
-- @string data 数据
|
|||
|
|
-- @number[opt=nil] timeout 可选参数,发送超时时间,单位秒;为nil时表示不支持timeout
|
|||
|
|
-- @return result true - 成功,false - 失败
|
|||
|
|
-- @usage
|
|||
|
|
-- socketClient = socketCh395.tcp()
|
|||
|
|
-- socketClient:connect("www.baidu.com","80")
|
|||
|
|
-- socketClient:asyncSend("12345678")
|
|||
|
|
function mt:asyncSend(data, timeout)
|
|||
|
|
if self.error then
|
|||
|
|
log.warn('socket.client:asyncSend', 'error', self.error)
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
self.timeout = timeout
|
|||
|
|
table.insert(self.output, data or "")
|
|||
|
|
-- log.info("socket.asyncSend",self.wait)
|
|||
|
|
if self.wait == "SOCKET_WAIT" then
|
|||
|
|
coroutine.resume(self.co, true)
|
|||
|
|
end
|
|||
|
|
return true
|
|||
|
|
end
|
|||
|
|
--- 异步接收数据
|
|||
|
|
-- @return data 表示接收到的数据(如果是UDP,返回最新的一包数据;如果是TCP,返回所有收到的数据)
|
|||
|
|
-- ""表示未收到数据
|
|||
|
|
-- @usage
|
|||
|
|
-- socketClient = socketCh395.tcp()
|
|||
|
|
-- socketClient:connect("www.baidu.com","80")
|
|||
|
|
-- data = socketClient:asyncRecv()
|
|||
|
|
function mt:asyncRecv()
|
|||
|
|
if #self.input == 0 then
|
|||
|
|
return ""
|
|||
|
|
end
|
|||
|
|
if self.protocol == "UDP" then
|
|||
|
|
return table.remove(self.input)
|
|||
|
|
else
|
|||
|
|
local s = table.concat(self.input)
|
|||
|
|
self.input = {}
|
|||
|
|
if self.isBlock then
|
|||
|
|
table.insert(self.input, socketcore.sock_recv(self.msg.socket_index, self.msg.recv_len))
|
|||
|
|
end
|
|||
|
|
return s
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- 同步发送数据
|
|||
|
|
-- @string data 数据
|
|||
|
|
-- 此处传入的数据长度和剩余可用内存有关,只要内存够用,可以随便传入数据
|
|||
|
|
-- 虽然说此处的数据长度没有特别限制,但是调用core中的socket发送接口时,每次最多发送11200字节的数据
|
|||
|
|
-- 例如此处传入的data长度是112000字节,则在这个send接口中,会循环10次,每次发送11200字节的数据
|
|||
|
|
-- @number[opt=120] timeout 可选参数,发送超时时间,单位秒
|
|||
|
|
-- @return result true - 成功,false - 失败
|
|||
|
|
-- @usage
|
|||
|
|
-- socketClient = socketCh395.tcp()
|
|||
|
|
-- socketClient:connect("www.baidu.com","80")
|
|||
|
|
-- socketClient:send("12345678")
|
|||
|
|
function mt:send(data, timeout)
|
|||
|
|
assert(self.co == coroutine.running(), "socket:send: coroutine mismatch")
|
|||
|
|
if self.error then
|
|||
|
|
log.warn('socket.client:send', 'error', self.error)
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
log.debug("socket.send", "total " .. string.len(data or "") .. " bytes", "first 30 bytes", (data or ""):sub(1, 30))
|
|||
|
|
local sendSize = self.protocol == "UDP" and 1472 or SENDSIZE
|
|||
|
|
for i = 1, string.len(data or ""), sendSize do
|
|||
|
|
-- 按最大MTU单元对data分包
|
|||
|
|
self.wait = "SOCKET_SEND"
|
|||
|
|
sockectCh395Send(self.id, data:sub(i, i + sendSize - 1))
|
|||
|
|
self.timerId = sys.timerStart(coroutine.resume, (timeout or 120) * 1000, self.co, false, "TIMEOUT")
|
|||
|
|
local result, reason = coroutine.yield()
|
|||
|
|
if not result and reason == 'server close' then
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
if self.timerId and reason ~= "TIMEOUT" then
|
|||
|
|
sys.timerStop(self.timerId)
|
|||
|
|
end
|
|||
|
|
if not result then
|
|||
|
|
log.info("socket:send" .. self.id, "send fail", reason)
|
|||
|
|
sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
return true
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- 同步接收数据
|
|||
|
|
-- @number[opt=0] timeout 可选参数,接收超时时间,单位毫秒
|
|||
|
|
-- @string[opt=nil] msg 可选参数,控制socket所在的线程退出recv阻塞状态
|
|||
|
|
-- @bool[opt=nil] msgNoResume 可选参数,控制socket所在的线程退出recv阻塞状态
|
|||
|
|
-- false或者nil表示“在recv阻塞状态,收到msg消息,可以退出阻塞状态”,true表示不退出
|
|||
|
|
-- 此参数仅lib内部使用,应用脚本不要使用此参数
|
|||
|
|
-- @return result 数据接收结果
|
|||
|
|
-- true表示成功(接收到了数据)
|
|||
|
|
-- false表示失败(没有接收到数据)
|
|||
|
|
-- @return data
|
|||
|
|
-- 如果result为true,data表示接收到的数据(如果是UDP,返回最新的一包数据;如果是TCP,返回所有收到的数据)
|
|||
|
|
-- 如果result为false,超时失败,data为"timeout"
|
|||
|
|
-- 如果result为false,msg控制退出,data为msg的字符串
|
|||
|
|
-- 如果result为false,socket连接被动断开控制退出,data为"CLOSED"
|
|||
|
|
-- 如果result为false,PDP断开连接控制退出,data为"IP_ERROR_IND"
|
|||
|
|
-- @return param 如果是msg控制退出,param的值是msg的参数
|
|||
|
|
-- @usage
|
|||
|
|
-- socketClient = socketCh395.tcp()
|
|||
|
|
-- socketClient:connect("www.baidu.com","80")
|
|||
|
|
-- result,data = socketClient:recv(60000,"APP_SOCKET_SEND_DATA")
|
|||
|
|
function mt:recv(timeout, msg, msgNoResume)
|
|||
|
|
assert(self.co == coroutine.running(), "socket:recv: coroutine mismatch")
|
|||
|
|
if self.error then
|
|||
|
|
log.warn('socket.client:recv', 'error', self.error)
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
self.msgNoResume = msgNoResume
|
|||
|
|
if msg and not self.iSubscribe then
|
|||
|
|
self.iSubscribe = msg
|
|||
|
|
self.subMessage = function(data)
|
|||
|
|
-- if data then table.insert(self.output, data) end
|
|||
|
|
if self.wait == "+RECEIVE" and not self.msgNoResume then
|
|||
|
|
if data then
|
|||
|
|
table.insert(self.output, data)
|
|||
|
|
end
|
|||
|
|
coroutine.resume(self.co, 0xAA)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
sys.subscribe(msg, self.subMessage)
|
|||
|
|
end
|
|||
|
|
if msg and #self.output > 0 then
|
|||
|
|
sys.publish(msg, false)
|
|||
|
|
end
|
|||
|
|
if #self.input == 0 then
|
|||
|
|
self.wait = "+RECEIVE"
|
|||
|
|
if timeout and timeout > 0 then
|
|||
|
|
local r, s = sys.wait(timeout)
|
|||
|
|
if r == nil then
|
|||
|
|
return false, "timeout"
|
|||
|
|
elseif r == 0xAA then
|
|||
|
|
local dat = table.concat(self.output)
|
|||
|
|
self.output = {}
|
|||
|
|
return false, msg, dat
|
|||
|
|
else
|
|||
|
|
return r, s
|
|||
|
|
end
|
|||
|
|
else
|
|||
|
|
local r, s = coroutine.yield()
|
|||
|
|
if not r and s == 'server close' then
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
if r == 0xAA then
|
|||
|
|
local dat = table.concat(self.output)
|
|||
|
|
self.output = {}
|
|||
|
|
return false, msg, dat
|
|||
|
|
else
|
|||
|
|
return r, s
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
if self.protocol == "UDP" then
|
|||
|
|
local s = table.remove(self.input)
|
|||
|
|
return true, s
|
|||
|
|
else
|
|||
|
|
log.warn("-------------------使用缓冲区---------------")
|
|||
|
|
local s = table.concat(self.input)
|
|||
|
|
self.input = {}
|
|||
|
|
if self.isBlock then
|
|||
|
|
table.insert(self.input, sockectCh395Recv(self.msg.socket_index, self.msg.recv_len))
|
|||
|
|
end
|
|||
|
|
return true, s
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- 主动关闭并且销毁一个socket
|
|||
|
|
-- @return nil
|
|||
|
|
-- @usage
|
|||
|
|
-- socketClient = socketCh395.tcp()
|
|||
|
|
-- socketClient:connect("www.baidu.com","80")
|
|||
|
|
-- socketClient:close()
|
|||
|
|
function mt:close()
|
|||
|
|
assert(self.co == coroutine.running(), "socket:close: coroutine mismatch")
|
|||
|
|
if self.iSubscribe then
|
|||
|
|
sys.unsubscribe(self.iSubscribe, self.subMessage)
|
|||
|
|
self.iSubscribe = false
|
|||
|
|
end
|
|||
|
|
-- log.info('socketCh395', 'close' .. self.id)
|
|||
|
|
-- 此处不要再判断状态,否则在连接超时失败时,conneted状态仍然是未连接,会导致无法close
|
|||
|
|
-- if self.connected then
|
|||
|
|
log.info("socket:sock_close", self.id)
|
|||
|
|
local result, reason
|
|||
|
|
|
|||
|
|
if self.id then
|
|||
|
|
sockectCh395Close(self.id)
|
|||
|
|
self.wait = "SOCKET_CLOSE"
|
|||
|
|
while true do
|
|||
|
|
result, reason = coroutine.yield()
|
|||
|
|
if not result and reason == 'server close' then
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
if reason == "RESPONSE" then
|
|||
|
|
break
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
if self.connected then
|
|||
|
|
self.connected = false
|
|||
|
|
if socketsConnected > 0 then
|
|||
|
|
socketsConnected = socketsConnected - 1
|
|||
|
|
end
|
|||
|
|
sys.publish("SOCKET_ACTIVE", socketsConnected > 0)
|
|||
|
|
end
|
|||
|
|
if self.input then
|
|||
|
|
self.input = {}
|
|||
|
|
end
|
|||
|
|
if self.protocol == 'TCPSERVERCLIENT' then
|
|||
|
|
sockets[self.id] = true
|
|||
|
|
else
|
|||
|
|
if self.id ~= nil then
|
|||
|
|
sockets[self.id] = nil
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- socket接收自定义控制处理
|
|||
|
|
-- @function[opt=nil] rcvCbFnc,socket接收到数据后,执行的回调函数,回调函数的调用形式为:
|
|||
|
|
-- rcvCbFnc(readFnc,socketIndex,rcvDataLen)
|
|||
|
|
-- rcvCbFnc内部,会判断是否读取数据,如果读取,执行readFnc(socketIndex,rcvDataLen),返回true;否则返回false或者nil
|
|||
|
|
function mt:setRcvProc(rcvCbFnc)
|
|||
|
|
assert(self.co == coroutine.running(), "socket:setRcvProc: coroutine mismatch")
|
|||
|
|
self.rcvProcFnc = rcvCbFnc
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
function on_response(msg)
|
|||
|
|
local t = {
|
|||
|
|
[rtos.MSG_SOCK_CLOSE_CNF] = 'SOCKET_CLOSE',
|
|||
|
|
-- 33
|
|||
|
|
[rtos.MSG_SOCK_SEND_CNF] = 'SOCKET_SEND',
|
|||
|
|
-- 32
|
|||
|
|
[rtos.MSG_SOCK_CONN_CNF] = 'SOCKET_CONNECT'
|
|||
|
|
-- 34
|
|||
|
|
}
|
|||
|
|
if not sockets[msg.socket_index] then
|
|||
|
|
log.warn('response on nil socket', msg.socket_index, t[msg.id], msg.result)
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
if sockets[msg.socket_index] == true then
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
log.info(sockets[msg.socket_index].wait, t[msg.id], msg.socket_index, msg.id)
|
|||
|
|
if sockets[msg.socket_index].wait ~= t[msg.id] then
|
|||
|
|
log.warn('response on invalid wait', sockets[msg.socket_index].id, sockets[msg.socket_index].wait, t[msg.id],
|
|||
|
|
msg.socket_index)
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
log.info('socket:on_response:', msg.socket_index, t[msg.id], msg.result)
|
|||
|
|
if sockets[msg.socket_index].protocol == 'TCPSERVERCLIENT' then
|
|||
|
|
msg.result=0
|
|||
|
|
end
|
|||
|
|
coroutine.resume(sockets[msg.socket_index].co, msg.result == 0, 'RESPONSE')
|
|||
|
|
end
|
|||
|
|
sys.subscribe('SOCK_CONN_CNF', on_response)
|
|||
|
|
sys.subscribe('SOCK_CLOSE_CNF', on_response)
|
|||
|
|
sys.subscribe('SOCK_SEND_CNF', on_response)
|
|||
|
|
-- 被动关闭
|
|||
|
|
sys.subscribe('MSG_SOCK_CLOSE_IND', function(msg)
|
|||
|
|
local data = string.sub(spiSend(spi.SPI_1, string.char(0x2F, msg.socket_index, 0xff, 0xff)), 3, -1)
|
|||
|
|
log.info('socketCh395', 'close socket ' .. msg.socket_index .. 'state', data:toHex())
|
|||
|
|
if string.sub(data, 1, 1) ~= '\x00' then
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
log.info('socket.rtos.MSG_SOCK_CLOSE_IND',msg.socket_index)
|
|||
|
|
if not sockets[msg.socket_index] then
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
elseif sockets[msg.socket_index] == true then
|
|||
|
|
return
|
|||
|
|
elseif sockets[msg.socket_index].protocol == 'TCPSERVERCLIENT' then
|
|||
|
|
coroutine.resume(sockets[msg.socket_index].co, false, 'CLOSED')
|
|||
|
|
sockets[msg.socket_index] = true
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
if not sockets[msg.socket_index] then
|
|||
|
|
log.warn('close ind on nil socket', msg.socket_index, msg.id)
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
if sockets[msg.socket_index].connected then
|
|||
|
|
sockets[msg.socket_index].connected = false
|
|||
|
|
if socketsConnected > 0 then
|
|||
|
|
socketsConnected = socketsConnected - 1
|
|||
|
|
end
|
|||
|
|
sys.publish('SOCKET_ACTIVE', socketsConnected > 0)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
|
|||
|
|
sockets[msg.socket_index].error = 'CLOSED'
|
|||
|
|
--[[
|
|||
|
|
if type(socketcore.sock_destroy) == "function" then
|
|||
|
|
socketcore.sock_destroy(msg.socket_index)
|
|||
|
|
end]]
|
|||
|
|
sys.publish('LIB_SOCKET_CLOSE_IND', sockets[msg.socket_index].ssl, sockets[msg.socket_index].protocol,
|
|||
|
|
sockets[msg.socket_index].address, sockets[msg.socket_index].port)
|
|||
|
|
coroutine.resume(sockets[msg.socket_index].co, false, 'CLOSED')
|
|||
|
|
end)
|
|||
|
|
sys.subscribe('MSG_SOCK_RECV_IND', function(msg)
|
|||
|
|
if type(sockets[msg.socket_index])~='table' then
|
|||
|
|
log.warn('close ind on nil socket', msg.socket_index, msg.id)
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
log.debug('socket.recv', msg.recv_len, sockets[msg.socket_index].rcvProcFnc)
|
|||
|
|
if sockets[msg.socket_index].rcvProcFnc then
|
|||
|
|
sockets[msg.socket_index].rcvProcFnc(msg.recv_data)
|
|||
|
|
else
|
|||
|
|
if sockets[msg.socket_index].wait == '+RECEIVE' then
|
|||
|
|
coroutine.resume(sockets[msg.socket_index].co, true, msg.recv_data)
|
|||
|
|
else -- 数据进缓冲区,缓冲区溢出采用覆盖模式
|
|||
|
|
if #sockets[msg.socket_index].input > INDEX_MAX then
|
|||
|
|
log.error('socket recv', 'out of stack', 'block')
|
|||
|
|
-- sockets[msg.socket_index].input = {}
|
|||
|
|
sockets[msg.socket_index].isBlock = true
|
|||
|
|
sockets[msg.socket_index].msg = msg
|
|||
|
|
else
|
|||
|
|
sockets[msg.socket_index].isBlock = false
|
|||
|
|
|
|||
|
|
table.insert(sockets[msg.socket_index].input, msg.recv_data)
|
|||
|
|
end
|
|||
|
|
sys.publish('SOCKET_RECV', msg.socket_index)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end)
|
|||
|
|
|
|||
|
|
-- 设置TCP层自动重传的参数
|
|||
|
|
-- @number[opt=4] retryCnt 重传次数;取值范围0到12
|
|||
|
|
-- @number[opt=16] retryMaxTimeout 限制每次重传允许的最大超时时间(单位秒),取值范围1到16
|
|||
|
|
-- @return nil
|
|||
|
|
-- @usage
|
|||
|
|
-- setTcpResendPara(3,8)
|
|||
|
|
-- setTcpResendPara(4,16)
|
|||
|
|
function setTcpResendPara(retryCnt, retryMaxTimeout)
|
|||
|
|
ril.request("AT+TCPUSERPARAM=6," .. (retryCnt or 4) .. ",7200," .. (retryMaxTimeout or 16))
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- 设置域名解析参数
|
|||
|
|
-- 注意:0027以及之后的core版本才支持此功能
|
|||
|
|
-- @number[opt=4] retryCnt 重传次数;取值范围1到8
|
|||
|
|
-- @number[opt=4] retryTimeoutMulti 重传超时时间倍数,取值范围1到5
|
|||
|
|
-- 第n次重传超时时间的计算方式为:第n次的重传超时基数*retryTimeoutMulti,单位为秒
|
|||
|
|
-- 重传超时基数表为{1, 1, 2, 4, 4, 4, 4, 4}
|
|||
|
|
-- 第1次重传超时时间为:1*retryTimeoutMulti 秒
|
|||
|
|
-- 第2次重传超时时间为:1*retryTimeoutMulti 秒
|
|||
|
|
-- 第3次重传超时时间为:2*retryTimeoutMulti 秒
|
|||
|
|
-- ...........................................
|
|||
|
|
-- 第8次重传超时时间为:8*retryTimeoutMulti 秒
|
|||
|
|
-- @return nil
|
|||
|
|
-- @usage
|
|||
|
|
-- socket.setDnsParsePara(8,5)
|
|||
|
|
function setDnsParsePara(retryCnt, retryTimeoutMulti)
|
|||
|
|
ril.request("AT*DNSTMOUT=" .. (retryCnt or 4) .. "," .. (retryTimeoutMulti or 4))
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
function setIpStatis(interval)
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- 打印所有socket的状态
|
|||
|
|
-- @return 无
|
|||
|
|
-- @usage socketCh395.printStatus()
|
|||
|
|
function printStatus()
|
|||
|
|
for _, client in pairs(sockets) do
|
|||
|
|
for k, v in pairs(client) do
|
|||
|
|
log.info('socket.printStatus', 'client', client.id, k, v)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- 设置数据传输后,允许进入休眠状态的延时时长
|
|||
|
|
-- 3024版本以及之后的版本才支持此功能
|
|||
|
|
-- 此功能设置的参数,设置成功后,掉电会自动保存
|
|||
|
|
-- @number tm,数据传输后,允许进入休眠状态的延时时长,单位为秒,取值范围1到20
|
|||
|
|
-- 注意:此时间越短,允许进入休眠状态越快,功耗越低;但是在某些网络环境中,此时间越短,可能会造成数据传输不稳定
|
|||
|
|
-- 建议在可以接受的功耗范围内,此值设置的越大越好
|
|||
|
|
-- 如果没有设置此参数,此延时时长是和基站的配置有关,一般来说是10秒左右
|
|||
|
|
-- @return nil
|
|||
|
|
-- @usage
|
|||
|
|
-- socketCh395.setLowPower(5)
|
|||
|
|
function setLowPower(tm)
|
|||
|
|
ril.request("AT*RTIME=" .. tm)
|
|||
|
|
end
|