socket4G.lua 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. -- 模块功能:数据链路激活、socket4G管理(创建、连接、数据收发、状态维护)
  2. -- @module socket4G
  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 sockets = {}
  11. -- 单次发送数据最大值
  12. local SENDSIZE = 11200
  13. -- 缓冲区最大下标
  14. local INDEX_MAX = 256
  15. -- 是否有socket正处在链接
  16. local socketsConnected = 0
  17. -- ip流量统计间隔,每个统计间隔内新增ip流量
  18. local ipStatisInterval, ipDataFlow = 0,0
  19. local function ipDataFlowAdd(flow)
  20. if ipStatisInterval~=0 then
  21. ipDataFlow = ipDataFlow+flow
  22. end
  23. end
  24. -- SOCKET 是否有可用
  25. -- @return 可用true,不可用false
  26. -- socket4G.isReady = link.isReady
  27. local function errorInd(error)
  28. local coSuspended = {}
  29. for _, c in pairs(sockets) do -- IP状态出错时,通知所有已连接的socket
  30. c.error = error
  31. --不能打开如下3行代码,IP出错时,会通知每个socket,socket会主动close
  32. --如果设置了connected=false,则主动close时,直接退出,不会执行close动作,导致core中的socket资源没释放
  33. --会引发core中socket耗尽以及socket id重复的问题
  34. --c.connected = false
  35. --socketsConnected = c.connected or socketsConnected
  36. --if error == 'CLOSED' then sys.publish("SOCKET_ACTIVE", socketsConnected) end
  37. if c.co and coroutine.status(c.co) == "suspended" then
  38. --coroutine.resume(c.co, false)
  39. table.insert(coSuspended, c.co)
  40. end
  41. end
  42. for k, v in pairs(coSuspended) do
  43. if v and coroutine.status(v) == "suspended" then
  44. coroutine.resume(v, false, error)
  45. end
  46. end
  47. end
  48. sys.subscribe("IP_ERROR_IND", function()errorInd('IP_ERROR_IND') end)
  49. --sys.subscribe('IP_SHUT_IND', function()errorInd('CLOSED') end)
  50. -- 创建socket函数
  51. local mt = {}
  52. mt.__index = mt
  53. local function socket(protocol, cert, tCoreExtPara)
  54. local ssl = protocol:match("SSL")
  55. local co = coroutine.running()
  56. if not co then
  57. log.warn("socket.socket: socket must be called in coroutine")
  58. return nil
  59. end
  60. -- 实例的属性参数表
  61. local o = {
  62. id = nil,
  63. protocol = protocol,
  64. tCoreExtPara = tCoreExtPara,
  65. ssl = ssl,
  66. cert = cert,
  67. co = co,
  68. input = {},
  69. output = {},
  70. wait = "",
  71. connected = false,
  72. iSubscribe = false,
  73. subMessage = nil,
  74. isBlock = false,
  75. msg = nil,
  76. rcvProcFnc = nil,
  77. }
  78. return setmetatable(o, mt)
  79. end
  80. --- 创建基于TCP的socket对象
  81. -- @bool[opt=nil] ssl 是否为ssl连接,true表示是,其余表示否
  82. -- @table[opt=nil] cert ssl连接需要的证书配置,只有ssl参数为true时,此参数才有意义,cert格式如下:
  83. -- {
  84. -- caCert = "ca.crt", --CA证书文件(Base64编码 X.509格式),如果存在此参数,则表示客户端会对服务器的证书进行校验;不存在则不校验
  85. -- clientCert = "client.crt", --客户端证书文件(Base64编码 X.509格式),服务器对客户端的证书进行校验时会用到此参数
  86. -- clientKey = "client.key", --客户端私钥文件(Base64编码 X.509格式)
  87. -- clientPassword = "123456", --客户端证书文件密码[可选]
  88. -- insist = 1, --证书中的域名校验失败时,是否坚持连接,默认为1,坚持连接,0为不连接
  89. -- hostNameFlag = 0, --服务器域名是否上报,默认为0,不上报,1为上报
  90. -- }
  91. -- @number[opt=nil] tCoreExtPara 建立链接扩展参数
  92. -- {
  93. -- rcvBufferSize = "num" --接收缓冲区大小,默认为0
  94. -- }
  95. -- @return client,创建成功返回socket客户端对象;创建失败返回nil
  96. -- @usage
  97. -- c = socket4G.tcp()
  98. -- c = socket4G.tcp(true)
  99. -- c = socket4G.tcp(true, {caCert="ca.crt"})
  100. -- c = socket4G.tcp(true, {caCert="ca.crt", clientCert="client.crt", clientKey="client.key"})
  101. -- c = socket4G.tcp(true, {caCert="ca.crt", clientCert="client.crt", clientKey="client.key", clientPassword="123456"})
  102. function tcp(ssl, cert, tCoreExtPara)
  103. return socket("TCP" .. (ssl == true and "SSL" or ""), (ssl == true) and cert or nil, tCoreExtPara)
  104. end
  105. --- 创建基于UDP的socket对象
  106. -- @return client,创建成功返回socket客户端对象;创建失败返回nil
  107. -- @usage c = socket4G.udp()
  108. function udp()
  109. return socket("UDP")
  110. end
  111. --- 连接服务器
  112. -- @string address 服务器地址,支持ip和域名
  113. -- @param port string或者number类型,服务器端口
  114. -- @number[opt=120] timeout 可选参数,连接超时时间,单位秒
  115. -- @return bool result true - 成功,false - 失败
  116. -- @return string ,id '0' -- '8' ,返回通道ID编号
  117. -- @usage
  118. -- socketClient = socket4G.tcp()
  119. -- socketClient:connect("www.baidu.com","80")
  120. function mt:connect(address, port, timeout)
  121. assert(self.co == coroutine.running(), "socket:connect: coroutine mismatch")
  122. if not link.isReady() then
  123. log.info("socket.connect: ip not ready")
  124. return false
  125. end
  126. if self.protocol=="TCP" then
  127. ipDataFlowAdd(120)
  128. elseif self.protocol=="TCPSSL" then
  129. ipDataFlowAdd(800)
  130. end
  131. self.address = address
  132. self.port = port
  133. local tCoreExtPara = self.tCoreExtPara or {}
  134. -- 默认缓冲区大小
  135. local rcvBufferSize = tCoreExtPara.rcvBufferSize or 0
  136. local socket_connect_fnc = (type(socketcore.sock_conn_ext)=="function") and socketcore.sock_conn_ext or socketcore.sock_conn
  137. if self.protocol == 'TCP' then
  138. self.id = socket_connect_fnc(0, address, port, rcvBufferSize)
  139. elseif self.protocol == 'TCPSSL' then
  140. local cert = {hostName = address}
  141. local insist = 1
  142. local hostNameFlag = 0
  143. if self.cert then
  144. if self.cert.caCert then
  145. if self.cert.caCert:sub(1, 1) ~= "/" then self.cert.caCert = "/lua/" .. self.cert.caCert end
  146. cert.caCert = io.readFile(self.cert.caCert)
  147. end
  148. if self.cert.clientCert then
  149. if self.cert.clientCert:sub(1, 1) ~= "/" then self.cert.clientCert = "/lua/" .. self.cert.clientCert end
  150. cert.clientCert = io.readFile(self.cert.clientCert)
  151. end
  152. if self.cert.clientKey then
  153. if self.cert.clientKey:sub(1, 1) ~= "/" then self.cert.clientKey = "/lua/" .. self.cert.clientKey end
  154. cert.clientKey = io.readFile(self.cert.clientKey)
  155. end
  156. insist = self.cert.insist == 0 and 0 or 1
  157. hostNameFlag = self.cert.hostNameFlag == 1 and 1 or 0
  158. end
  159. self.id = socket_connect_fnc(2, address, port, cert, rcvBufferSize, insist, nil, hostNameFlag)
  160. else
  161. self.id = socket_connect_fnc(1, address, port, rcvBufferSize)
  162. end
  163. if type(socketcore.sock_conn_ext)=="function" then
  164. if not self.id or self.id<0 then
  165. if self.id==-2 then
  166. require "http"
  167. --请求腾讯云免费HttpDns解析
  168. http.request("GET", "119.29.29.29/d?dn=" .. address, nil, nil, nil, 40000,
  169. function(result, statusCode, head, body)
  170. log.info("socket.httpDnsCb", result, statusCode, head, body)
  171. sys.publish("SOCKET_HTTPDNS_RESULT_"..address.."_"..port, result, statusCode, head, body)
  172. end)
  173. local _, result, statusCode, head, body = sys.waitUntil("SOCKET_HTTPDNS_RESULT_"..address.."_"..port)
  174. --DNS解析成功
  175. if result and statusCode == "200" and body and body:match("^[%d%.]+") then
  176. return self:connect(body:match("^([%d%.]+)"),port,timeout)
  177. end
  178. end
  179. self.id = nil
  180. end
  181. end
  182. if not self.id then
  183. log.info("socket:connect: core sock conn error", self.protocol, address, port, self.cert)
  184. return false
  185. end
  186. log.info("socket:connect-coreid,prot,addr,port,cert,timeout", self.id, self.protocol, address, port, self.cert, timeout or 120)
  187. sockets[self.id] = self
  188. self.wait = "SOCKET_CONNECT"
  189. self.timerId = sys.timerStart(coroutine.resume, (timeout or 120) * 1000, self.co, false, "TIMEOUT")
  190. local result, reason = coroutine.yield()
  191. if self.timerId and reason ~= "TIMEOUT" then sys.timerStop(self.timerId) end
  192. if not result then
  193. log.info("socket:connect: connect fail", reason)
  194. if reason == "RESPONSE" then
  195. sockets[self.id] = nil
  196. self.id = nil
  197. end
  198. sys.publish("LIB_SOCKET_CONNECT_FAIL_IND", self.ssl, self.protocol, address, port)
  199. return false
  200. end
  201. log.info("socket:connect: connect ok")
  202. if not self.connected then
  203. self.connected = true
  204. socketsConnected = socketsConnected+1
  205. sys.publish("SOCKET_ACTIVE", socketsConnected>0)
  206. end
  207. return true, self.id
  208. end
  209. --- 异步发送数据
  210. -- @number[opt=nil] keepAlive 服务器和客户端最大通信间隔时间,也叫心跳包最大时间,单位秒
  211. -- @string[opt=nil] pingreq 心跳包的字符串
  212. -- @return boole,false 失败,true 表示成功
  213. -- @usage
  214. -- socketClient = socket4G.tcp()
  215. -- socketClient:connect("www.baidu.com","80")
  216. -- while socketClient:asyncSelect() do end
  217. function mt:asyncSelect(keepAlive, pingreq)
  218. assert(self.co == coroutine.running(), "socket:asyncSelect: coroutine mismatch")
  219. if self.error then
  220. log.warn('socket.client:asyncSelect', 'error', self.error)
  221. return false
  222. end
  223. self.wait = "SOCKET_SEND"
  224. local dataLen = 0
  225. --log.info("socket.asyncSelect #self.output",#self.output)
  226. while #self.output ~= 0 do
  227. local data = table.concat(self.output)
  228. dataLen = string.len(data)
  229. self.output = {}
  230. local sendSize = self.protocol == "UDP" and 1472 or SENDSIZE
  231. for i = 1, dataLen, sendSize do
  232. -- 按最大MTU单元对data分包
  233. socketcore.sock_send(self.id, data:sub(i, i + sendSize - 1))
  234. ipDataFlowAdd((data:sub(i, i + sendSize - 1)):len()+(self.protocol == "UDP" and 40 or 80))
  235. if self.timeout then
  236. self.timerId = sys.timerStart(coroutine.resume, self.timeout * 1000, self.co, false, "TIMEOUT")
  237. end
  238. --log.info("socket.asyncSelect self.timeout",self.timeout)
  239. local result, reason = coroutine.yield()
  240. if self.timerId and reason ~= "TIMEOUT" then sys.timerStop(self.timerId) end
  241. sys.publish("SOCKET_ASYNC_SEND", result)
  242. if not result then
  243. sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
  244. --log.warn('socket.asyncSelect', 'send error')
  245. return false
  246. end
  247. end
  248. end
  249. self.wait = "SOCKET_WAIT"
  250. --log.info("socket.asyncSelect",dataLen,self.id)
  251. if dataLen>0 then sys.publish("SOCKET_SEND", self.id, true) end
  252. if keepAlive and keepAlive ~= 0 then
  253. if type(pingreq) == "function" then
  254. sys.timerStart(pingreq, keepAlive * 1000)
  255. else
  256. sys.timerStart(self.asyncSend, keepAlive * 1000, self, pingreq or "\0")
  257. end
  258. end
  259. return coroutine.yield()
  260. end
  261. function mt:getAsyncSend()
  262. if self.error then return 0 end
  263. return #(self.output)
  264. end
  265. --- 异步缓存待发送的数据
  266. -- @string data 数据
  267. -- @number[opt=nil] timeout 可选参数,发送超时时间,单位秒;为nil时表示不支持timeout
  268. -- @return result true - 成功,false - 失败
  269. -- @usage
  270. -- socketClient = socket4G.tcp()
  271. -- socketClient:connect("www.baidu.com","80")
  272. -- socketClient:asyncSend("12345678")
  273. function mt:asyncSend(data, timeout)
  274. if self.error then
  275. log.warn('socket.client:asyncSend', 'error', self.error)
  276. return false
  277. end
  278. self.timeout = timeout
  279. table.insert(self.output, data or "")
  280. --log.info("socket.asyncSend",self.wait)
  281. if self.wait == "SOCKET_WAIT" then coroutine.resume(self.co, true) end
  282. return true
  283. end
  284. --- 异步接收数据
  285. -- @return data 表示接收到的数据(如果是UDP,返回最新的一包数据;如果是TCP,返回所有收到的数据)
  286. -- ""表示未收到数据
  287. -- @usage
  288. -- socketClient = socket4G.tcp()
  289. -- socketClient:connect("www.baidu.com","80")
  290. -- data = socketClient:asyncRecv()
  291. function mt:asyncRecv()
  292. if #self.input == 0 then return "" end
  293. if self.protocol == "UDP" then
  294. return table.remove(self.input)
  295. else
  296. local s = table.concat(self.input)
  297. self.input = {}
  298. if self.isBlock then table.insert(self.input, socketcore.sock_recv(self.msg.socket_index, self.msg.recv_len)) end
  299. return s
  300. end
  301. end
  302. --- 同步发送数据
  303. -- @string data 数据
  304. -- 此处传入的数据长度和剩余可用内存有关,只要内存够用,可以随便传入数据
  305. -- 虽然说此处的数据长度没有特别限制,但是调用core中的socket发送接口时,每次最多发送11200字节的数据
  306. -- 例如此处传入的data长度是112000字节,则在这个send接口中,会循环10次,每次发送11200字节的数据
  307. -- @number[opt=120] timeout 可选参数,发送超时时间,单位秒
  308. -- @return result true - 成功,false - 失败
  309. -- @usage
  310. -- socketClient = socket4G.tcp()
  311. -- socketClient:connect("www.baidu.com","80")
  312. -- socketClient:send("12345678")
  313. function mt:send(data, timeout)
  314. assert(self.co == coroutine.running(), "socket:send: coroutine mismatch")
  315. if self.error then
  316. log.warn('socket.client:send', 'error', self.error)
  317. return false
  318. end
  319. log.debug("socket.send", "total " .. string.len(data or "") .. " bytes", "first 30 bytes", (data or ""):sub(1, 30))
  320. local sendSize = self.protocol == "UDP" and 1472 or SENDSIZE
  321. for i = 1, string.len(data or ""), sendSize do
  322. -- 按最大MTU单元对data分包
  323. self.wait = "SOCKET_SEND"
  324. socketcore.sock_send(self.id, data:sub(i, i + sendSize - 1))
  325. ipDataFlowAdd((data:sub(i, i + sendSize - 1)):len()+(self.protocol == "UDP" and 40 or 80))
  326. self.timerId = sys.timerStart(coroutine.resume, (timeout or 120) * 1000, self.co, false, "TIMEOUT")
  327. local result, reason = coroutine.yield()
  328. if self.timerId and reason ~= "TIMEOUT" then sys.timerStop(self.timerId) end
  329. if not result then
  330. log.info("socket:send", "send fail", reason)
  331. sys.publish("LIB_SOCKET_SEND_FAIL_IND", self.ssl, self.protocol, self.address, self.port)
  332. return false
  333. end
  334. end
  335. return true
  336. end
  337. --- 同步接收数据
  338. -- @number[opt=0] timeout 可选参数,接收超时时间,单位毫秒
  339. -- @string[opt=nil] msg 可选参数,控制socket所在的线程退出recv阻塞状态
  340. -- @bool[opt=nil] msgNoResume 可选参数,控制socket所在的线程退出recv阻塞状态
  341. -- false或者nil表示“在recv阻塞状态,收到msg消息,可以退出阻塞状态”,true表示不退出
  342. -- 此参数仅lib内部使用,应用脚本不要使用此参数
  343. -- @return result 数据接收结果
  344. -- true表示成功(接收到了数据)
  345. -- false表示失败(没有接收到数据)
  346. -- @return data
  347. -- 如果result为true,data表示接收到的数据(如果是UDP,返回最新的一包数据;如果是TCP,返回所有收到的数据)
  348. -- 如果result为false,超时失败,data为"timeout"
  349. -- 如果result为false,msg控制退出,data为msg的字符串
  350. -- 如果result为false,socket连接被动断开控制退出,data为"CLOSED"
  351. -- 如果result为false,PDP断开连接控制退出,data为"IP_ERROR_IND"
  352. -- @return param 如果是msg控制退出,param的值是msg的参数
  353. -- @usage
  354. -- socketClient = socket4G.tcp()
  355. -- socketClient:connect("www.baidu.com","80")
  356. -- result,data = socketClient:recv(60000,"APP_SOCKET_SEND_DATA")
  357. function mt:recv(timeout, msg, msgNoResume)
  358. assert(self.co == coroutine.running(), "socket:recv: coroutine mismatch")
  359. if self.error then
  360. log.warn('socket.client:recv', 'error', self.error)
  361. return false
  362. end
  363. self.msgNoResume = msgNoResume
  364. if msg and not self.iSubscribe then
  365. self.iSubscribe = msg
  366. self.subMessage = function(data)
  367. --if data then table.insert(self.output, data) end
  368. if self.wait == "+RECEIVE" and not self.msgNoResume then
  369. if data then table.insert(self.output, data) end
  370. coroutine.resume(self.co, 0xAA)
  371. end
  372. end
  373. sys.subscribe(msg, self.subMessage)
  374. end
  375. if msg and #self.output > 0 then sys.publish(msg, false) end
  376. if #self.input == 0 then
  377. self.wait = "+RECEIVE"
  378. if timeout and timeout > 0 then
  379. local r, s = sys.wait(timeout)
  380. if r == nil then
  381. return false, "timeout"
  382. elseif r == 0xAA then
  383. local dat = table.concat(self.output)
  384. self.output = {}
  385. return false, msg, dat
  386. else
  387. return r, s
  388. end
  389. else
  390. local r, s = coroutine.yield()
  391. if r == 0xAA then
  392. local dat = table.concat(self.output)
  393. self.output = {}
  394. return false, msg, dat
  395. else
  396. return r, s
  397. end
  398. end
  399. end
  400. if self.protocol == "UDP" then
  401. local s = table.remove(self.input)
  402. return true, s
  403. else
  404. log.warn("-------------------使用缓冲区---------------")
  405. local s = table.concat(self.input)
  406. self.input = {}
  407. if self.isBlock then table.insert(self.input, socketcore.sock_recv(self.msg.socket_index, self.msg.recv_len)) end
  408. return true, s
  409. end
  410. end
  411. --- 主动关闭并且销毁一个socket
  412. -- @return nil
  413. -- @usage
  414. -- socketClient = socket4G.tcp()
  415. -- socketClient:connect("www.baidu.com","80")
  416. -- socketClient:close()
  417. function mt:close()
  418. assert(self.co == coroutine.running(), "socket:close: coroutine mismatch")
  419. if self.iSubscribe then
  420. sys.unsubscribe(self.iSubscribe, self.subMessage)
  421. self.iSubscribe = false
  422. end
  423. --此处不要再判断状态,否则在连接超时失败时,conneted状态仍然是未连接,会导致无法close
  424. --if self.connected then
  425. log.info("socket:sock_close", self.id)
  426. local result, reason
  427. if self.id then
  428. socketcore.sock_close(self.id)
  429. if self.protocol~="UDP" then ipDataFlowAdd(120) end
  430. self.wait = "SOCKET_CLOSE"
  431. while true do
  432. result, reason = coroutine.yield()
  433. if reason == "RESPONSE" then break end
  434. end
  435. end
  436. if self.connected then
  437. self.connected = false
  438. if socketsConnected>0 then
  439. socketsConnected = socketsConnected-1
  440. end
  441. sys.publish("SOCKET_ACTIVE", socketsConnected>0)
  442. end
  443. if self.input then
  444. self.input = {}
  445. end
  446. --end
  447. if self.id ~= nil then
  448. sockets[self.id] = nil
  449. end
  450. end
  451. -- socket接收自定义控制处理
  452. -- @function[opt=nil] rcvCbFnc,socket接收到数据后,执行的回调函数,回调函数的调用形式为:
  453. -- rcvCbFnc(readFnc,socketIndex,rcvDataLen)
  454. -- rcvCbFnc内部,会判断是否读取数据,如果读取,执行readFnc(socketIndex,rcvDataLen),返回true;否则返回false或者nil
  455. function mt:setRcvProc(rcvCbFnc)
  456. assert(self.co == coroutine.running(), "socket:setRcvProc: coroutine mismatch")
  457. self.rcvProcFnc = rcvCbFnc
  458. end
  459. local function on_response(msg)
  460. local t = {
  461. [rtos.MSG_SOCK_CLOSE_CNF] = 'SOCKET_CLOSE',
  462. [rtos.MSG_SOCK_SEND_CNF] = 'SOCKET_SEND',
  463. [rtos.MSG_SOCK_CONN_CNF] = 'SOCKET_CONNECT',
  464. }
  465. if not sockets[msg.socket_index] then
  466. log.warn('response on nil socket', msg.socket_index, t[msg.id], msg.result)
  467. return
  468. end
  469. if sockets[msg.socket_index].wait ~= t[msg.id] then
  470. log.warn('response on invalid wait', sockets[msg.socket_index].id, sockets[msg.socket_index].wait, t[msg.id], msg.socket_index)
  471. return
  472. end
  473. log.info("socket:on_response:", msg.socket_index, t[msg.id], msg.result)
  474. if type(socketcore.sock_destroy) == "function" then
  475. if (msg.id == rtos.MSG_SOCK_CONN_CNF and msg.result ~= 0) or msg.id == rtos.MSG_SOCK_CLOSE_CNF then
  476. socketcore.sock_destroy(msg.socket_index)
  477. end
  478. end
  479. coroutine.resume(sockets[msg.socket_index].co, msg.result == 0, "RESPONSE")
  480. end
  481. rtos.on(rtos.MSG_SOCK_CLOSE_CNF, on_response)
  482. rtos.on(rtos.MSG_SOCK_CONN_CNF, on_response)
  483. rtos.on(rtos.MSG_SOCK_SEND_CNF, on_response)
  484. rtos.on(rtos.MSG_SOCK_CLOSE_IND, function(msg)
  485. log.info("socket.rtos.MSG_SOCK_CLOSE_IND")
  486. if not sockets[msg.socket_index] then
  487. log.warn('close ind on nil socket', msg.socket_index, msg.id)
  488. return
  489. end
  490. if sockets[msg.socket_index].connected then
  491. sockets[msg.socket_index].connected = false
  492. if socketsConnected>0 then
  493. socketsConnected = socketsConnected-1
  494. end
  495. sys.publish("SOCKET_ACTIVE", socketsConnected>0)
  496. end
  497. sockets[msg.socket_index].error = 'CLOSED'
  498. --[[
  499. if type(socketcore.sock_destroy) == "function" then
  500. socketcore.sock_destroy(msg.socket_index)
  501. end]]
  502. 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)
  503. coroutine.resume(sockets[msg.socket_index].co, false, "CLOSED")
  504. end)
  505. rtos.on(rtos.MSG_SOCK_RECV_IND, function(msg)
  506. if not sockets[msg.socket_index] then
  507. log.warn('close ind on nil socket', msg.socket_index, msg.id)
  508. return
  509. end
  510. -- local s = socketcore.sock_recv(msg.socket_index, msg.recv_len)
  511. -- log.debug("socket.recv", "total " .. msg.recv_len .. " bytes", "first " .. 30 .. " bytes", s:sub(1, 30))
  512. log.debug("socket.recv", msg.recv_len, sockets[msg.socket_index].rcvProcFnc)
  513. ipDataFlowAdd(msg.recv_len+(sockets[msg.socket_index].protocol=="UDP" and 40 or 80))
  514. if sockets[msg.socket_index].rcvProcFnc then
  515. sockets[msg.socket_index].rcvProcFnc(socketcore.sock_recv, msg.socket_index, msg.recv_len)
  516. else
  517. if sockets[msg.socket_index].wait == "+RECEIVE" then
  518. coroutine.resume(sockets[msg.socket_index].co, true, socketcore.sock_recv(msg.socket_index, msg.recv_len))
  519. else -- 数据进缓冲区,缓冲区溢出采用覆盖模式
  520. if #sockets[msg.socket_index].input > INDEX_MAX then
  521. log.error("socket recv", "out of stack", "block")
  522. -- sockets[msg.socket_index].input = {}
  523. sockets[msg.socket_index].isBlock = true
  524. sockets[msg.socket_index].msg = msg
  525. else
  526. sockets[msg.socket_index].isBlock = false
  527. table.insert(sockets[msg.socket_index].input, socketcore.sock_recv(msg.socket_index, msg.recv_len))
  528. end
  529. sys.publish("SOCKET_RECV", msg.socket_index)
  530. end
  531. end
  532. end)
  533. --- 设置TCP层自动重传的参数
  534. -- @number[opt=4] retryCnt 重传次数;取值范围0到12
  535. -- @number[opt=16] retryMaxTimeout 限制每次重传允许的最大超时时间(单位秒),取值范围1到16
  536. -- @return nil
  537. -- @usage
  538. -- setTcpResendPara(3,8)
  539. -- setTcpResendPara(4,16)
  540. function setTcpResendPara(retryCnt, retryMaxTimeout)
  541. ril.request("AT+TCPUSERPARAM=6," .. (retryCnt or 4) .. ",7200," .. (retryMaxTimeout or 16))
  542. end
  543. --- 设置域名解析参数
  544. -- 注意:0027以及之后的core版本才支持此功能
  545. -- @number[opt=4] retryCnt 重传次数;取值范围1到8
  546. -- @number[opt=4] retryTimeoutMulti 重传超时时间倍数,取值范围1到5
  547. -- 第n次重传超时时间的计算方式为:第n次的重传超时基数*retryTimeoutMulti,单位为秒
  548. -- 重传超时基数表为{1, 1, 2, 4, 4, 4, 4, 4}
  549. -- 第1次重传超时时间为:1*retryTimeoutMulti 秒
  550. -- 第2次重传超时时间为:1*retryTimeoutMulti 秒
  551. -- 第3次重传超时时间为:2*retryTimeoutMulti 秒
  552. -- ...........................................
  553. -- 第8次重传超时时间为:8*retryTimeoutMulti 秒
  554. -- @return nil
  555. -- @usage
  556. -- socket4G.setDnsParsePara(8,5)
  557. function setDnsParsePara(retryCnt, retryTimeoutMulti)
  558. ril.request("AT*DNSTMOUT="..(retryCnt or 4)..","..(retryTimeoutMulti or 4))
  559. end
  560. -- 打印所有socket的状态
  561. -- @return 无
  562. -- @usage socket4G.printStatus()
  563. function printStatus()
  564. for _, client in pairs(sockets) do
  565. for k, v in pairs(client) do
  566. log.info('socket.printStatus', 'client', client.id, k, v)
  567. end
  568. end
  569. end
  570. --- 设置数据传输后,允许进入休眠状态的延时时长
  571. -- 3024版本以及之后的版本才支持此功能
  572. -- 此功能设置的参数,设置成功后,掉电会自动保存
  573. -- @number tm 数据传输后,允许进入休眠状态的延时时长,单位为秒,取值范围1到20
  574. -- 注意:此时间越短,允许进入休眠状态越快,功耗越低;但是在某些网络环境中,此时间越短,可能会造成数据传输不稳定
  575. -- 建议在可以接受的功耗范围内,此值设置的越大越好
  576. -- 如果没有设置此参数,此延时时长是和基站的配置有关,一般来说是10秒左右
  577. -- @return nil
  578. -- @usage
  579. -- socket4G.setLowPower(5)
  580. function setLowPower(tm)
  581. ril.request("AT*RTIME="..tm)
  582. end
  583. local function ipStatisTimerCb()
  584. if ipDataFlow~=0 then
  585. sys.publish("LIB_IP_STATIS_RPT",ipDataFlow)
  586. ipDataFlow = 0
  587. end
  588. end
  589. function setIpStatis(interval)
  590. if ipStatisInterval~=interval then
  591. ipStatisInterval = interval
  592. ipStatisTimerCb()
  593. if interval==0 then
  594. sys.timerStop(ipStatisTimerCb)
  595. else
  596. sys.timerLoopStart(ipStatisTimerCb,interval*1000)
  597. end
  598. end
  599. end
  600. --setDnsParsePara(4,4)
  601. --setTcpResendPara(1,16)