socket_wifi.lua 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  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. http.request("GET", "119.29.29.29/d?dn=" .. address, nil, nil, nil, 40000, function(result, statusCode, head, body)
  337. log.info("socket.httpDnsCb", result, statusCode, head, body)
  338. sys.publish("SOCKET_HTTPDNS_RESULT_" .. address .. "_" .. port, result, statusCode, head, body)
  339. end)
  340. local _, result, statusCode, head, body = sys.waitUntil("SOCKET_HTTPDNS_RESULT_" .. address .. "_" .. port)
  341. -- DNS解析成功
  342. if result and statusCode == "200" and body and body:match("^[%d%.]+") then
  343. return self:connect(body:match("^([%d%.]+)"), port)
  344. -- DNS解析失败
  345. else
  346. if dnsParser then
  347. dnsParserToken = dnsParserToken + 1
  348. dnsParser(address, dnsParserToken)
  349. local result, ip = sys.waitUntil("USER_DNS_PARSE_RESULT_" .. dnsParserToken, 40000)
  350. if result and ip and ip:match("^[%d%.]+") then
  351. return self:connect(ip:match("^[%d%.]+"), port)
  352. end
  353. end
  354. end
  355. end
  356. if r == false then
  357. if self.ssl then
  358. self:sslDestroy()
  359. end
  360. sys.publish("LIB_SOCKET_CONNECT_FAIL_IND", self.ssl, self.protocol, address, port)
  361. return false
  362. end
  363. self.connected = true
  364. socketStatusNtfy()
  365. return true
  366. end
  367. --- 异步收发选择器
  368. -- @number keepAlive,服务器和客户端最大通信间隔时间,也叫心跳包最大时间,单位秒
  369. -- @string pingreq,心跳包的字符串
  370. -- @return boole,false 失败,true 表示成功
  371. function mt:asyncSelect(keepAlive, pingreq)
  372. assert(self.co == coroutine.running(), "socket:asyncSelect: coroutine mismatch")
  373. if self.error then
  374. log.warn('socket.client:asyncSelect', 'error', self.error)
  375. return false
  376. end
  377. self.wait = "SOCKET_SEND"
  378. while #self.output ~= 0 do
  379. local data = table.concat(self.output)
  380. self.output = {}
  381. for i = 1, string.len(data), SENDSIZE do
  382. -- 按最大MTU单元对data分包
  383. local stepData = string.sub(data, i, i + SENDSIZE - 1)
  384. -- 发送AT命令执行数据发送
  385. req(string.format("AT+" .. (self.ssl and "SSL" or "CIP") .. "SEND=%d,%d", self.id, string.len(stepData)), stepData)
  386. self.wait = self.ssl and "+SSLSEND" or "+CIPSEND"
  387. if not coroutine.yield() then
  388. if self.ssl then
  389. self:sslDestroy()
  390. end
  391. sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
  392. return false
  393. end
  394. end
  395. end
  396. self.wait = "SOCKET_WAIT"
  397. sys.publish("SOCKET_SEND", self.id)
  398. if keepAlive and keepAlive ~= 0 then
  399. if type(pingreq) == "function" then
  400. sys.timerStart(pingreq, keepAlive * 1000)
  401. else
  402. sys.timerStart(self.asyncSend, keepAlive * 1000, self, pingreq or "\0")
  403. end
  404. end
  405. return coroutine.yield()
  406. end
  407. --- 异步发送数据
  408. -- @string data 数据
  409. -- @return result true - 成功,false - 失败
  410. -- @usage c = socket.tcp(); c:connect(); c:asyncSend("12345678");
  411. function mt:asyncSend(data)
  412. if self.error then
  413. log.warn('socket.client:asyncSend', 'error', self.error)
  414. return false
  415. end
  416. table.insert(self.output, data or "")
  417. if self.wait == "SOCKET_WAIT" then
  418. coroutine.resume(self.co, true)
  419. end
  420. return true
  421. end
  422. --- 异步接收数据
  423. -- @return nil, 表示没有收到数据
  424. -- @return data 如果是UDP协议,返回新的数据包,如果是TCP,返回所有收到的数据,没有数据返回长度为0的空串
  425. -- @usage c = socket.tcp(); c:connect()
  426. -- @usage data = c:asyncRecv()
  427. function mt:asyncRecv()
  428. if #self.input == 0 then
  429. return ""
  430. end
  431. if self.protocol == "UDP" then
  432. return table.remove(self.input)
  433. else
  434. local s = table.concat(self.input)
  435. self.input = {}
  436. return s
  437. end
  438. end
  439. --- 发送数据
  440. -- @string data 数据
  441. -- @return result true - 成功,false - 失败
  442. -- @usage c = socket.tcp(); c:connect(); c:send("12345678");
  443. function mt:send(data)
  444. assert(self.co == coroutine.running(), "socket:send: coroutine mismatch")
  445. if self.error then
  446. log.warn('socket.client:send', 'error', self.error)
  447. return false
  448. end
  449. if self.id == nil then
  450. log.warn('socket.client:send', 'closed')
  451. return false
  452. end
  453. for i = 1, string.len(data or ""), SENDSIZE do
  454. -- 按最大MTU单元对data分包
  455. local stepData = string.sub(data, i, i + SENDSIZE - 1)
  456. -- 发送AT命令执行数据发送
  457. req(string.format("AT+" .. (self.ssl and "SSL" or "CIP") .. "SEND=%d,%d", self.id, string.len(stepData)), stepData)
  458. self.wait = self.ssl and "+SSLSEND" or "+CIPSEND"
  459. if not coroutine.yield() then
  460. if self.ssl then
  461. self:sslDestroy()
  462. end
  463. sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
  464. return false
  465. end
  466. end
  467. return true
  468. end
  469. --- 接收数据
  470. -- @number[opt=0] timeout 可选参数,接收超时时间,单位毫秒
  471. -- @string[opt=nil] msg 可选参数,控制socket所在的线程退出recv阻塞状态
  472. -- @bool[opt=nil] msgNoResume 可选参数,控制socket所在的线程退出recv阻塞状态,false或者nil表示“在recv阻塞状态,收到msg消息,可以退出阻塞状态”,true表示不退出
  473. -- @return result 数据接收结果,true表示成功,false表示失败
  474. -- @return data 如果成功的话,返回接收到的数据;超时时返回错误为"timeout";msg控制退出时返回msg的字符串
  475. -- @return param 如果是msg返回的false,则data的值是msg,param的值是msg的参数
  476. -- @usage c = socket.tcp(); c:connect()
  477. -- @usage result, data = c:recv()
  478. -- @usage false,msg,param = c:recv(60000,"publish_msg")
  479. function mt:recv(timeout, msg, msgNoResume)
  480. assert(self.co == coroutine.running(), "socket:recv: coroutine mismatch")
  481. if self.error then
  482. log.warn('socket.client:recv', 'error', self.error)
  483. return false
  484. end
  485. self.msgNoResume = msgNoResume
  486. if msg and not self.iSubscribe then
  487. self.iSubscribe = msg
  488. self.subMessage = function(data)
  489. -- if data then table.insert(self.output, data) end
  490. if (self.wait == "+RECEIVE" or self.wait == "+SSL RECEIVE") and not self.msgNoResume then
  491. if data then
  492. table.insert(self.output, data)
  493. end
  494. coroutine.resume(self.co, 0xAA)
  495. end
  496. end
  497. sys.subscribe(msg, self.subMessage)
  498. end
  499. if msg and #self.output ~= 0 then
  500. sys.publish(msg, false)
  501. end
  502. if #self.input == 0 then
  503. self.wait = self.ssl and "+SSL RECEIVE" or "+RECEIVE"
  504. if timeout and timeout > 0 then
  505. local r, s = sys.wait(timeout)
  506. -- if not r then
  507. -- return false, "timeout"
  508. -- elseif r and r == msg then
  509. -- return false, r, s
  510. -- else
  511. -- if self.ssl and not r then self:sslDestroy() end
  512. -- return r, s
  513. -- end
  514. if r == nil then
  515. return false, "timeout"
  516. elseif r == 0xAA then
  517. local dat = table.concat(self.output)
  518. self.output = {}
  519. return false, msg, dat
  520. else
  521. if self.ssl and not r then
  522. self:sslDestroy()
  523. end
  524. return r, s
  525. end
  526. else
  527. local r, s = coroutine.yield()
  528. if r == 0xAA then
  529. local dat = table.concat(self.output)
  530. self.output = {}
  531. return false, msg, dat
  532. else
  533. return r, s
  534. end
  535. end
  536. end
  537. if self.protocol == "UDP" then
  538. return true, table.remove(self.input)
  539. else
  540. local s = table.concat(self.input)
  541. self.input = {}
  542. return true, s
  543. end
  544. end
  545. function mt:sslDestroy()
  546. assert(self.co == coroutine.running(), "socket:sslDestroy: coroutine mismatch")
  547. if self.ssl and (self.connected or self.created) then
  548. self.connected = false
  549. self.created = false
  550. req("AT+SSLDESTROY=" .. self.id)
  551. self.wait = "+SSLDESTROY"
  552. coroutine.yield()
  553. socketStatusNtfy()
  554. end
  555. end
  556. --- 销毁一个socket
  557. -- @return nil
  558. -- @usage c = socket.tcp(); c:connect(); c:send("123"); c:close()
  559. function mt:close(slow)
  560. assert(self.co == coroutine.running(), "socket:close: coroutine mismatch")
  561. if self.iSubscribe then
  562. sys.unsubscribe(self.iSubscribe, self.subMessage)
  563. self.iSubscribe = false
  564. end
  565. if self.connected or self.created then
  566. self.connected = false
  567. self.created = false
  568. req(self.ssl and ("AT+SSLDESTROY=" .. self.id) or ("AT+CIPCLOSE=" .. self.id .. (slow and ",0" or "")))
  569. self.wait = self.ssl and "+SSLDESTROY" or "+CIPCLOSE"
  570. coroutine.yield()
  571. socketStatusNtfy()
  572. end
  573. if self.id ~= nil then
  574. ril.deRegUrc((self.ssl and "SSL&" or "") .. self.id, onSocketURC)
  575. table.insert((self.ssl and validSsl or valid), 1, self.id)
  576. if self.ssl then
  577. socketsSsl[self.id] = nil
  578. else
  579. sockets[self.id] = nil
  580. end
  581. self.id = nil
  582. end
  583. end
  584. local function onResponse(cmd, success, response, intermediate)
  585. local prefix = string.match(cmd, "AT(%+%u+)")
  586. local id = string.match(cmd, "AT%+%u+=(%d)")
  587. if response == '+PDP: DEACT' then
  588. sys.publish('PDP_DEACT_IND')
  589. end -- cipsend 如果正好pdp deact会返回+PDP: DEACT作为回应
  590. local tSocket = prefix:match("SSL") and socketsSsl or sockets
  591. if not tSocket[id] then
  592. log.warn('socket: response on nil socket', cmd, response)
  593. return
  594. end
  595. if cmd:match("^AT%+SSLCREATE") then
  596. tSocket[id].createResp = response
  597. end
  598. if tSocket[id].wait == prefix then
  599. if (prefix == "+CIPSTART" or prefix == "+SSLCONNECT") and success then
  600. -- CIPSTART,SSLCONNECT 返回OK只是表示被接受
  601. return
  602. end
  603. if prefix == '+CIPSEND' then
  604. if response ~= 'SEND OK' and response ~= 'OK' then
  605. local acceptLen = response:match("Recv (%d) bytes")
  606. if acceptLen then
  607. if acceptLen ~= cmd:match("AT%+%u+=%d,(%d+)") then
  608. success = false
  609. end
  610. else
  611. success = false
  612. end
  613. end
  614. elseif prefix == "+SSLSEND" then
  615. if response:match("%d, *([%u%d :]+)") ~= 'SEND OK' then
  616. success = false
  617. end
  618. end
  619. local reason, address
  620. if not success then
  621. if prefix == "+CIPSTART" then
  622. address = cmd:match("AT%+CIPSTART=%d,\"%a+\",\"(.+)\",%d+")
  623. elseif prefix == "+SSLCONNECT" and (tSocket[id].createResp or ""):match("SSL&%d+,CREATE ERROR: 4") then
  624. address = tSocket[id].address or ""
  625. end
  626. if address and not address:match("^[%d%.]+$") then
  627. reason = "DNS"
  628. end
  629. end
  630. if not reason and not success then
  631. tSocket[id].error = response
  632. end
  633. stopConnectTimer(tSocket, id)
  634. coroutine.resume(tSocket[id].co, success, reason)
  635. end
  636. end
  637. local function onSocketReceiveUrc(urc)
  638. local len, datatest = string.match(urc, "+CIPRECVDATA:(%d+),(.+)")
  639. local id = link_wifi.getRecvId()
  640. tSocket = (tag == "SSL" and socketsSsl or sockets)
  641. len = tonumber(len)
  642. if len == 0 then
  643. return urc
  644. end
  645. if string.len(datatest) == len then
  646. sys.publish("SOCKET_RECV", id)
  647. if tSocket[id].wait == "+RECEIVE" then
  648. coroutine.resume(tSocket[id].co, true, datatest)
  649. else -- 数据进缓冲区,缓冲区溢出采用覆盖模式
  650. if #tSocket[id].input > INDEX_MAX then
  651. tSocket[id].input = {}
  652. end
  653. table.insert(tSocket[id].input, datatest)
  654. end
  655. elseif string.len(datatest) < len then
  656. log.info("不等的情况")
  657. local cache = {}
  658. table.insert(cache, datatest)
  659. len = len - string.len(datatest)
  660. local function filter(data)
  661. -- 剩余未收到的数据长度
  662. if string.len(data) >= len then -- at通道的内容比剩余未收到的数据多
  663. -- 截取网络发来的数据
  664. table.insert(cache, string.sub(data, 1, len))
  665. -- 剩下的数据扔给at进行后续处理
  666. data = string.sub(data, len + 1, -1)
  667. if not tSocket[id] then
  668. log.warn('socket: receive on nil socket', id)
  669. else
  670. sys.publish("SOCKET_RECV", id)
  671. local s = table.concat(cache)
  672. if tSocket[id].wait == "+RECEIVE" or tSocket[id].wait == "+SSL RECEIVE" then
  673. coroutine.resume(tSocket[id].co, true, s)
  674. else -- 数据进缓冲区,缓冲区溢出采用覆盖模式
  675. if #tSocket[id].input > INDEX_MAX then
  676. tSocket[id].input = {}
  677. end
  678. table.insert(tSocket[id].input, s)
  679. end
  680. end
  681. return data
  682. else
  683. table.insert(cache, data)
  684. len = len - string.len(data)
  685. return "", filter
  686. end
  687. end
  688. return filter
  689. end
  690. end
  691. ril.regRsp("+CIPCLOSE", onResponse)
  692. ril.regRsp("+CIPSEND", onResponse)
  693. ril.regRsp("+CIPSTART", onResponse)
  694. ril.regRsp("+SSLDESTROY", onResponse)
  695. ril.regRsp("+SSLCREATE", onResponse)
  696. ril.regRsp("+SSLSEND", onResponse)
  697. ril.regRsp("+SSLCONNECT", onResponse)
  698. ril.regUrc("+CIPRECVDATA", onSocketReceiveUrc)
  699. ril.regUrc("+SSL RECEIVE", onSocketReceiveUrc)
  700. ril.regRsp("+SYSFLASH", function(cmd, result, response, intermediate, param)
  701. if cmd:find("AT%+SYSFLASH=0") then
  702. req("AT+SYSFLASH=1,\"" .. param.path32 .. "\",0," .. io.fileSize(param.path8955), io.readFile(param.path8955), nil, nil, param.id)
  703. elseif cmd:find("AT%+SYSFLASH=1") then
  704. local tSocket = sockets
  705. coroutine.resume(tSocket[param].co)
  706. end
  707. end)
  708. function socket_wifi.printStatus()
  709. log.info('socket.printStatus', 'valid id', table.concat(valid), table.concat(validSsl))
  710. for m, n in pairs({sockets, socketsSsl}) do
  711. for _, client in pairs(n) do
  712. for k, v in pairs(client) do
  713. log.info('socket.printStatus', 'client', client.id, k, v)
  714. end
  715. end
  716. end
  717. end
  718. --- 设置TCP层自动重传的参数
  719. -- @number[opt=4] retryCnt,重传次数;取值范围0到12
  720. -- @number[opt=16] retryMaxTimeout,限制每次重传允许的最大超时时间(单位秒),取值范围1到16
  721. -- @return nil
  722. -- @usage
  723. -- setTcpResendPara(3,8)
  724. -- setTcpResendPara(4,16)
  725. function socket_wifi.setTcpResendPara(retryCnt, retryMaxTimeout)
  726. req("AT+TCPUSERPARAM=6," .. (retryCnt or 4) .. ",7200," .. (retryMaxTimeout or 16))
  727. ril.setDataTimeout(((retryCnt or 4) * (retryMaxTimeout or 16) + 60) * 1000)
  728. end
  729. --- 设置用户自定义的DNS解析器.
  730. -- 通过域名连接服务器时,DNS解析的过程如下:
  731. -- 1、使用core中提供的方式,连接运营商DNS服务器解析,如果解析成功,则结束;如果解析失败,走第2步
  732. -- 2、使用脚本lib中提供的免费腾讯云HttpDns解析,如果解析成功,则结束;如果解析失败,走第3步
  733. -- 3、如果存在用户自定义的DNS解析器,则使用此处用户自定义的DNS解析器去解析
  734. -- @function[opt=nil] parserFnc,用户自定义的DNS解析器函数,函数的调用形式为:
  735. -- parserFnc(domainName,token),调用接口后会等待解析结果的消息通知或者40秒超时失败
  736. -- domainName:string类型,表示域名,例如"www.baidu.com"
  737. -- token:string类型,此次DNS解析请求的token,例如"1"
  738. -- 解析结束后,要publish一个消息来通知解析结果,消息参数中的ip地址最多返回一个,sys.publish("USER_DNS_PARSE_RESULT_"..token,ip),例如:
  739. -- sys.publish("USER_DNS_PARSE_RESULT_1","115.239.211.112")
  740. -- 表示解析成功,解析到1个IP地址115.239.211.112
  741. -- sys.publish("USER_DNS_PARSE_RESULT_1")
  742. -- 表示解析失败
  743. -- @return nil
  744. -- @usage socket.setDnsParser(parserFnc)
  745. function socket_wifi.setDnsParser(parserFnc)
  746. dnsParser = parserFnc
  747. end
  748. --- 设置数据发送模式(在网络准备就绪之前调用此接口设置).
  749. -- 如果设置为快发模式,注意如下两点:
  750. -- 1、通过send接口发送的数据,如果成功发送到服务器,设备端无法获取到这个成功状态
  751. -- 2、通过send接口发送的数据,如果发送失败,设备端可以获取到这个失败状态
  752. -- 慢发模式可以获取到send接口发送的成功或者失败
  753. --
  754. -- ****************************************************************************************************************************************************************
  755. -- TCP协议发送数据时,数据发送出去之后,必须等到服务器返回TCP ACK包,才认为数据发送成功,在网络较差的情况下,这种ACK确认就会导致发送过程很慢。
  756. -- 从而导致用户程序后续的AT处理逻辑一直处于等待状态。例如执行AT+CIPSEND动作发送一包数据后,接下来要执行AT+QTTS播放TTS,但是CIPSEND一直等了1分钟才返回SEND OK,
  757. -- 这时AT+QTTS就会一直等待1分钟,可能不是程序中想看到的。
  758. -- 此时就可以设置为快发模式,AT+CIPSEND可以立即返回一个结果,此结果表示“数据是否被缓冲区所保存”,从而不影响后续其他AT指令的及时执行
  759. --
  760. -- AT版本可以通过AT+CIPQSEND指令、Luat版本可以通过socket.setSendMode接口设置发送模式为快发或者慢发
  761. --
  762. -- 快发模式下,在core中有一个1460*7=10220字节的缓冲区,要发送的数据首先存储到此缓冲区,然后在core中自动循环发送。
  763. -- 如果此缓冲区已满,则AT+CIPSEND会直接返回ERROR,socket:send接口也会直接返回失败
  764. --
  765. -- 同时满足如下几种条件,适合使用快发模式:
  766. -- 1. 发送的数据量小,并且发送频率低,数据发送速度远远不会超过core中的10220字节大小;
  767. -- 没有精确地判断标准,可以简单的按照3分钟不超过10220字节来判断;曾经有一个不适合快发模式的例子如下:
  768. -- 用户使用Luat版本的http上传一个几十K的文件,设置了快发模式,导致一直发送失败,因为循环的向core中的缓冲区插入数据,
  769. -- 插入数据的速度远远超过发送数据到服务器的速度,所以很快就导致缓冲区慢,再插入数据时,就直接返回失败
  770. -- 2. 对每次发送的数据,不需要确认发送结果
  771. -- 3. 数据发送功能不能影响其他功能的及时响应
  772. -- ****************************************************************************************************************************************************************
  773. --
  774. -- @number[opt=0] mode,数据发送模式,0表示慢发,1表示快发
  775. -- @return nil
  776. -- @usage socket.setSendMode(1)
  777. function socket_wifi.setSendMode(mode)
  778. linkTest.setSendMode(mode)
  779. end
  780. -- setTcpResendPara(4, 16)
  781. return socket_wifi