socket_wifi.lua 32 KB

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