luat_lib_websocket.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. /*
  2. @module websocket
  3. @summary websocket客户端
  4. @version 1.0
  5. @date 2022.11.28
  6. @demo websocket
  7. @usage
  8. local wsc = nil
  9. if websocket then
  10. wsc = websocket.create(nil, "ws://nutz.cn/websocket")
  11. wsc:autoreconn(true, 3000) -- 自动重连机制
  12. wsc:on(function(wsc, event, data)
  13. log.info("wsc", event, data)
  14. if event == "conack" then
  15. wsc:send((json.encode({action="login",device_id=device_id})))
  16. end
  17. end)
  18. wsc:connect()
  19. --sys.waitUntil("websocket_conack", 15000)
  20. while true do
  21. sys.wait(45000)
  22. if wsc:ready() then
  23. wsc:send((json.encode({action="echo", msg=os.date()})))
  24. end
  25. end
  26. wsc:close()
  27. wsc = nil
  28. end
  29. */
  30. #include "luat_base.h"
  31. #include "luat_network_adapter.h"
  32. #include "luat_rtos.h"
  33. #include "luat_zbuff.h"
  34. #include "luat_malloc.h"
  35. #include "luat_websocket.h"
  36. #define LUAT_LOG_TAG "websocket"
  37. #include "luat_log.h"
  38. #define LUAT_WEBSOCKET_CTRL_TYPE "WS*"
  39. static luat_websocket_ctrl_t *get_websocket_ctrl(lua_State *L)
  40. {
  41. if (luaL_testudata(L, 1, LUAT_WEBSOCKET_CTRL_TYPE))
  42. {
  43. return ((luat_websocket_ctrl_t *)luaL_checkudata(L, 1, LUAT_WEBSOCKET_CTRL_TYPE));
  44. }
  45. else
  46. {
  47. return ((luat_websocket_ctrl_t *)lua_touserdata(L, 1));
  48. }
  49. }
  50. static int32_t l_websocket_callback(lua_State *L, void *ptr)
  51. {
  52. rtos_msg_t *msg = (rtos_msg_t *)lua_topointer(L, -1);
  53. luat_websocket_ctrl_t *websocket_ctrl = (luat_websocket_ctrl_t *)msg->ptr;
  54. luat_websocket_pkg_t pkg = {0};
  55. // size_t payload_size = 0;
  56. switch (msg->arg1)
  57. {
  58. case WEBSOCKET_MSG_TIMER_PING:
  59. {
  60. luat_websocket_ping(websocket_ctrl);
  61. break;
  62. }
  63. case WEBSOCKET_MSG_PUBLISH:
  64. {
  65. if (websocket_ctrl->websocket_cb)
  66. {
  67. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_cb);
  68. if (lua_isfunction(L, -1))
  69. {
  70. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_ref);
  71. lua_pushstring(L, "recv");
  72. luat_websocket_payload((char *)msg->arg2, &pkg, 64 * 1024);
  73. lua_pushlstring(L, pkg.payload, pkg.plen);
  74. lua_pushinteger(L, pkg.FIN);
  75. lua_pushinteger(L, pkg.OPT_CODE);
  76. lua_call(L, 5, 0);
  77. }
  78. }
  79. luat_heap_free((char *)msg->arg2);
  80. break;
  81. }
  82. case WEBSOCKET_MSG_CONNACK:
  83. {
  84. if (websocket_ctrl->websocket_cb)
  85. {
  86. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_cb);
  87. if (lua_isfunction(L, -1))
  88. {
  89. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_ref);
  90. lua_pushstring(L, "conack");
  91. lua_call(L, 2, 0);
  92. }
  93. lua_getglobal(L, "sys_pub");
  94. if (lua_isfunction(L, -1))
  95. {
  96. lua_pushstring(L, "WEBSOCKET_CONNACK");
  97. lua_geti(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_ref);
  98. lua_call(L, 2, 0);
  99. }
  100. }
  101. break;
  102. }
  103. case WEBSOCKET_MSG_RELEASE:
  104. {
  105. if (websocket_ctrl->websocket_ref)
  106. {
  107. luaL_unref(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_ref);
  108. websocket_ctrl->websocket_ref = 0;
  109. }
  110. break;
  111. }
  112. default:
  113. {
  114. LLOGD("l_websocket_callback error arg1:%d", msg->arg1);
  115. break;
  116. }
  117. }
  118. // lua_pushinteger(L, 0);
  119. return 0;
  120. }
  121. int l_luat_websocket_msg_cb(luat_websocket_ctrl_t *ctrl, int arg1, int arg2)
  122. {
  123. rtos_msg_t msg = {
  124. .handler = l_websocket_callback,
  125. .ptr = ctrl,
  126. .arg1 = arg1,
  127. .arg2 = arg2,
  128. };
  129. luat_msgbus_put(&msg, 0);
  130. return 0;
  131. }
  132. /*
  133. 配置是否打开debug信息
  134. @api wsc:debug(onoff)
  135. @boolean 是否打开debug开关
  136. @return nil 无返回值
  137. @usage wsc:debug(true)
  138. */
  139. static int l_websocket_set_debug(lua_State *L)
  140. {
  141. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  142. if (lua_isboolean(L, 2))
  143. {
  144. websocket_ctrl->netc->is_debug = lua_toboolean(L, 2);
  145. }
  146. return 0;
  147. }
  148. /*
  149. websocket客户端创建
  150. @api websocket.create(adapter, url)
  151. @int 适配器序号, 只能是network.ETH0,network.STA,network.AP,如果不填,会选择最后一个注册的适配器
  152. @string 连接字符串,参考usage
  153. @return userdata 若成功会返回websocket客户端实例,否则返回nil
  154. @usage
  155. -- 普通TCP链接
  156. wsc = websocket.create(nil,"ws://air32.cn/abc")
  157. -- 加密TCP链接
  158. wsc = websocket.create(nil,"wss://air32.cn/abc")
  159. */
  160. static int l_websocket_create(lua_State *L)
  161. {
  162. int ret = 0;
  163. int adapter_index = luaL_optinteger(L, 1, network_get_last_register_adapter());
  164. if (adapter_index < 0 || adapter_index >= NW_ADAPTER_QTY)
  165. {
  166. return 0;
  167. }
  168. luat_websocket_ctrl_t *websocket_ctrl = (luat_websocket_ctrl_t *)lua_newuserdata(L, sizeof(luat_websocket_ctrl_t));
  169. if (!websocket_ctrl)
  170. {
  171. LLOGE("out of memory when malloc websocket_ctrl");
  172. return 0;
  173. }
  174. ret = luat_websocket_init(websocket_ctrl, adapter_index);
  175. if (ret)
  176. {
  177. LLOGE("websocket init FAID ret %d", ret);
  178. return 0;
  179. }
  180. luat_websocket_connopts_t opts = {0};
  181. // 连接参数相关
  182. const char *ip;
  183. size_t ip_len = 0;
  184. #ifdef LUAT_USE_LWIP
  185. websocket_ctrl->ip_addr.type = 0xff;
  186. #else
  187. websocket_ctrl->ip_addr.is_ipv6 = 0xff;
  188. #endif
  189. opts.url = luaL_checklstring(L, 2, &ip_len);
  190. ret = luat_websocket_set_connopts(websocket_ctrl, luaL_checklstring(L, 2, &ip_len));
  191. // TODO 判断ret, 如果初始化失败, 应该终止
  192. luaL_setmetatable(L, LUAT_WEBSOCKET_CTRL_TYPE);
  193. lua_pushvalue(L, -1);
  194. websocket_ctrl->websocket_ref = luaL_ref(L, LUA_REGISTRYINDEX);
  195. return 1;
  196. }
  197. /*
  198. 注册websocket回调
  199. @api wsc:on(cb)
  200. @function cb websocket回调,参数包括websocket_client, event, data, payload
  201. @return nil 无返回值
  202. @usage
  203. wsc:on(function(websocket_client, event, data, payload)
  204. -- 用户自定义代码
  205. log.info("websocket", "event", event, websocket_client, data, payload)
  206. end)
  207. */
  208. static int l_websocket_on(lua_State *L)
  209. {
  210. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  211. if (websocket_ctrl->websocket_cb != 0)
  212. {
  213. luaL_unref(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_cb);
  214. websocket_ctrl->websocket_cb = 0;
  215. }
  216. if (lua_isfunction(L, 2))
  217. {
  218. lua_pushvalue(L, 2);
  219. websocket_ctrl->websocket_cb = luaL_ref(L, LUA_REGISTRYINDEX);
  220. }
  221. return 0;
  222. }
  223. /*
  224. 连接服务器
  225. @api wsc:connect()
  226. @return boolean 发起成功返回true, 否则返回false
  227. @usage
  228. -- 开始建立连接
  229. wsc:connect()
  230. -- 本函数仅代表发起成功, 后续仍需根据ready函数判断websocket是否连接正常
  231. */
  232. static int l_websocket_connect(lua_State *L)
  233. {
  234. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  235. int ret = luat_websocket_connect(websocket_ctrl);
  236. if (ret)
  237. {
  238. LLOGE("socket connect ret=%d\n", ret);
  239. luat_websocket_close_socket(websocket_ctrl);
  240. lua_pushboolean(L, 0);
  241. return 1;
  242. }
  243. lua_pushboolean(L, 1);
  244. return 1;
  245. }
  246. /*
  247. 自动重连
  248. @api wsc:autoreconn(reconnect, reconnect_time)
  249. @bool 是否自动重连
  250. @int 自动重连周期 单位ms 默认3000ms
  251. @usage
  252. wsc:autoreconn(true)
  253. */
  254. static int l_websocket_autoreconn(lua_State *L)
  255. {
  256. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  257. if (lua_isboolean(L, 2))
  258. {
  259. websocket_ctrl->reconnect = lua_toboolean(L, 2);
  260. }
  261. websocket_ctrl->reconnect_time = luaL_optinteger(L, 3, 3000);
  262. return 0;
  263. }
  264. /*
  265. 发布消息
  266. @api wsc:send(data, fin, opt)
  267. @string 待发送的数据,必填
  268. @int 是否为最后一帧,默认1
  269. @int 操作码, 默认为字符串帧
  270. @return int 消息id, 当qos为1或2时会有效值. 若底层返回是否, 会返回nil
  271. @usage
  272. wsc:publish("/luatos/123456", "123")
  273. */
  274. static int l_websocket_send(lua_State *L)
  275. {
  276. uint32_t payload_len = 0;
  277. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  278. const char *payload = NULL;
  279. luat_zbuff_t *buff = NULL;
  280. int ret = 0;
  281. if (lua_isstring(L, 2))
  282. {
  283. payload = luaL_checklstring(L, 2, &payload_len);
  284. }
  285. else if (luaL_testudata(L, 2, LUAT_ZBUFF_TYPE))
  286. {
  287. buff = ((luat_zbuff_t *)luaL_checkudata(L, 2, LUAT_ZBUFF_TYPE));
  288. payload = buff->addr;
  289. payload_len = buff->used;
  290. }
  291. else
  292. {
  293. LLOGD("only support string or zbuff");
  294. return 0;
  295. }
  296. luat_websocket_pkg_t pkg = {
  297. .FIN = 1,
  298. .OPT_CODE = 0x01,
  299. .plen = payload_len,
  300. .payload = payload};
  301. ret = luat_websocket_send_frame(websocket_ctrl, &pkg);
  302. return 0;
  303. }
  304. /*
  305. websocket客户端关闭(关闭后资源释放无法再使用)
  306. @api wsc:close()
  307. @usage
  308. wsc:close()
  309. */
  310. static int l_websocket_close(lua_State *L)
  311. {
  312. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  313. // websocket_disconnect(&(websocket_ctrl->broker));
  314. luat_websocket_close_socket(websocket_ctrl);
  315. if (websocket_ctrl->websocket_cb != 0)
  316. {
  317. luaL_unref(L, LUA_REGISTRYINDEX, websocket_ctrl->websocket_cb);
  318. websocket_ctrl->websocket_cb = 0;
  319. }
  320. luat_websocket_release_socket(websocket_ctrl);
  321. return 0;
  322. }
  323. /*
  324. websocket客户端是否就绪
  325. @api wsc:ready()
  326. @return bool 客户端是否就绪
  327. @usage
  328. local error = wsc:ready()
  329. */
  330. static int l_websocket_ready(lua_State *L)
  331. {
  332. luat_websocket_ctrl_t *websocket_ctrl = get_websocket_ctrl(L);
  333. lua_pushboolean(L, websocket_ctrl->websocket_state > 0 ? 1 : 0);
  334. return 1;
  335. }
  336. static int _websocket_struct_newindex(lua_State *L);
  337. void luat_websocket_struct_init(lua_State *L)
  338. {
  339. luaL_newmetatable(L, LUAT_WEBSOCKET_CTRL_TYPE);
  340. lua_pushcfunction(L, _websocket_struct_newindex);
  341. lua_setfield(L, -2, "__index");
  342. lua_pop(L, 1);
  343. }
  344. #include "rotable2.h"
  345. static const rotable_Reg_t reg_websocket[] =
  346. {
  347. {"create", ROREG_FUNC(l_websocket_create)},
  348. {"on", ROREG_FUNC(l_websocket_on)},
  349. {"connect", ROREG_FUNC(l_websocket_connect)},
  350. {"autoreconn", ROREG_FUNC(l_websocket_autoreconn)},
  351. {"send", ROREG_FUNC(l_websocket_send)},
  352. {"close", ROREG_FUNC(l_websocket_close)},
  353. {"ready", ROREG_FUNC(l_websocket_ready)},
  354. {NULL, ROREG_INT(0)}};
  355. static int _websocket_struct_newindex(lua_State *L)
  356. {
  357. rotable_Reg_t *reg = reg_websocket;
  358. const char *key = luaL_checkstring(L, 2);
  359. while (1)
  360. {
  361. if (reg->name == NULL)
  362. return 0;
  363. if (!strcmp(reg->name, key))
  364. {
  365. lua_pushcfunction(L, reg->value.value.func);
  366. return 1;
  367. }
  368. reg++;
  369. }
  370. // return 0;
  371. }
  372. static const rotable_Reg_t reg_websocket_emtry[] =
  373. {
  374. {NULL, ROREG_INT(0)}};
  375. LUAMOD_API int luaopen_websocket(lua_State *L)
  376. {
  377. #ifdef LUAT_USE_NETWORK
  378. luat_newlib2(L, reg_websocket);
  379. luat_websocket_struct_init(L);
  380. return 1;
  381. #else
  382. LLOGE("websocket require network enable!!");
  383. luat_newlib2(L, reg_websocket_emtry);
  384. return 1;
  385. #endif
  386. }