socket.lua 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. --- 模块功能:数据链路激活、SOCKET管理(创建、连接、数据收发、状态维护)
  2. -- @module socket
  3. -- @author openLuat
  4. -- @license MIT
  5. -- @copyright openLuat
  6. -- @release 2017.9.25
  7. require "link"
  8. require "utils"
  9. module(..., package.seeall)
  10. local req = ril.request
  11. local valid = {"0", "1", "2", "3", "4", "5", "6", "7"}
  12. local validSsl = {"0", "1", "2", "3", "4", "5", "6", "7"}
  13. local sockets = {}
  14. local socketsSsl = {}
  15. -- 单次发送数据最大值
  16. local SENDSIZE = 1460
  17. -- 缓冲区最大下标
  18. local INDEX_MAX = 256
  19. --用户自定义的DNS解析器
  20. local dnsParser
  21. local dnsParserToken = 0
  22. --- SOCKET 是否有可用
  23. -- @return 可用true,不可用false
  24. socket.isReady = link.isReady
  25. local function isSocketActive(ssl)
  26. for _, c in pairs(ssl and socketsSsl or sockets) do
  27. if c.connected then
  28. return true
  29. end
  30. end
  31. end
  32. local function socketStatusNtfy()
  33. sys.publish("SOCKET_ACTIVE", isSocketActive() or isSocketActive(true))
  34. end
  35. local function stopConnectTimer(tSocket, id)
  36. if id and tSocket[id] and tSocket[id].co and coroutine.status(tSocket[id].co) == "suspended"
  37. and (tSocket[id].wait == "+SSLCONNECT" or tSocket[id].wait == "+CIPSTART") then
  38. -- and (tSocket[id].wait == "+SSLCONNECT" or (tSocket[id].protocol == "UDP" and tSocket[id].wait == "+CIPSTART")) then
  39. sys.timerStop(coroutine.resume, tSocket[id].co, false, "TIMEOUT")
  40. end
  41. end
  42. local function errorInd(error)
  43. local coSuspended = {}
  44. for k, v in pairs({sockets, socketsSsl}) do
  45. --if #v ~= 0 then
  46. for _, c in pairs(v) do -- IP状态出错时,通知所有已连接的socket
  47. --if c.connected or c.created then
  48. if error == 'CLOSED' and not c.ssl then c.connected = false socketStatusNtfy() end
  49. c.error = error
  50. if c.co and coroutine.status(c.co) == "suspended" then
  51. stopConnectTimer(v, c.id)
  52. --coroutine.resume(c.co, false)
  53. table.insert(coSuspended, c.co)
  54. end
  55. --end
  56. end
  57. --end
  58. end
  59. for k, v in pairs(coSuspended) do
  60. if v and coroutine.status(v) == "suspended" then
  61. coroutine.resume(v, false)
  62. end
  63. end
  64. end
  65. sys.subscribe("IP_ERROR_IND", function()errorInd('IP_ERROR_IND') end)
  66. sys.subscribe('IP_SHUT_IND', function()errorInd('CLOSED') end)
  67. --订阅rsp返回的消息处理函数
  68. local function onSocketURC(data, prefix)
  69. local tag, id, result = string.match(data, "([SSL]*)[&]*(%d), *([%u :%d]+)")
  70. tSocket = (tag == "SSL" and socketsSsl or sockets)
  71. if not id or not tSocket[id] then
  72. log.error('socket: urc on nil socket', data, id, tSocket[id], socketsSsl[id])
  73. return
  74. end
  75. if result == "CONNECT OK" or result:match("CONNECT ERROR") or result:match("CONNECT FAIL") then
  76. if tSocket[id].wait == "+CIPSTART" or tSocket[id].wait == "+SSLCONNECT" then
  77. stopConnectTimer(tSocket, id)
  78. coroutine.resume(tSocket[id].co, result == "CONNECT OK")
  79. else
  80. log.error("socket: error urc", tSocket[id].wait)
  81. end
  82. return
  83. end
  84. if tag == "SSL" and string.find(result, "ERROR:") == 1 then return end
  85. if string.find(result, "ERROR") or result == "CLOSED" then
  86. if result == 'CLOSED' and not tSocket[id].ssl then tSocket[id].connected = false socketStatusNtfy() end
  87. tSocket[id].error = result
  88. stopConnectTimer(tSocket, id)
  89. coroutine.resume(tSocket[id].co, false)
  90. end
  91. end
  92. -- 创建socket函数
  93. local mt = {}
  94. mt.__index = mt
  95. local function socket(protocol, cert)
  96. local ssl = protocol:match("SSL")
  97. local id = table.remove(ssl and validSsl or valid)
  98. if not id then
  99. log.warn("socket.socket: too many sockets")
  100. return nil
  101. end
  102. local co = coroutine.running()
  103. if not co then
  104. log.warn("socket.socket: socket must be called in coroutine")
  105. return nil
  106. end
  107. -- 实例的属性参数表
  108. local o = {
  109. id = id,
  110. protocol = protocol,
  111. ssl = ssl,
  112. cert = cert,
  113. co = co,
  114. input = {},
  115. output = {},
  116. wait = "",
  117. connected = false,
  118. iSubscribe = false,
  119. subMessage = nil,
  120. }
  121. tSocket = (ssl and socketsSsl or sockets)
  122. tSocket[id] = o
  123. return setmetatable(o, mt)
  124. end
  125. --- 创建基于TCP的socket对象
  126. -- @bool[opt=nil] ssl,是否为ssl连接,true表示是,其余表示否
  127. -- @table[opt=nil] cert,ssl连接需要的证书配置,只有ssl参数为true时,才参数才有意义,cert格式如下:
  128. -- {
  129. -- caCert = "ca.crt", --CA证书文件(Base64编码 X.509格式),如果存在此参数,则表示客户端会对服务器的证书进行校验;不存在则不校验
  130. -- clientCert = "client.crt", --客户端证书文件(Base64编码 X.509格式),服务器对客户端的证书进行校验时会用到此参数
  131. -- clientKey = "client.key", --客户端私钥文件(Base64编码 X.509格式)
  132. -- clientPassword = "123456", --客户端证书文件密码[可选]
  133. -- }
  134. -- @return client,创建成功返回socket客户端对象;创建失败返回nil
  135. -- @usage
  136. -- c = socket.tcp()
  137. -- c = socket.tcp(true)
  138. -- c = socket.tcp(true, {caCert="ca.crt"})
  139. -- c = socket.tcp(true, {caCert="ca.crt", clientCert="client.crt", clientKey="client.key"})
  140. -- c = socket.tcp(true, {caCert="ca.crt", clientCert="client.crt", clientKey="client.key", clientPassword="123456"})
  141. function tcp(ssl, cert)
  142. return socket("TCP" .. (ssl == true and "SSL" or ""), (ssl == true) and cert or nil)
  143. end
  144. --- 创建基于UDP的socket对象
  145. -- @return client,创建成功返回socket客户端对象;创建失败返回nil
  146. -- @usage c = socket.udp()
  147. function udp()
  148. return socket("UDP")
  149. end
  150. local sslInited
  151. local tSslInputCert, sSslInputCert = {}, ""
  152. local function sslInit()
  153. if not sslInited then
  154. sslInited = true
  155. req("AT+SSLINIT")
  156. end
  157. local i, item
  158. for i = 1, #tSslInputCert do
  159. item = table.remove(tSslInputCert, 1)
  160. req(item.cmd, item.arg)
  161. end
  162. tSslInputCert = {}
  163. end
  164. local function sslTerm()
  165. if sslInited then
  166. if not isSocketActive(true) then
  167. sSslInputCert, sslInited = ""
  168. req("AT+SSLTERM")
  169. end
  170. end
  171. end
  172. local function sslInputCert(t, f)
  173. if sSslInputCert:match(t .. f .. "&") then return end
  174. if not tSslInputCert then tSslInputCert = {} end
  175. local s = io.readFile((f:sub(1, 1) == "/") and f or ("/ldata/" .. f))
  176. if not s then log.error("inputcrt err open", path) return end
  177. table.insert(tSslInputCert, {cmd = "AT+SSLCERT=0,\"" .. t .. "\",\"" .. f .. "\",1," .. s:len(), arg = s or ""})
  178. sSslInputCert = sSslInputCert .. t .. f .. "&"
  179. end
  180. --- 连接服务器
  181. -- @string address 服务器地址,支持ip和域名
  182. -- @param port string或者number类型,服务器端口
  183. -- @return bool result true - 成功,false - 失败
  184. -- @number timeout, 链接服务器最长超时时间
  185. -- @usage c = socket.tcp(); c:connect("www.baidu.com",80,5);
  186. function mt:connect(address, port, timeout)
  187. assert(self.co == coroutine.running(), "socket:connect: coroutine mismatch")
  188. if not link.isReady() then
  189. log.info("socket.connect: ip not ready")
  190. return false
  191. end
  192. if cc and cc.anyCallExist() then
  193. log.info("socket:connect: call exist, cannot connect")
  194. return false
  195. end
  196. self.address = address
  197. self.port = port
  198. if self.ssl then
  199. local tConfigCert, i = {}
  200. if self.cert then
  201. if self.cert.caCert then
  202. sslInputCert("cacrt", self.cert.caCert)
  203. table.insert(tConfigCert, "AT+SSLCERT=1," .. self.id .. ",\"cacrt\",\"" .. self.cert.caCert .. "\"")
  204. end
  205. if self.cert.clientCert then
  206. sslInputCert("localcrt", self.cert.clientCert)
  207. table.insert(tConfigCert, "AT+SSLCERT=1," .. self.id .. ",\"localcrt\",\"" .. self.cert.clientCert .. "\",\"" .. (self.cert.clientPassword or "") .. "\"")
  208. end
  209. if self.cert.clientKey then
  210. sslInputCert("localprivatekey", self.cert.clientKey)
  211. table.insert(tConfigCert, "AT+SSLCERT=1," .. self.id .. ",\"localprivatekey\",\"" .. self.cert.clientKey .. "\"")
  212. end
  213. end
  214. sslInit()
  215. req(string.format("AT+SSLCREATE=%d,\"%s\",%d", self.id, address .. ":" .. port, (self.cert and self.cert.caCert) and 0 or 1))
  216. self.created = true
  217. for i = 1, #tConfigCert do
  218. req(tConfigCert[i])
  219. end
  220. req("AT+SSLCONNECT=" .. self.id)
  221. else
  222. req(string.format("AT+CIPSTART=%d,\"%s\",\"%s\",%s", self.id, self.protocol, address, port))
  223. end
  224. -- if self.ssl or self.protocol == "UDP" then sys.timerStart(coroutine.resume, 120000, self.co, false, "TIMEOUT") end
  225. sys.timerStart(coroutine.resume, (timeout or 120) * 1000, self.co, false, "TIMEOUT")
  226. ril.regUrc((self.ssl and "SSL&" or "") .. self.id, onSocketURC)
  227. self.wait = self.ssl and "+SSLCONNECT" or "+CIPSTART"
  228. local r, s = coroutine.yield()
  229. if r == false and s == "DNS" then
  230. if self.ssl then self:sslDestroy()self.error = nil end
  231. require "http"
  232. --请求腾讯云免费HttpDns解析
  233. http.request("GET", "119.29.29.29/d?dn=" .. address, nil, nil, nil, 40000,
  234. function(result, statusCode, head, body)
  235. log.info("socket.httpDnsCb", result, statusCode, head, body)
  236. sys.publish("SOCKET_HTTPDNS_RESULT_"..address.."_"..port, result, statusCode, head, body)
  237. end)
  238. local _, result, statusCode, head, body = sys.waitUntil("SOCKET_HTTPDNS_RESULT_"..address.."_"..port)
  239. --DNS解析成功
  240. if result and statusCode == "200" and body and body:match("^[%d%.]+") then
  241. return self:connect(body:match("^([%d%.]+)"), port)
  242. --DNS解析失败
  243. else
  244. if dnsParser then
  245. dnsParserToken = dnsParserToken + 1
  246. dnsParser(address, dnsParserToken)
  247. local result, ip = sys.waitUntil("USER_DNS_PARSE_RESULT_" .. dnsParserToken, 40000)
  248. if result and ip and ip:match("^[%d%.]+") then
  249. return self:connect(ip:match("^[%d%.]+"), port)
  250. end
  251. end
  252. end
  253. end
  254. if r == false then
  255. if self.ssl then self:sslDestroy() end
  256. sys.publish("LIB_SOCKET_CONNECT_FAIL_IND", self.ssl, self.protocol, address, port)
  257. return false
  258. end
  259. self.connected = true
  260. socketStatusNtfy()
  261. return true
  262. end
  263. --- 异步收发选择器
  264. -- @number keepAlive,服务器和客户端最大通信间隔时间,也叫心跳包最大时间,单位秒
  265. -- @string pingreq,心跳包的字符串
  266. -- @return boole,false 失败,true 表示成功
  267. function mt:asyncSelect(keepAlive, pingreq)
  268. assert(self.co == coroutine.running(), "socket:asyncSelect: coroutine mismatch")
  269. if self.error then
  270. log.warn('socket.client:asyncSelect', 'error', self.error)
  271. return false
  272. end
  273. self.wait = "SOCKET_SEND"
  274. while #self.output ~= 0 do
  275. local data = table.concat(self.output)
  276. self.output = {}
  277. for i = 1, string.len(data), SENDSIZE do
  278. -- 按最大MTU单元对data分包
  279. local stepData = string.sub(data, i, i + SENDSIZE - 1)
  280. --发送AT命令执行数据发送
  281. req(string.format("AT+" .. (self.ssl and "SSL" or "CIP") .. "SEND=%d,%d", self.id, string.len(stepData)), stepData)
  282. self.wait = self.ssl and "+SSLSEND" or "+CIPSEND"
  283. if not coroutine.yield() then
  284. if self.ssl then self:sslDestroy() end
  285. sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
  286. return false
  287. end
  288. end
  289. end
  290. self.wait = "SOCKET_WAIT"
  291. sys.publish("SOCKET_SEND", self.id)
  292. if keepAlive and keepAlive ~= 0 then
  293. if type(pingreq) == "function" then
  294. sys.timerStart(pingreq, keepAlive * 1000)
  295. else
  296. sys.timerStart(self.asyncSend, keepAlive * 1000, self, pingreq or "\0")
  297. end
  298. end
  299. return coroutine.yield()
  300. end
  301. --- 异步发送数据
  302. -- @string data 数据
  303. -- @return result true - 成功,false - 失败
  304. -- @usage c = socket.tcp(); c:connect(); c:asyncSend("12345678");
  305. function mt:asyncSend(data)
  306. if self.error then
  307. log.warn('socket.client:asyncSend', 'error', self.error)
  308. return false
  309. end
  310. table.insert(self.output, data or "")
  311. if self.wait == "SOCKET_WAIT" then coroutine.resume(self.co, true) end
  312. return true
  313. end
  314. --- 异步接收数据
  315. -- @return nil, 表示没有收到数据
  316. -- @return data 如果是UDP协议,返回新的数据包,如果是TCP,返回所有收到的数据,没有数据返回长度为0的空串
  317. -- @usage c = socket.tcp(); c:connect()
  318. -- @usage data = c:asyncRecv()
  319. function mt:asyncRecv()
  320. if #self.input == 0 then return "" end
  321. if self.protocol == "UDP" then
  322. return table.remove(self.input)
  323. else
  324. local s = table.concat(self.input)
  325. self.input = {}
  326. return s
  327. end
  328. end
  329. --- 发送数据
  330. -- @string data 数据
  331. -- @return result true - 成功,false - 失败
  332. -- @usage c = socket.tcp(); c:connect(); c:send("12345678");
  333. function mt:send(data)
  334. assert(self.co == coroutine.running(), "socket:send: coroutine mismatch")
  335. if self.error then
  336. log.warn('socket.client:send', 'error', self.error)
  337. return false
  338. end
  339. if self.id == nil then
  340. log.warn('socket.client:send', 'closed')
  341. return false
  342. end
  343. for i = 1, string.len(data or ""), SENDSIZE do
  344. -- 按最大MTU单元对data分包
  345. local stepData = string.sub(data, i, i + SENDSIZE - 1)
  346. --发送AT命令执行数据发送
  347. req(string.format("AT+" .. (self.ssl and "SSL" or "CIP") .. "SEND=%d,%d", self.id, string.len(stepData)), stepData)
  348. self.wait = self.ssl and "+SSLSEND" or "+CIPSEND"
  349. if not coroutine.yield() then
  350. if self.ssl then self:sslDestroy() end
  351. sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
  352. return false
  353. end
  354. end
  355. return true
  356. end
  357. --- 接收数据
  358. -- @number[opt=0] timeout 可选参数,接收超时时间,单位毫秒
  359. -- @string[opt=nil] msg 可选参数,控制socket所在的线程退出recv阻塞状态
  360. -- @bool[opt=nil] msgNoResume 可选参数,控制socket所在的线程退出recv阻塞状态,false或者nil表示“在recv阻塞状态,收到msg消息,可以退出阻塞状态”,true表示不退出
  361. -- @return result 数据接收结果,true表示成功,false表示失败
  362. -- @return data 如果成功的话,返回接收到的数据;超时时返回错误为"timeout";msg控制退出时返回msg的字符串
  363. -- @return param 如果是msg返回的false,则data的值是msg,param的值是msg的参数
  364. -- @usage c = socket.tcp(); c:connect()
  365. -- @usage result, data = c:recv()
  366. -- @usage false,msg,param = c:recv(60000,"publish_msg")
  367. function mt:recv(timeout, msg, msgNoResume)
  368. assert(self.co == coroutine.running(), "socket:recv: coroutine mismatch")
  369. if self.error then
  370. log.warn('socket.client:recv', 'error', self.error)
  371. return false
  372. end
  373. self.msgNoResume = msgNoResume
  374. if msg and not self.iSubscribe then
  375. self.iSubscribe = msg
  376. self.subMessage = function(data)
  377. if data then table.insert(self.output, data) end
  378. if (self.wait == "+RECEIVE" or self.wait == "+SSL RECEIVE") and not self.msgNoResume then
  379. coroutine.resume(self.co, 0xAA)
  380. end
  381. end
  382. sys.subscribe(msg, self.subMessage)
  383. end
  384. if msg and #self.output ~= 0 then sys.publish(msg, false) end
  385. if #self.input == 0 then
  386. self.wait = self.ssl and "+SSL RECEIVE" or "+RECEIVE"
  387. if timeout and timeout > 0 then
  388. local r, s = sys.wait(timeout)
  389. -- if not r then
  390. -- return false, "timeout"
  391. -- elseif r and r == msg then
  392. -- return false, r, s
  393. -- else
  394. -- if self.ssl and not r then self:sslDestroy() end
  395. -- return r, s
  396. -- end
  397. if r == nil then
  398. return false, "timeout"
  399. elseif r == 0xAA then
  400. local dat = table.concat(self.output)
  401. self.output = {}
  402. return false, msg, dat
  403. else
  404. if self.ssl and not r then self:sslDestroy() end
  405. return r, s
  406. end
  407. else
  408. local r, s = coroutine.yield()
  409. if r == 0xAA then
  410. local dat = table.concat(self.output)
  411. self.output = {}
  412. return false, msg, dat
  413. else
  414. return r, s
  415. end
  416. end
  417. end
  418. if self.protocol == "UDP" then
  419. return true, table.remove(self.input)
  420. else
  421. local s = table.concat(self.input)
  422. self.input = {}
  423. return true, s
  424. end
  425. end
  426. function mt:sslDestroy()
  427. assert(self.co == coroutine.running(), "socket:sslDestroy: coroutine mismatch")
  428. if self.ssl and (self.connected or self.created) then
  429. self.connected = false
  430. self.created = false
  431. req("AT+SSLDESTROY=" .. self.id)
  432. self.wait = "+SSLDESTROY"
  433. coroutine.yield()
  434. socketStatusNtfy()
  435. end
  436. end
  437. --- 销毁一个socket
  438. -- @return nil
  439. -- @usage c = socket.tcp(); c:connect(); c:send("123"); c:close()
  440. function mt:close(slow)
  441. assert(self.co == coroutine.running(), "socket:close: coroutine mismatch")
  442. if self.iSubscribe then
  443. sys.unsubscribe(self.iSubscribe, self.subMessage)
  444. self.iSubscribe = false
  445. end
  446. if self.connected or self.created then
  447. self.connected = false
  448. self.created = false
  449. req(self.ssl and ("AT+SSLDESTROY=" .. self.id) or ("AT+CIPCLOSE=" .. self.id .. (slow and ",0" or "")))
  450. self.wait = self.ssl and "+SSLDESTROY" or "+CIPCLOSE"
  451. coroutine.yield()
  452. socketStatusNtfy()
  453. end
  454. if self.id ~= nil then
  455. ril.deRegUrc((self.ssl and "SSL&" or "") .. self.id, onSocketURC)
  456. table.insert((self.ssl and validSsl or valid), 1, self.id)
  457. if self.ssl then
  458. socketsSsl[self.id] = nil
  459. else
  460. sockets[self.id] = nil
  461. end
  462. self.id = nil
  463. end
  464. end
  465. local function onResponse(cmd, success, response, intermediate)
  466. local prefix = string.match(cmd, "AT(%+%u+)")
  467. local id = string.match(cmd, "AT%+%u+=(%d)")
  468. if response == '+PDP: DEACT' then sys.publish('PDP_DEACT_IND') end -- cipsend 如果正好pdp deact会返回+PDP: DEACT作为回应
  469. local tSocket = prefix:match("SSL") and socketsSsl or sockets
  470. if not tSocket[id] then
  471. log.warn('socket: response on nil socket', cmd, response)
  472. return
  473. end
  474. if cmd:match("^AT%+SSLCREATE") then
  475. tSocket[id].createResp = response
  476. end
  477. if tSocket[id].wait == prefix then
  478. if (prefix == "+CIPSTART" or prefix == "+SSLCONNECT") and success then
  479. -- CIPSTART,SSLCONNECT 返回OK只是表示被接受
  480. return
  481. end
  482. if prefix == '+CIPSEND' then
  483. if response:match("%d, *([%u%d :]+)") ~= 'SEND OK' then
  484. local acceptLen = response:match("DATA ACCEPT:%d,(%d+)")
  485. if acceptLen then
  486. if acceptLen ~= cmd:match("AT%+%u+=%d,(%d+)") then
  487. success = false
  488. end
  489. else
  490. success = false
  491. end
  492. end
  493. elseif prefix == "+SSLSEND" then
  494. if response:match("%d, *([%u%d :]+)") ~= 'SEND OK' then
  495. success = false
  496. end
  497. end
  498. local reason, address
  499. if not success then
  500. if prefix == "+CIPSTART" then
  501. address = cmd:match("AT%+CIPSTART=%d,\"%a+\",\"(.+)\",%d+")
  502. elseif prefix == "+SSLCONNECT" and (tSocket[id].createResp or ""):match("SSL&%d+,CREATE ERROR: 4") then
  503. address = tSocket[id].address or ""
  504. end
  505. if address and not address:match("^[%d%.]+$") then
  506. reason = "DNS"
  507. end
  508. end
  509. if not reason and not success then tSocket[id].error = response end
  510. stopConnectTimer(tSocket, id)
  511. coroutine.resume(tSocket[id].co, success, reason)
  512. end
  513. end
  514. local function onSocketReceiveUrc(urc)
  515. local tag, id, len = string.match(urc, "([SSL]*) *RECEIVE,(%d), *(%d+)")
  516. tSocket = (tag == "SSL" and socketsSsl or sockets)
  517. len = tonumber(len)
  518. if len == 0 then return urc end
  519. local cache = {}
  520. local function filter(data)
  521. --剩余未收到的数据长度
  522. if string.len(data) >= len then -- at通道的内容比剩余未收到的数据多
  523. -- 截取网络发来的数据
  524. table.insert(cache, string.sub(data, 1, len))
  525. -- 剩下的数据扔给at进行后续处理
  526. data = string.sub(data, len + 1, -1)
  527. if not tSocket[id] then
  528. log.warn('socket: receive on nil socket', id)
  529. else
  530. sys.publish("SOCKET_RECV", id)
  531. local s = table.concat(cache)
  532. if tSocket[id].wait == "+RECEIVE" or tSocket[id].wait == "+SSL RECEIVE" then
  533. coroutine.resume(tSocket[id].co, true, s)
  534. else -- 数据进缓冲区,缓冲区溢出采用覆盖模式
  535. if #tSocket[id].input > INDEX_MAX then tSocket[id].input = {} end
  536. table.insert(tSocket[id].input, s)
  537. end
  538. end
  539. return data
  540. else
  541. table.insert(cache, data)
  542. len = len - string.len(data)
  543. return "", filter
  544. end
  545. end
  546. return filter
  547. end
  548. ril.regRsp("+CIPCLOSE", onResponse)
  549. ril.regRsp("+CIPSEND", onResponse)
  550. ril.regRsp("+CIPSTART", onResponse)
  551. ril.regRsp("+SSLDESTROY", onResponse)
  552. ril.regRsp("+SSLCREATE", onResponse)
  553. ril.regRsp("+SSLSEND", onResponse)
  554. ril.regRsp("+SSLCONNECT", onResponse)
  555. ril.regUrc("+RECEIVE", onSocketReceiveUrc)
  556. ril.regUrc("+SSL RECEIVE", onSocketReceiveUrc)
  557. function printStatus()
  558. log.info('socket.printStatus', 'valid id', table.concat(valid), table.concat(validSsl))
  559. for m, n in pairs({sockets, socketsSsl}) do
  560. for _, client in pairs(n) do
  561. for k, v in pairs(client) do
  562. log.info('socket.printStatus', 'client', client.id, k, v)
  563. end
  564. end
  565. end
  566. end
  567. --- 设置TCP层自动重传的参数
  568. -- @number[opt=4] retryCnt,重传次数;取值范围0到12
  569. -- @number[opt=16] retryMaxTimeout,限制每次重传允许的最大超时时间(单位秒),取值范围1到16
  570. -- @return nil
  571. -- @usage
  572. -- setTcpResendPara(3,8)
  573. -- setTcpResendPara(4,16)
  574. function setTcpResendPara(retryCnt, retryMaxTimeout)
  575. req("AT+TCPUSERPARAM=6," .. (retryCnt or 4) .. ",7200," .. (retryMaxTimeout or 16))
  576. ril.setDataTimeout(((retryCnt or 4) * (retryMaxTimeout or 16) + 60) * 1000)
  577. end
  578. --- 设置用户自定义的DNS解析器.
  579. -- 通过域名连接服务器时,DNS解析的过程如下:
  580. -- 1、使用core中提供的方式,连接运营商DNS服务器解析,如果解析成功,则结束;如果解析失败,走第2步
  581. -- 2、使用脚本lib中提供的免费腾讯云HttpDns解析,如果解析成功,则结束;如果解析失败,走第3步
  582. -- 3、如果存在用户自定义的DNS解析器,则使用此处用户自定义的DNS解析器去解析
  583. -- @function[opt=nil] parserFnc,用户自定义的DNS解析器函数,函数的调用形式为:
  584. -- parserFnc(domainName,token),调用接口后会等待解析结果的消息通知或者40秒超时失败
  585. -- domainName:string类型,表示域名,例如"www.baidu.com"
  586. -- token:string类型,此次DNS解析请求的token,例如"1"
  587. -- 解析结束后,要publish一个消息来通知解析结果,消息参数中的ip地址最多返回一个,sys.publish("USER_DNS_PARSE_RESULT_"..token,ip),例如:
  588. -- sys.publish("USER_DNS_PARSE_RESULT_1","115.239.211.112")
  589. -- 表示解析成功,解析到1个IP地址115.239.211.112
  590. -- sys.publish("USER_DNS_PARSE_RESULT_1")
  591. -- 表示解析失败
  592. -- @return nil
  593. -- @usage socket.setDnsParser(parserFnc)
  594. function setDnsParser(parserFnc)
  595. dnsParser = parserFnc
  596. end
  597. --- 设置数据发送模式(在网络准备就绪之前调用此接口设置).
  598. -- 如果设置为快发模式,注意如下两点:
  599. -- 1、通过send接口发送的数据,如果成功发送到服务器,设备端无法获取到这个成功状态
  600. -- 2、通过send接口发送的数据,如果发送失败,设备端可以获取到这个失败状态
  601. -- 慢发模式可以获取到send接口发送的成功或者失败
  602. --
  603. -- ****************************************************************************************************************************************************************
  604. -- TCP协议发送数据时,数据发送出去之后,必须等到服务器返回TCP ACK包,才认为数据发送成功,在网络较差的情况下,这种ACK确认就会导致发送过程很慢。
  605. -- 从而导致用户程序后续的AT处理逻辑一直处于等待状态。例如执行AT+CIPSEND动作发送一包数据后,接下来要执行AT+QTTS播放TTS,但是CIPSEND一直等了1分钟才返回SEND OK,
  606. -- 这时AT+QTTS就会一直等待1分钟,可能不是程序中想看到的。
  607. -- 此时就可以设置为快发模式,AT+CIPSEND可以立即返回一个结果,此结果表示“数据是否被缓冲区所保存”,从而不影响后续其他AT指令的及时执行
  608. --
  609. -- AT版本可以通过AT+CIPQSEND指令、Luat版本可以通过socket.setSendMode接口设置发送模式为快发或者慢发
  610. --
  611. -- 快发模式下,在core中有一个1460*7=10220字节的缓冲区,要发送的数据首先存储到此缓冲区,然后在core中自动循环发送。
  612. -- 如果此缓冲区已满,则AT+CIPSEND会直接返回ERROR,socket:send接口也会直接返回失败
  613. --
  614. -- 同时满足如下几种条件,适合使用快发模式:
  615. -- 1. 发送的数据量小,并且发送频率低,数据发送速度远远不会超过core中的10220字节大小;
  616. -- 没有精确地判断标准,可以简单的按照3分钟不超过10220字节来判断;曾经有一个不适合快发模式的例子如下:
  617. -- 用户使用Luat版本的http上传一个几十K的文件,设置了快发模式,导致一直发送失败,因为循环的向core中的缓冲区插入数据,
  618. -- 插入数据的速度远远超过发送数据到服务器的速度,所以很快就导致缓冲区慢,再插入数据时,就直接返回失败
  619. -- 2. 对每次发送的数据,不需要确认发送结果
  620. -- 3. 数据发送功能不能影响其他功能的及时响应
  621. -- ****************************************************************************************************************************************************************
  622. --
  623. -- @number[opt=0] mode,数据发送模式,0表示慢发,1表示快发
  624. -- @return nil
  625. -- @usage socket.setSendMode(1)
  626. function setSendMode(mode)
  627. link.setSendMode(mode)
  628. end
  629. setTcpResendPara(4, 16)