socket.lua 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. --- 数据链路激活、SOCKET管理(创建、连接、数据收发、状态维护)
  2. -- @module link
  3. -- @author 稀饭放姜、小强
  4. -- @license MIT
  5. -- @copyright openLuat.com
  6. -- @release 2017.9.25
  7. require "link"
  8. module(..., package.seeall)
  9. local valid = {"0", "1", "2", "3", "4", "5", "6", "7"}
  10. local sockets = {}
  11. -- 单次发送数据最大值
  12. local SENDSIZE = 1460
  13. -- 缓冲区最大下标
  14. local INDEX_MAX = 49
  15. --- SOCKET 是否有可用
  16. -- @return 可用true,不可用false
  17. socket.isReady = link.isReady
  18. local function errorInd(error)
  19. for _, c in pairs(sockets) do -- IP状态出错时,通知所有已连接的socket
  20. if c.connected then
  21. if error == 'CLOSED' then c.connected = false end
  22. c.error = error
  23. coroutine.resume(c.co, false)
  24. end
  25. end
  26. end
  27. sys.subscribe("IP_ERROR_IND", function() errorInd('IP_ERROR_IND') end)
  28. sys.subscribe('IP_SHUT_IND', function() errorInd('CLOSED') end)
  29. --订阅rsp返回的消息处理函数
  30. local function onSocketURC(data, prefix)
  31. local id, result = string.match(data, "(%d), *([%u :%d]+)")
  32. if not sockets[id] then
  33. log.error('socket: response on nil socket', cmd, response)
  34. return
  35. end
  36. if result == "CONNECT OK" or result == "CONNECT FAIL" then
  37. if sockets[id].wait == "+CIPSTART" then
  38. coroutine.resume(sockets[id].co, result == "CONNECT OK")
  39. else
  40. log.error("socket: error urc", sockets[id].wait)
  41. end
  42. return
  43. end
  44. if string.find(result, "ERROR") or result == "CLOSED" then
  45. if result == 'CLOSED' then sockets[id].connected = false end
  46. sockets[id].error = result
  47. coroutine.resume(sockets[id].co, false)
  48. end
  49. end
  50. -- 创建socket函数
  51. local mt = {__index = {}}
  52. local function socket(protocol)
  53. local id = table.remove(valid)
  54. if not id then
  55. log.warn("socket.socket: too many sockets")
  56. return nil
  57. end
  58. local co = coroutine.running()
  59. if not co then
  60. log.warn("socket.socket: socket must be called in coroutine")
  61. return nil
  62. end
  63. -- 实例的属性参数表
  64. local o = {
  65. id = id,
  66. protocol = protocol,
  67. co = co,
  68. input = {},
  69. wait = "",
  70. }
  71. sockets[id] = o
  72. return setmetatable(o, mt)
  73. end
  74. --- 创建基于TCP的socket对象
  75. -- @return 无
  76. -- @usage c = socket.tcp()
  77. function tcp()
  78. return socket("TCP")
  79. end
  80. --- 创建基于UDP的socket对象
  81. -- @return 无
  82. -- @usage c = socket.udp()
  83. function udp()
  84. return socket("UDP")
  85. end
  86. --- socket:connect 连接服务器
  87. -- @param address ip地址或者域名
  88. -- @param port 端口
  89. -- @return result true - 成功,false - 失败
  90. -- @usage c = socket.tcp(); c:connect();
  91. function mt.__index:connect(address, port)
  92. assert(self.co == coroutine.running(), "socket:connect: coroutine mismatch")
  93. if not link.isReady() then
  94. log.info("socket.connect: ip not ready")
  95. return false
  96. end
  97. if cc and cc.anycallexist() then
  98. log.info("socket:connect: call exist, cannot connect")
  99. return false
  100. end
  101. ril.request(string.format("AT+CIPSTART=%d,\"%s\",\"%s\",%s", self.id, self.protocol, address, port))
  102. ril.regurc(self.id, onSocketURC)
  103. self.wait = "+CIPSTART"
  104. if coroutine.yield() == false then return false end
  105. self.connected = true
  106. return true
  107. end
  108. --- socket:send
  109. -- @param data 数据
  110. -- @return result true - 成功,false - 失败
  111. -- @usage c = socket.tcp(); c:connect(); c:send("12345678");
  112. function mt.__index:send(data)
  113. assert(self.co == coroutine.running(), "socket:send: coroutine mismatch")
  114. if self.error then
  115. log.warn('socket.client:send', 'error', self.error)
  116. return false
  117. end
  118. for i = 1, string.len(data), SENDSIZE do
  119. -- 按最大MTU单元对data分包
  120. local stepData = string.sub(data, i, i + SENDSIZE - 1)
  121. --发送AT命令执行数据发送
  122. ril.request(string.format("AT+CIPSEND=%d,%d", self.id, string.len(stepData)), stepData)
  123. self.wait = "+CIPSEND"
  124. if not coroutine.yield() then return false end
  125. end
  126. return true
  127. end
  128. --- socket:recv([timeout])
  129. -- @param timeout 可选参数,接收超时时间
  130. -- @return result true - 成功,false - 失败
  131. -- @return data 如果成功的话,返回接收到的数据,超时时返回错误为"timeout"
  132. -- @usage c = socket.tcp(); c:connect(); result, data = c:recv()
  133. function mt.__index:recv(timeout)
  134. assert(self.co == coroutine.running(), "socket:recv: coroutine mismatch")
  135. if self.error then
  136. log.warn('socket.client:recv', 'error', self.error)
  137. return false
  138. end
  139. if #self.input == 0 then
  140. self.wait = "+RECEIVE"
  141. if timeout then
  142. local r, s = sys.wait(timeout)
  143. if r == nil then
  144. return false, "timeout"
  145. else
  146. return r, s
  147. end
  148. else
  149. return coroutine.yield()
  150. end
  151. end
  152. if self.protocol == "UDP" then
  153. return true, table.remove(self.input)
  154. else
  155. local s = table.concat(self.input)
  156. self.input = {}
  157. return true, s
  158. end
  159. end
  160. --- socket:close
  161. -- @return 无
  162. -- @usage c = socket.tcp(); c:connect(); c:send("123"); c:close()
  163. function mt.__index:close()
  164. assert(self.co == coroutine.running(), "socket:close: coroutine mismatch")
  165. if self.connected then
  166. self.connected = false
  167. ril.request("AT+CIPCLOSE=" .. self.id)
  168. self.wait = "+CIPCLOSE"
  169. coroutine.yield()
  170. end
  171. ril.deregurc(self.id, onSocketURC)
  172. table.insert(valid, 1, self.id)
  173. sockets[self.id] = nil
  174. self.id = nil
  175. end
  176. local function onResponse(cmd, success, response, intermediate)
  177. local prefix = string.match(cmd, "AT(%+%u+)")
  178. local id = string.match(cmd, "AT%+%u+=(%d)")
  179. if not sockets[id] then
  180. log.warn('socket: response on nil socket', cmd, response)
  181. return
  182. end
  183. if sockets[id].wait == prefix then
  184. if prefix == "+CIPSTART" and success then
  185. -- CIPSTART 返回OK只是表示被接受
  186. return
  187. end
  188. if prefix == '+CIPSEND' and response:match("%d, *([%u%d :]+)") ~= 'SEND OK' then
  189. success = false
  190. end
  191. if not success then sockets[id].error = response end
  192. coroutine.resume(sockets[id].co, success)
  193. end
  194. end
  195. ril.regrsp("+CIPCLOSE", onResponse)
  196. ril.regrsp("+CIPSEND", onResponse)
  197. ril.regrsp("+CIPSTART", onResponse)
  198. ril.regurc("+RECEIVE", function(urc)
  199. local id, len = string.match(urc, ",(%d),(%d+)", string.len("+RECEIVE") + 1)
  200. len = tonumber(len)
  201. if len == 0 then return urc end
  202. local cache = {}
  203. local function filter(data)
  204. --剩余未收到的数据长度
  205. if string.len(data) >= len then -- at通道的内容比剩余未收到的数据多
  206. -- 截取网络发来的数据
  207. table.insert(cache, string.sub(data, 1, len))
  208. -- 剩下的数据仍按at进行后续处理
  209. data = string.sub(data, len + 1, -1)
  210. if not sockets[id] then
  211. log.warn('socket: receive on nil socket', id)
  212. else
  213. local s = table.concat(cache)
  214. if sockets[id].wait == "+RECEIVE" then
  215. coroutine.resume(sockets[id].co, true, s)
  216. else -- 数据进缓冲区,缓冲区溢出采用覆盖模式
  217. if #sockets[id].input > INDEX_MAX then sockets[id].input = {} end
  218. table.insert(sockets[id].input, s)
  219. end
  220. end
  221. return data
  222. else
  223. table.insert(cache, data)
  224. len = len - string.len(data)
  225. return "", filter
  226. end
  227. end
  228. return filter
  229. end)
  230. function printStatus()
  231. log.info('socket.printStatus', 'valid id', table.concat(valid))
  232. for _, client in pairs(sockets) do
  233. for k, v in pairs(client) do
  234. log.info('socket.printStatus', 'client', client.id, k, v)
  235. end
  236. end
  237. end