/* @module socket @summary 网络接口 @version 1.0 @date 2022.11.13 @demo socket @tag LUAT_USE_NETWORK @usage -- 本库用于网络通信, 支持TCP, UDP, 也支持TLS加密传输 -- 支持加密传输版本有 TLS 1.0/1.1/1.2/1.3, DTLS 1.0/1.2, 当前不支持TLS 1.3 -- 不支持 SSL 3.0, 该协议已经被废弃, 也不安全 -- 支持的加密算法有 RSA, ECC, AES, 3DES, SHA1, SHA256, MD5 等等 -- 完整的加密套件列表, 可通过 crypto.cipher_suites() 获取 -- 本库的函数, 除非特别说明, 都是立即返回的非阻塞函数 -- 这意味着, 函数调用成功, 并不代表网络操作成功, 只代表网络操作已经开始 */ #include "luat_base.h" #include "luat_mem.h" // #ifdef LUAT_USE_NETWORK #include "luat_network_adapter.h" #include "luat_rtos.h" #include "luat_zbuff.h" #define LUAT_LOG_TAG "socket" #include "luat_log.h" //static const char NW_TYPE[] = "NWA*"; #define LUAT_NW_CTRL_TYPE "NWCTRL*" typedef struct { network_ctrl_t *netc; int cb_ref; //回调函数 char *task_name; uint8_t adapter_index; }luat_socket_ctrl_t; #define L_CTRL_CHECK do {if (!l_ctrl || !l_ctrl->netc){return 0;}}while(0) network_adapter_info* network_adapter_fetch(int id, void** userdata); /* 获取本地ip @api socket.localIP(adapter) @int 适配器序号, 默认是平台自带的能上外网的适配器,通过socket.dft()可以获取和修改 @return string 通常是内网ip, 也可能是外网ip, 取决于运营商的分配 @return string 网络掩码 @return string 网关IP @usage sys.taskInit(function() while 1 do sys.wait(3000) log.info("socket", "ip", socket.localIP()) -- 输出示例 -- 62.39.244.10 255.255.255.255 0.0.0.0 end end) */ static int l_socket_local_ip(lua_State *L) { luat_ip_addr_t local_ip, net_mask, gate_way, ipv6; int adapter_index = luaL_optinteger(L, 1, network_register_get_default()); if (adapter_index < 0 || adapter_index >= NW_ADAPTER_QTY) { return 0; } #ifdef LUAT_USE_LWIP network_set_ip_invaild(&ipv6); int ret = network_get_full_local_ip_info(NULL, adapter_index, &local_ip, &net_mask, &gate_way, &ipv6); #else void* userdata = NULL; network_adapter_info* info = network_adapter_fetch(adapter_index, &userdata); if (info == NULL) return 0; int ret = info->get_local_ip_info(&local_ip, &net_mask, &gate_way, userdata); #endif if (ret == 0) { #ifdef LUAT_USE_LWIP lua_pushfstring(L, "%s", ipaddr_ntoa(&local_ip)); lua_pushfstring(L, "%s", ipaddr_ntoa(&net_mask)); lua_pushfstring(L, "%s", ipaddr_ntoa(&gate_way)); #if LWIP_IPV6 if (IPADDR_TYPE_V6 == ipv6.type) { char *ipv6_string = ip6addr_ntoa(&ipv6.u_addr.ip6); lua_pushfstring(L, "%s", ipv6_string); } else #endif { lua_pushnil(L); } return 4; #else lua_pushfstring(L, "%d.%d.%d.%d", (local_ip.ipv4 >> 0) & 0xFF, (local_ip.ipv4 >> 8) & 0xFF, (local_ip.ipv4 >> 16) & 0xFF, (local_ip.ipv4 >> 24) & 0xFF); lua_pushfstring(L, "%d.%d.%d.%d", (net_mask.ipv4 >> 0) & 0xFF, (net_mask.ipv4 >> 8) & 0xFF, (net_mask.ipv4 >> 16) & 0xFF, (net_mask.ipv4 >> 24) & 0xFF); lua_pushfstring(L, "%d.%d.%d.%d", (gate_way.ipv4 >> 0) & 0xFF, (gate_way.ipv4 >> 8) & 0xFF, (gate_way.ipv4 >> 16) & 0xFF, (gate_way.ipv4 >> 24) & 0xFF); return 3; #endif } return 0; } static int32_t l_socket_callback(lua_State *L, void* ptr) { rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1); luat_socket_ctrl_t *l_ctrl =(luat_socket_ctrl_t *)msg->ptr; if (l_ctrl->netc) { if (l_ctrl->cb_ref) { lua_geti(L, LUA_REGISTRYINDEX, l_ctrl->cb_ref); if (lua_isfunction(L, -1)) { lua_pushlightuserdata(L, l_ctrl); lua_pushinteger(L, msg->arg1); lua_pushinteger(L, msg->arg2); lua_call(L, 3, 0); } } else if (l_ctrl->task_name) { lua_getglobal(L, "sys_send"); if (lua_isfunction(L, -1)) { lua_pushstring(L, l_ctrl->task_name); lua_pushinteger(L, msg->arg1); lua_pushinteger(L, msg->arg2); lua_call(L, 3, 0); } } else { lua_getglobal(L, "sys_pub"); if (lua_isfunction(L, -1)) { lua_pushstring(L, LUAT_NW_CTRL_TYPE); lua_pushinteger(L, l_ctrl->netc->adapter_index); lua_pushinteger(L, l_ctrl->netc->socket_id); lua_pushinteger(L, msg->arg1); lua_pushinteger(L, msg->arg2); lua_call(L, 5, 0); } } } lua_pushinteger(L, 0); return 1; } static int32_t luat_lib_socket_callback(void *data, void *param) { OS_EVENT *event = (OS_EVENT *)data; rtos_msg_t msg; msg.handler = l_socket_callback; msg.ptr = param; msg.arg1 = event->ID & 0x0fffffff; msg.arg2 = event->Param1; luat_msgbus_put(&msg, 0); return 0; } static luat_socket_ctrl_t * l_get_ctrl(lua_State *L, int index) { if (luaL_testudata(L, 1, LUAT_NW_CTRL_TYPE)) { return ((luat_socket_ctrl_t *)luaL_checkudata(L, 1, LUAT_NW_CTRL_TYPE)); } else { return ((luat_socket_ctrl_t *)lua_touserdata(L, 1)); } } // __gc static int l_socket_gc(lua_State *L) { luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); if (!l_ctrl){return 0;} if (l_ctrl->netc) { network_force_close_socket(l_ctrl->netc); network_release_ctrl(l_ctrl->netc); l_ctrl->netc = NULL; } if (l_ctrl->cb_ref) { luaL_unref(L, LUA_REGISTRYINDEX, l_ctrl->cb_ref); l_ctrl->cb_ref = 0; } if (l_ctrl->task_name) { luat_heap_free(l_ctrl->task_name); l_ctrl->task_name = 0; } return 0; } /* 在指定网卡上申请一个socket_ctrl @api socket.create(adapter, cb) @int 适配器序号,默认是平台自带的能上外网的适配器,通过socket.dft()可以获取和修改 @string/function string为消息通知的taskName,function则为回调函数 @return userdata 成功返回network_ctrl,失败返回nil @usage --以太网网卡上申请一个network_ctrl,通过socket_cb_fun回调相关消息 local netc = socket.create(socket.LWIP_GP, socket_cb_fun) --以太网网卡上申请一个network_ctrl,通过sendMsg方式通知taskName为"IOT_TASK"回调相关消息 local netc = socket.create(socket.LWIP_GP, "IOT_TASK") -- 在默认网络适配器上创建一个network_ctrl local netc = socket.create(nil, "MySocket") --[[ 当通过回调函数回调消息时,输入给function一共3个参数: param1为申请的network_ctrl param2为具体的消息,只能是socket.LINK, socket.ON_LINE, socket.TX_OK, socket.EVENT, socket.CLOSED等等 param3为消息对应的参数,目前只有0和-1,0表示成功或者可能有新数据(具体消息为socket.EVENT),-1表示失败或者有异常,需要断开重连 ]] --[[ Air8000内核固件运行起来之后,在脚本调用socket.dft(id)之前,默认适配器编号为socket.LWIP_GP,最后一个注册的适配器编号为socket.LWIP_AP; Air780xx内核固件运行起来之后,在脚本调用socket.dft(id)之前,默认适配器编号为socket.LWIP_GP,最后一个注册的适配器编号为socket.LWIP_GP; Air8101内核固件运行起来之后,在脚本调用socket.dft(id)之前,默认适配器编号为socket.LWIP_STA,最后一个注册的适配器编号为socket.LWIP_STA; ]] */ static int l_socket_create(lua_State *L) { int adapter_index = luaL_optinteger(L, 1, network_register_get_default()); if (adapter_index < 0 || adapter_index >= NW_ADAPTER_QTY) { lua_pushnil(L); return 1; } luat_socket_ctrl_t *l_ctrl = (luat_socket_ctrl_t *)lua_newuserdata(L, sizeof(luat_socket_ctrl_t)); if (!l_ctrl) { lua_pushnil(L); return 1; } l_ctrl->adapter_index = adapter_index; l_ctrl->netc = network_alloc_ctrl(adapter_index); if (!l_ctrl->netc) { LLOGD("create fail"); lua_pushnil(L); return 1; } network_init_ctrl(l_ctrl->netc, NULL, luat_lib_socket_callback, l_ctrl); if (lua_isfunction(L, 2)) { lua_pushvalue(L, 2); l_ctrl->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); l_ctrl->task_name = NULL; } else if (lua_isstring(L, 2)) { l_ctrl->cb_ref = 0; size_t len; const char *buf; buf = lua_tolstring(L, 2, &len);//取出字符串数据 l_ctrl->task_name = luat_heap_malloc(len + 1); memset(l_ctrl->task_name, 0, len + 1); memcpy(l_ctrl->task_name, buf, len); } luaL_setmetatable(L, LUAT_NW_CTRL_TYPE); return 1; } /* 配置是否打开debug信息 @api socket.debug(ctrl, onoff) @user_data socket.create得到的ctrl @boolean true 打开debug开关 @return nil 无返回值 @usage -- 打开调试信息,默认是关闭状态 socket.debug(ctrl, true) */ static int l_socket_set_debug(lua_State *L) { luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; if (lua_isboolean(L, 2)) { l_ctrl->netc->is_debug = lua_toboolean(L, 2); } return 0; } /* 配置network一些信息, @api socket.config(ctrl, local_port, is_udp, is_tls, keep_idle, keep_interval, keep_cnt, server_cert, client_cert, client_key, client_password) @user_data socket.create得到的ctrl @int 本地端口号,小端格式,如果不写,则自动分配一个,如果用户填了端口号则需要小于60000, 默认不写 @boolean 是否是UDP,默认false @boolean 是否是加密传输,默认false @int tcp keep live模式下的idle时间(秒),如果留空则表示不启用,如果是不支持标准posix接口的网卡(比如W5500),则为心跳间隔 @int tcp keep live模式下的探测间隔时间(秒) @int tcp keep live模式下的探测次数 @string TCP模式下的服务器ca证书数据,UDP模式下的PSK,不需要加密传输写nil,后续参数也全部nil @string TCP模式下的客户端证书数据,UDP模式下的PSK-ID,TCP模式下如果不需要验证客户端证书时,忽略,一般不需要验证客户端证书 @string TCP模式下的客户端私钥加密数据 @string TCP模式下的客户端私钥口令数据 @return boolean 成功返回true,失败返回false @usage --最普通的TCP传输 socket.config(ctrl) --最普通的加密TCP传输,证书都不用验证的那种 socket.config(ctrl, nil, nil ,true) */ static int l_socket_config(lua_State *L) { luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; uint8_t is_udp = 0; uint8_t is_tls = 0; int param_pos = 1; uint32_t keep_idle, keep_interval, keep_cnt; const char *server_cert = NULL; const char *client_cert = NULL; const char *client_key = NULL; const char *client_password = NULL; size_t server_cert_len, client_cert_len, client_key_len, client_password_len; uint16_t local_port = luaL_optinteger(L, ++param_pos, 0); if (lua_isboolean(L, ++param_pos)) { is_udp = lua_toboolean(L, param_pos); } if (lua_isboolean(L, ++param_pos)) { is_tls = lua_toboolean(L, param_pos); } #ifndef LUAT_USE_TLS if (is_tls){ LLOGE("NOT SUPPORT TLS"); lua_pushboolean(L, 0); return 1; } #endif keep_idle = luaL_optinteger(L, ++param_pos, 0); keep_interval = luaL_optinteger(L, ++param_pos, 0); keep_cnt = luaL_optinteger(L, ++param_pos, 0); if (lua_isstring(L, ++param_pos)) { server_cert_len = 0; server_cert = luaL_checklstring(L, param_pos, &server_cert_len); } if (lua_isstring(L, ++param_pos)) { client_cert_len = 0; client_cert = luaL_checklstring(L, param_pos, &client_cert_len); } if (lua_isstring(L, ++param_pos)) { client_key_len = 0; client_key = luaL_checklstring(L, param_pos, &client_key_len); } if (lua_isstring(L, ++param_pos)) { client_password_len = 0; client_password = luaL_checklstring(L, param_pos, &client_password_len); } network_set_base_mode(l_ctrl->netc, !is_udp, 10000, keep_idle, keep_idle, keep_interval, keep_cnt); network_set_local_port(l_ctrl->netc, local_port); if (is_tls) { network_init_tls(l_ctrl->netc, (server_cert || client_cert)?2:0); if (is_udp) { network_set_psk_info(l_ctrl->netc, (const unsigned char *)server_cert, server_cert_len, (const unsigned char *)client_key, client_key_len); } else { if (server_cert) { network_set_server_cert(l_ctrl->netc, (const unsigned char *)server_cert, server_cert_len + 1); } if (client_cert) { network_set_client_cert(l_ctrl->netc, (const unsigned char *)client_cert, client_cert_len + 1, (const unsigned char *)client_key, client_key_len + 1, (const unsigned char *)client_password, client_password_len + 1); } } } else { network_deinit_tls(l_ctrl->netc); } lua_pushboolean(L, 1); return 1; } /* 等待网卡linkup @api socket.linkup(ctrl) @user_data socket.create得到的ctrl @return boolean true没有异常发生,false失败了,如果false则不需要看下一个返回值了 @return boolean true已经linkup,false没有linkup,之后需要接收socket.LINK消息 @usage -- 判断一下是否已经联网 local succ, result = socket.linkup(ctrl) */ static int l_socket_linkup(lua_State *L) { luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; int result = network_wait_link_up(l_ctrl->netc, 0); lua_pushboolean(L, (result < 0)?0:1); lua_pushboolean(L, result == 0); return 2; } /* 作为客户端连接服务器 @api socket.connect(ctrl, ip, remote_port, need_ipv6_dns) @user_data socket.create得到的ctrl @string or int ip或者域名,如果是IPV4,可以是大端格式的int值 @int 服务器端口号,小端格式 @boolean 域名解析是否要IPV6,true要,false不要,默认false不要,只有支持IPV6的协议栈才有效果 @return boolean true没有异常发生,false失败了,如果false则不需要看下一个返回值了,如果有异常,后续要close @return boolean true已经connect,false没有connect,之后需要接收socket.ON_LINE消息 @usage local succ, result = socket.connect(ctrl, "netlab.luatos.com", 40123) --[[ 常见的连接失败的code值, 会在日志中显示 -1 底层内存不足 -3 超时 -8 端口已经被占用 -11 链接未建立 -13 模块主动断开连接 -14 服务器主动断开连接 ]] */ static int l_socket_connect(lua_State *L) { #ifdef LUAT_USE_LWIP luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; luat_ip_addr_t ip_addr; const char *ip = NULL; size_t ip_len; network_set_ip_invaild(&ip_addr); if (lua_isinteger(L, 2)) { network_set_ip_ipv4(&ip_addr, lua_tointeger(L, 2)); ip = NULL; ip_len = 0; } else { ip_len = 0; ip = luaL_checklstring(L, 2, &ip_len); } uint16_t remote_port = luaL_checkinteger(L, 3); LLOGD("connect to %s,%d", ip, remote_port); if (!network_ip_is_vaild_ipv4(&ip_addr)) { if (LUA_TBOOLEAN == lua_type(L, 4)) { network_connect_ipv6_domain(l_ctrl->netc, lua_toboolean(L, 4)); } else { network_connect_ipv6_domain(l_ctrl->netc, 0); } } int result = network_connect(l_ctrl->netc, ip, ip_len, (!network_ip_is_vaild_ipv4(&ip_addr))?NULL:&ip_addr, remote_port, 0); lua_pushboolean(L, (result < 0)?0:1); lua_pushboolean(L, result == 0); return 2; #else luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; luat_ip_addr_t ip_addr; const char *ip = NULL; size_t ip_len; ip_addr.is_ipv6 = 0xff; if (lua_isinteger(L, 2)) { ip_addr.is_ipv6 = 0; ip_addr.ipv4 = lua_tointeger(L, 2); ip = NULL; ip_len = 0; } else { ip_len = 0; ip = luaL_checklstring(L, 2, &ip_len); } uint16_t remote_port = luaL_checkinteger(L, 3); LLOGD("connect to %s,%d", ip, remote_port); int result = network_connect(l_ctrl->netc, ip, ip_len, ip_addr.is_ipv6?NULL:&ip_addr, remote_port, 0); lua_pushboolean(L, (result < 0)?0:1); lua_pushboolean(L, result == 0); return 2; #endif } /* 作为客户端断开连接 @api socket.discon(ctrl) @user_data socket.create得到的ctrl @return boolean true没有异常发生,false失败了,如果false则不需要看下一个返回值了 @return boolean true已经断开,false没有断开,之后需要接收socket.CLOSED消息 @usage local succ, result = socket.discon(ctrl) */ static int l_socket_disconnect(lua_State *L) { luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; int result = network_close(l_ctrl->netc, 0); lua_pushboolean(L, (result < 0)?0:1); lua_pushboolean(L, result == 0); return 2; } /* 强制关闭socket @api socket.close(ctrl) @user_data socket.create得到的ctrl @return nil 无返回值 */ static int l_socket_close(lua_State *L) { luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; network_force_close_socket(l_ctrl->netc); return 0; } /* 发送数据给对端,UDP单次发送不要超过1460字节,否则很容易失败 @api socket.tx(ctrl, data, ip, port, flag) @user_data socket.create得到的ctrl @string or user_data zbuff 要发送的数据 @string or int 对端IP,如果是TCP应用则忽略,如果是UDP,如果留空则用connect时候的参数,如果是IPV4,可以是大端格式的int值 @int 对端端口号,小端格式,如果是TCP应用则忽略,如果是UDP,如果留空则用connect时候的参数 @int 发送参数,目前预留,不起作用 @return boolean true没有异常发生,false失败了,如果false则不需要看下一个返回值了,如果false,后续要close @return boolean true缓冲区满了,false没有满,如果true,则需要等待一段时间或者等到socket.TX_OK消息后再尝试发送,同时忽略下一个返回值 @return boolean true已经收到应答,false没有收到应答,之后需要接收socket.TX_OK消息, 也可以忽略继续发送,直到full==true @usage local succ, full, result = socket.tx(ctrl, "123456", "xxx.xxx.xxx.xxx", xxxx) */ static int l_socket_tx(lua_State *L) { #ifdef LUAT_USE_LWIP char ip_buf[68] = {0}; luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; luat_ip_addr_t ip_addr = {0}; luat_zbuff_t *buff = NULL; const char *ip = NULL; const char *data = NULL; size_t ip_len = 0, data_len = 0; network_set_ip_invaild(&ip_addr); if (lua_isstring(L, 2)) { data_len = 0; data = luaL_checklstring(L, 2, &data_len); } else { buff = ((luat_zbuff_t *)luaL_checkudata(L, 2, LUAT_ZBUFF_TYPE)); data = (const char*)buff->addr; data_len = buff->used; } if (lua_isinteger(L, 3)) { network_set_ip_ipv4(&ip_addr, lua_tointeger(L, 3)); } else if (lua_isstring(L, 3)) { ip_len = 0; ip = luaL_checklstring(L, 3, &ip_len); memcpy(ip_buf, ip, ip_len); ip_buf[ip_len] = 0; ipaddr_aton(ip_buf, &ip_addr); } uint32_t tx_len; int result = network_tx(l_ctrl->netc, (const uint8_t *)data, data_len, luaL_optinteger(L, 5, 0), network_ip_is_vaild(&ip_addr)?&ip_addr:NULL, luaL_optinteger(L, 4, 0), &tx_len, 0); #else luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; luat_ip_addr_t ip_addr = {0}; luat_zbuff_t *buff = NULL; const char *ip = NULL; const char *data = NULL; size_t ip_len = 0, data_len = 0; ip_addr.is_ipv6 = 0xff; if (lua_isstring(L, 2)) { data_len = 0; data = luaL_checklstring(L, 2, &data_len); } else { buff = ((luat_zbuff_t *)luaL_checkudata(L, 2, LUAT_ZBUFF_TYPE)); data = (const char *)buff->addr; data_len = buff->used; } if (lua_isinteger(L, 3)) { ip_addr.is_ipv6 = 0; ip_addr.ipv4 = lua_tointeger(L, 3); } else if (lua_isstring(L, 3)) { ip_len = 0; ip = luaL_checklstring(L, 3, &ip_len); if (network_string_is_ipv4(ip, ip_len)) { ip_addr.is_ipv6 = 0; ip_addr.ipv4 = network_string_to_ipv4(ip, ip_len); } else { char *name = luat_heap_malloc(ip_len + 1); memcpy(name, ip, ip_len); name[ip_len] = 0; network_string_to_ipv6(name, &ip_addr); free(name); } } uint32_t tx_len; int result = network_tx(l_ctrl->netc, (const uint8_t *)data, data_len, luaL_optinteger(L, 5, 0), (ip_addr.is_ipv6 != 0xff)?&ip_addr:NULL, luaL_optinteger(L, 4, 0), &tx_len, 0); #endif lua_pushboolean(L, (result < 0)?0:1); lua_pushboolean(L, tx_len != data_len); lua_pushboolean(L, result == 0); return 3; } /* 接收对端发出的数据,注意数据已经缓存在底层,使用本函数只是提取出来,UDP模式下一次只会取出一个数据包 @api socket.rx(ctrl, buff, flag, limit) @user_data socket.create得到的ctrl @user_data zbuff 存放接收的数据,如果缓冲区不够大会自动扩容 @int 接收参数,目前预留,不起作用 @int 接收数据长度限制,如果指定了,则只取前N个字节. 2024.1.5 新增 @return boolean true没有异常发生,false失败了,如果false则不需要看下一个返回值了,如果false,后续要close @return int 本次接收到数据长度 @return string 对端IP,只有UDP模式下才有意义,TCP模式返回nil,注意返回的格式,如果是IPV4,1byte 0x00 + 4byte地址 如果是IPV6,1byte 0x01 + 16byte地址 @return int 对端port,只有UDP模式下才有意义,TCP模式返回0 @usage -- 从socket中读取数据, ctrl是socket.create返回的, 请查阅demo/socket local buff = zbuff.create(2048) local succ, data_len, remote_ip, remote_port = socket.rx(ctrl, buff) -- 限制读取长度, 2024.1.5 新增 -- 注意 -- 如果是UDP数据, 如果limit小于UDP数据包长度, 只会取前limit个字节, 剩余数据会丢弃 -- 如果是TCP数据, 如果有剩余数据, 不会丢弃, 可继续读取. -- 有新的数据到来才会有新的EVENT数据, 未读取完成的数据不会触发新EVENT事件 local succ, data_len, remote_ip, remote_port = socket.rx(ctrl, buff, 1500) -- 读取缓冲区大小, 2024.1.5 新增, 注意,老版本固件不传buff参数会报错的 -- 对于TCP数据, 这里返回的是待读取的数据的总长度 -- 对于UDP数据, 这里返回的是单个UDP数据包的长度 local succ, data_len = socket.rx(ctrl) if succ then log.info("待收取数据长度", data_len) end */ static int l_socket_rx(lua_State *L) { luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; luat_zbuff_t *buff = NULL; luat_ip_addr_t ip_addr = {0}; uint8_t ip[17] = {0}; uint16_t port = 0; // uint8_t new_flag = 0; uint32_t rx_len = 0; uint32_t total_len = 0; int result = network_rx(l_ctrl->netc, NULL, 0, 0, NULL, NULL, &total_len); if (result < 0) { lua_pushboolean(L, 0); lua_pushinteger(L, 0); return 2; } if (!total_len) { lua_pushboolean(L, 1); lua_pushinteger(L, 0); return 2; } // 是否传入的zbuff呢? 没有的话就返回值长度 if (lua_isuserdata(L, 2)) { buff = ((luat_zbuff_t *)luaL_checkudata(L, 2, LUAT_ZBUFF_TYPE)); } else { // 仅返回长度 lua_pushboolean(L, 1); lua_pushinteger(L, total_len); return 2; } // 是否限制接收数据长度 if (lua_gettop(L) >= 4 && lua_isinteger(L, 4)) { uint32_t limit = lua_tointeger(L, 4); if (total_len > limit) { LLOGD("待数据数据长度 %d 限制读取长度 %d", total_len, limit); total_len = limit; } } if (1) { if ((buff->len - buff->used) < total_len) { result = __zbuff_resize(buff, total_len + buff->used); if (result < 0) { if (buff->len > buff->used) { LLOGW("zbuff自动扩容失败, 减少接收到数据长度 %d -> %d", total_len, buff->len - buff->used); total_len = buff->len - buff->used; } else { LLOGE("zbuff自动扩容失败, 且zbuff没有剩余空间,无法读取剩余数据"); lua_pushboolean(L, 0); lua_pushinteger(L, 0); return 2; } } } result = network_rx(l_ctrl->netc, buff->addr + buff->used, total_len, 0, &ip_addr, &port, &rx_len); if (result < 0) { lua_pushboolean(L, 0); lua_pushinteger(L, 0); return 2; } else if (!rx_len) { lua_pushboolean(L, 1); lua_pushinteger(L, 0); return 2; } else { buff->used += rx_len; lua_pushboolean(L, 1); lua_pushinteger(L, rx_len); if (l_ctrl->netc->is_tcp) { lua_pushnil(L); lua_pushnil(L); } else { #ifdef LUAT_USE_LWIP #if LWIP_IPV6 if (IPADDR_TYPE_V4 == ip_addr.type) { ip[0] = 0; memcpy(ip + 1, &ip_addr.u_addr.ip4.addr, 4); lua_pushlstring(L, (const char*)ip, 5); } else { ip[0] = 1; memcpy(ip + 1, ip_addr.u_addr.ip6.addr, 16); lua_pushlstring(L, (const char*)ip, 17); } #else ip[0] = 0; memcpy(ip + 1, &ip_addr.addr, 4); lua_pushlstring(L, (const char*)ip, 5); #endif #else if (!ip_addr.is_ipv6) { ip[0] = 0; memcpy(ip + 1, &ip_addr.ipv4, 4); lua_pushlstring(L, (const char*)ip, 5); } else { ip[0] = 1; memcpy(ip + 1, &ip_addr.ipv6_u8_addr, 16); lua_pushlstring(L, (const char*)ip, 17); } #endif lua_pushinteger(L, port); } } } return 4; } /* 读取数据(非zbuff版本,已废弃) @api socket.read(netc, len) @userdata socket.create得到的ctrl @int 限制读取数据长度,可选,不传就是读出全部 @return boolean 读取成功与否 @return string 读取的数据,仅当读取成功时有效 @return string 对方IP地址,仅当读取成功且UDP通信时有效 @return int 对方端口,仅当读取成功且UDP通信时有效 @usage -- 本函数于2024.4.8添加, 用于简易读取不大的数据 -- 请优先使用socket.rx函数, 本函数主要用于固件不含zbuff库时的变通调用 local ok, data = socket.read(netc, 1500) if ok and #data > 0 then log.info("读取到的数据", data) end */ static int l_socket_read(lua_State *L) { luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; luat_ip_addr_t ip_addr = {0}; uint8_t ip[17] = {0}; uint16_t port = 0; // uint8_t new_flag = 0; uint32_t rx_len = 0; uint32_t total_len = 0; int result = network_rx(l_ctrl->netc, NULL, 0, 0, NULL, NULL, &total_len); if (result < 0) { lua_pushboolean(L, 0); return 1; } if (!total_len) { lua_pushboolean(L, 1); lua_pushstring(L, ""); return 2; } // 是否限制接收数据长度 if (lua_gettop(L) >= 2 && lua_isinteger(L, 2)) { uint32_t limit = lua_tointeger(L, 2); if (limit > 0 && total_len > limit) { LLOGD("待数据数据长度 %d 限制读取长度 %d", total_len, limit); total_len = limit; } } luaL_Buffer bf = {0}; char* buff = luaL_buffinitsize(L, &bf, total_len); result = network_rx(l_ctrl->netc, (uint8_t*)buff, total_len, 0, &ip_addr, &port, &rx_len); if (result < 0) { lua_pushboolean(L, 0); return 1; } else if (!rx_len) { lua_pushboolean(L, 1); lua_pushstring(L, ""); return 2; } else { lua_pushboolean(L, 1); luaL_pushresultsize(&bf, rx_len); if (l_ctrl->netc->is_tcp) { return 2; } else { #ifdef LUAT_USE_LWIP #if LWIP_IPV6 if (IPADDR_TYPE_V4 == ip_addr.type) { ip[0] = 0; memcpy(ip + 1, &ip_addr.u_addr.ip4.addr, 4); lua_pushlstring(L, (const char*)ip, 5); } else { ip[0] = 1; memcpy(ip + 1, ip_addr.u_addr.ip6.addr, 16); lua_pushlstring(L, (const char*)ip, 17); } #else ip[0] = 0; memcpy(ip + 1, &ip_addr.addr, 4); lua_pushlstring(L, (const char*)ip, 5); #endif #else if (!ip_addr.is_ipv6) { ip[0] = 0; memcpy(ip + 1, &ip_addr.ipv4, 4); lua_pushlstring(L, (const char*)ip, 5); } else { ip[0] = 1; memcpy(ip + 1, &ip_addr.ipv6_u8_addr, 16); lua_pushlstring(L, (const char*)ip, 17); } #endif lua_pushinteger(L, port); return 4; } } } /* 等待新的socket消息,在连接成功和发送数据成功后,使用一次将network状态转换到接收新数据 @api socket.wait(ctrl) @user_data socket.create得到的ctrl @return boolean true没有异常发生,false失败了,如果false则不需要看下一个返回值了,如果false,后续要close @return boolean true有新的数据需要接收,false没有数据,之后需要接收socket.EVENT消息 @usage local succ, result = socket.wait(ctrl) */ static int l_socket_wait(lua_State *L) { luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; int result = network_wait_event(l_ctrl->netc, NULL, 0, NULL); lua_pushboolean(L, (result < 0)?0:1); lua_pushboolean(L, result == 0); return 2; } /* 作为服务端开始监听 @api socket.listen(ctrl) @user_data socket.create得到的ctrl @return boolean true没有异常发生,false失败了,如果false则不需要看下一个返回值了,如果false,后续要close @return boolean true已经connect,false没有connect,之后需要接收socket.ON_LINE消息 @usage local succ, result = socket.listen(ctrl) */ static int l_socket_listen(lua_State *L) { luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; int result = network_listen(l_ctrl->netc, 0); lua_pushboolean(L, (result < 0)?0:1); lua_pushboolean(L, result == 0); if (result < 0) { LLOGE("listen fail %d", result); } return 2; } /* 作为服务端接收到一个新的客户端 @api socket.accept(ctrl, args) @user_data socket.create得到的ctrl,这里是服务器端 @string or function or nil string为消息通知的taskName,function则为回调函数,和socket.create参数一致 @return boolean true没有异常发生,false失败了,如果false则不需要看下一个返回值了,如果false,后续要close @return user_data or nil 如果支持1对多,则会返回新的ctrl,自动create,如果不支持则返回nil @usage local succ, new_netc = socket.accept(ctrl, cb) -- 注意, 当目标适配器不支持1对多时,new_netc会是nil, 第二个参数无效 -- 当第二个参数为nil时, 固定为一对一模式, new_netc也会是nil */ static int l_socket_accept(lua_State *L) { luat_socket_ctrl_t *old_ctrl = l_get_ctrl(L, 1); if (!old_ctrl) return 0; if ((lua_isfunction(L, 2) || lua_isstring(L, 2)) && network_accept_enable(old_ctrl->netc)) { luat_socket_ctrl_t *new_ctrl = (luat_socket_ctrl_t *)lua_newuserdata(L, sizeof(luat_socket_ctrl_t)); if (!new_ctrl) { lua_pushboolean(L, 0); lua_pushnil(L); return 2; } new_ctrl->adapter_index = old_ctrl->adapter_index; new_ctrl->netc = network_alloc_ctrl(old_ctrl->adapter_index); if (!new_ctrl->netc) { LLOGD("create fail"); lua_pushboolean(L, 0); lua_pushnil(L); return 2; } network_init_ctrl(new_ctrl->netc, NULL, luat_lib_socket_callback, new_ctrl); if (lua_isfunction(L, 2)) { lua_pushvalue(L, 2); new_ctrl->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); new_ctrl->task_name = NULL; } else if (lua_isstring(L, 2)) { new_ctrl->cb_ref = 0; size_t len; const char *buf; buf = lua_tolstring(L, 2, &len);//取出字符串数据 new_ctrl->task_name = luat_heap_malloc(len + 1); memset(new_ctrl->task_name, 0, len + 1); memcpy(new_ctrl->task_name, buf, len); } else { LLOGE("accept模式必须传入回调函数或taskName"); network_release_ctrl(new_ctrl->netc); new_ctrl->netc = NULL; lua_pushboolean(L, 0); return 1; } if (network_socket_accept(old_ctrl->netc, new_ctrl->netc)) { network_release_ctrl(new_ctrl->netc); new_ctrl->netc = NULL; lua_pushboolean(L, 0); lua_pushnil(L); return 2; } // 恢复到可监听状态 LLOGD("accept success %p", new_ctrl->netc); luaL_setmetatable(L, LUAT_NW_CTRL_TYPE); lua_pushboolean(L, 1); lua_pushvalue(L, -2); return 2; } else { lua_pushboolean(L, !network_socket_accept(old_ctrl->netc, NULL)); lua_pushnil(L); return 2; } } /* 获取socket当前状态 @api socket.state(ctrl) @user_data socket.create得到的ctrl @return int or nil,输入参数正确的情况下,返回状态的数值,否则返回nil @return string or nil,输入参数正确的情况下,返回状态的中文描述,否则返回nil @usage local state, str = socket.state(ctrl) log.info("state", state, str) state 0 "硬件离线", 1 "离线", 2 "等待DNS", 3 "正在连接", 4 "正在TLS握手", 5 "在线", 6 "在监听", 7 "正在离线", 8 "未知" */ static int l_socket_state(lua_State *L) { luat_socket_ctrl_t *l_ctrl = l_get_ctrl(L, 1); L_CTRL_CHECK; lua_pushinteger(L, l_ctrl->netc->state); lua_pushstring(L, network_ctrl_state_string(l_ctrl->netc->state)); return 2; } /* 主动释放掉network_ctrl @api socket.release(ctrl) @user_data socket.create得到的ctrl @usage -- 释放后就不能再使用了 socket.release(ctrl) */ static int l_socket_release(lua_State *L) { return l_socket_gc(L); } /* 设置DNS服务器 @api socket.setDNS(adapter_index, dns_index, ip) @int 适配器序号,请参考socket库的常量表 @int dns服务器序号,从1开始 @string or int dns,如果是IPV4,可以是大端格式的int值 @return boolean 成功返回true,失败返回false @usage -- 设置默认网络适配器的DNS配置 socket.setDNS(nil, 1, "114.114.114.114") -- 设置制定网络适配器的DNS配置 socket.setDNS(socket.ETH0, 1, "114.114.114.114") */ static int l_socket_set_dns(lua_State *L) { #ifdef LUAT_USE_LWIP char ip_buf[68]; int adapter_index = luaL_optinteger(L, 1, network_register_get_default()); if (adapter_index < 0 || adapter_index >= NW_ADAPTER_QTY) { lua_pushboolean(L, 0); return 1; } int dns_index = luaL_optinteger(L, 2, 1); luat_ip_addr_t ip_addr; const char *ip; size_t ip_len; network_set_ip_invaild(&ip_addr); if (lua_isinteger(L, 3)) { network_set_ip_ipv4(&ip_addr, lua_tointeger(L, 3)); ip = NULL; ip_len = 0; } else { ip_len = 0; ip = luaL_checklstring(L, 3, &ip_len); memcpy(ip_buf, ip, ip_len); ip_buf[ip_len] = 0; ipaddr_aton(ip_buf, &ip_addr); } #else int adapter_index = luaL_optinteger(L, 1, network_register_get_default()); if (adapter_index < 0 || adapter_index >= NW_ADAPTER_QTY) { lua_pushboolean(L, 0); return 1; } int dns_index = luaL_optinteger(L, 2, 1); luat_ip_addr_t ip_addr; const char *ip; size_t ip_len; ip_addr.is_ipv6 = 0xff; if (lua_isinteger(L, 3)) { ip_addr.is_ipv6 = 0; ip_addr.ipv4 = lua_tointeger(L, 3); ip = NULL; ip_len = 0; } else { ip_len = 0; ip = luaL_checklstring(L, 3, &ip_len); ip_addr.is_ipv6 = !network_string_is_ipv4(ip, ip_len); if (ip_addr.is_ipv6) { char *temp = luat_heap_malloc(ip_len + 1); memcpy(temp, ip, ip_len); temp[ip_len] = 0; network_string_to_ipv6(temp, &ip_addr); luat_heap_free(temp); } else { ip_addr.ipv4 = network_string_to_ipv4(ip, ip_len); } } #endif void* userdata = NULL; network_adapter_info* info = network_adapter_fetch(adapter_index, &userdata); if (info == NULL) { LLOGW("adapter_index is invaild %d", adapter_index); lua_pushboolean(L, 0); } else { network_set_dns_server(adapter_index, dns_index - 1, &ip_addr); lua_pushboolean(L, 1); } return 1; } /* 设置SSL的log登记 @api socket.sslLog(log_level) @int mbedtls log等级 @return nil 无返回值 @usage --[[ SSL/TLS log级别说明 0不打印 1只打印错误和警 2大部分info 3及3以上详细的debug 过多的信息可能会造成内存碎片化 ]] -- 打印大部分info日志 socket.sslLog(2) */ static int l_socket_set_ssl_log(lua_State *L) { #if defined(MBEDTLS_DEBUG_C) mbedtls_debug_set_threshold(luaL_optinteger(L, 1, 1)); #endif return 0; } #ifdef LUAT_USE_SNTP #include "luat_sntp.h" #endif /* 查看网卡适配器的联网状态 @api socket.adapter(index) @int 需要查看的适配器序号,可以留空会查看全部网卡,直到遇到IP READY的,如果指定网卡,只能是socket.ETH0(外置以太网),socket.LWIP_ETH(内置以太网),socket.LWIP_STA(内置WIFI的STA),socket.LWIP_AP(内置WIFI的AP),socket.LWIP_GP(内置蜂窝网络的GPRS),socket.USB(外置USB网卡) @return boolean 被查看的适配器是否IP READY,true表示已经准备好可以联网了,false暂时不可以联网 @return int 最后一个被查看的适配器序号 @usage -- 查看全部网卡,直到找到一个是IP READY的 local isReady,index = socket.adapter() --如果isReady为true,则index为IP READY的网卡适配器序号 --查看外置以太网(比如W5500)是否IP READY local isReady,default = socket.adapter(socket.ETH0) */ static int l_socket_adapter(lua_State *L) { int adapter_index = luaL_optinteger(L, 1, -1); if (adapter_index > NW_ADAPTER_INDEX_LWIP_NONE && adapter_index < NW_ADAPTER_QTY) { lua_pushboolean(L, network_check_ready(NULL, adapter_index)); lua_pushinteger(L, adapter_index); } else { for(int i = NW_ADAPTER_INDEX_LWIP_GPRS; i < NW_ADAPTER_QTY; i++) { if (network_check_ready(NULL, i)) { lua_pushboolean(L, 1); lua_pushinteger(L, i); return 2; } } lua_pushboolean(L, 0); lua_pushinteger(L, NW_ADAPTER_QTY - 1); } return 2; } /* 获取对端ip @api socket.remoteIP(ctrl) @user_data socket.create得到的ctrl @return string IP1,如果为nil,则表示没有获取到IP地址 @return string IP2,如果为nil,则表示没有IP2 @return string IP3,如果为nil,则表示没有IP3 @return string IP4,如果为nil,则表示没有IP4 @usage -- 注意: ,必须在接收到socket.ON_LINE消息之后才可能获取到,最多返回4个IP。 -- socket.connect里如果remote_port设置成0,则当DNS完成时就返回socket.ON_LINE消息 local ip1,ip2,ip3,ip4 = socket.remoteIP(ctrl) */ static int l_socket_remote_ip(lua_State *L) { luat_socket_ctrl_t *ctrl = l_get_ctrl(L, 1); uint8_t i; uint8_t total; if (!ctrl) { goto NO_REMOTE_IP; } if (!ctrl->netc->dns_ip_nums || !ctrl->netc->dns_ip) { goto NO_REMOTE_IP; } total = (ctrl->netc->dns_ip_nums > 4)?4:ctrl->netc->dns_ip_nums; for(i = 0; i < total; i++) { #ifdef LUAT_USE_LWIP lua_pushfstring(L, "%s", ipaddr_ntoa(&ctrl->netc->dns_ip[i].ip)); #else PV_Union uPV; uPV.u32 = &ctrl->netc->dns_ip[i].ip.ipv4; lua_pushfstring(L, "%d.%d.%d.%d", uPV.u8[0], uPV.u8[1], uPV.u8[2], uPV.u8[3]); #endif } if (total < 4) { for(i = total; i < 4; i++) { lua_pushnil(L); } } return 4; NO_REMOTE_IP: lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); return 4; } /* 设置默认网络适配器编号 @api socket.dft(id) @int 默认适配器编号,若不传,则打包获取 @return int 默认适配器编号 @return int 最后一个注册的适配器编号(2025.7.25新增) @usage -- 本函数于 2025.1.6 新增 -- 获取当前默认适配器编号 local id = socket.dft() -- 设置默认适配器编号 socket.dft(socket.LWIP_ETH) -- 获取当前默认适配器编号, 及最后一个注册的适配器编号 local id, last_id = socket.dft() log.info("当前默认适配器编号", id, "最后一个注册的适配器编号", last_id) -- 1. 当前的默认网卡, 可以获取, 可以设置, 就是socket.dft(id)的第一个参数, 也是第一个返回值 -- 2. 最后注册的网卡, 可以获取, 不支持设置, 就是socket.dft()的第二个返回值 */ static int l_socket_default(lua_State *L) { uint8_t id = 0; if (lua_type(L, 1) == LUA_TNUMBER) { id = (uint8_t)luaL_checkinteger(L, 1); if (id < NW_ADAPTER_QTY) { network_register_set_default(id); } } lua_pushinteger(L, network_register_get_default()); lua_pushinteger(L, network_get_last_register_adapter_real()); return 2; } /* 断开指定网卡的所有数据链接(实验性支持) @api socket.close_all(id) @int 适配器编号 @return bool 执行结果 @usage -- 本函数于 2025.9.24 新增 -- 断开WIFI网卡上的所有数据链接 socket.close_all(socket.LWIP_STA) */ static int l_socket_close_all(lua_State *L) { uint8_t id = 0; if (lua_type(L, 1) != LUA_TNUMBER) { lua_pushboolean(L, 0); return 1; } id = (uint8_t)luaL_checkinteger(L, 1); if (id >= NW_ADAPTER_QTY) { lua_pushboolean(L, 0); return 1; } network_close_all_ctrl_by_adapter(id, 0); lua_pushboolean(L, 1); return 1; } #include "rotable2.h" static const rotable_Reg_t reg_socket_adapter[] = { {"create", ROREG_FUNC(l_socket_create)}, {"debug", ROREG_FUNC(l_socket_set_debug)}, {"config", ROREG_FUNC(l_socket_config)}, {"linkup", ROREG_FUNC(l_socket_linkup)}, {"connect", ROREG_FUNC(l_socket_connect)}, {"listen", ROREG_FUNC(l_socket_listen)}, {"accept", ROREG_FUNC(l_socket_accept)}, {"discon", ROREG_FUNC(l_socket_disconnect)}, {"close", ROREG_FUNC(l_socket_close)}, {"tx", ROREG_FUNC(l_socket_tx)}, {"rx", ROREG_FUNC(l_socket_rx)}, {"read", ROREG_FUNC(l_socket_read)}, {"wait", ROREG_FUNC(l_socket_wait)}, {"state", ROREG_FUNC(l_socket_state)}, {"release", ROREG_FUNC(l_socket_release)}, { "setDNS", ROREG_FUNC(l_socket_set_dns)}, { "sslLog", ROREG_FUNC(l_socket_set_ssl_log)}, {"localIP", ROREG_FUNC(l_socket_local_ip)}, {"remoteIP", ROREG_FUNC(l_socket_remote_ip)}, {"adapter", ROREG_FUNC(l_socket_adapter)}, {"dft", ROREG_FUNC(l_socket_default)}, {"close_all", ROREG_FUNC(l_socket_close_all)}, #ifdef LUAT_USE_SNTP {"sntp", ROREG_FUNC(l_sntp_get)}, {"ntptm", ROREG_FUNC(l_sntp_tm)}, {"sntp_port", ROREG_FUNC(l_sntp_port)}, #endif //@const ETH0 number 带硬件协议栈的ETH0,值为5 { "ETH0", ROREG_INT(NW_ADAPTER_INDEX_ETH0)}, //@const LWIP_ETH number 使用LWIP协议栈的以太网卡,值为4 { "LWIP_ETH", ROREG_INT(NW_ADAPTER_INDEX_LWIP_ETH)}, //@const LWIP_STA number 使用LWIP协议栈的WIFI STA,值为2 { "LWIP_STA", ROREG_INT(NW_ADAPTER_INDEX_LWIP_WIFI_STA)}, //@const LWIP_AP number 使用LWIP协议栈的WIFI AP,值为3 { "LWIP_AP", ROREG_INT(NW_ADAPTER_INDEX_LWIP_WIFI_AP)}, //@const LWIP_GP number 使用LWIP协议栈的移动蜂窝模块,值为1 { "LWIP_GP", ROREG_INT(NW_ADAPTER_INDEX_LWIP_GPRS)}, //@const USB number 使用LWIP协议栈的USB网卡,值为6 { "USB", ROREG_INT(NW_ADAPTER_INDEX_USB)}, //@const LINK number LINK事件 { "LINK", ROREG_INT(EV_NW_RESULT_LINK & 0x0fffffff)}, //@const ON_LINE number ON_LINE事件 { "ON_LINE", ROREG_INT(EV_NW_RESULT_CONNECT & 0x0fffffff)}, //@const EVENT number EVENT事件 { "EVENT", ROREG_INT(EV_NW_RESULT_EVENT & 0x0fffffff)}, //@const TX_OK number TX_OK事件 { "TX_OK", ROREG_INT(EV_NW_RESULT_TX & 0x0fffffff)}, //@const CLOSED number CLOSED事件 { "CLOSED", ROREG_INT(EV_NW_RESULT_CLOSE & 0x0fffffff)}, //@const LWIP_SPI number 使用LWIP协议栈的SPI网络 { "LWIP_SPI", ROREG_INT(NW_ADAPTER_INDEX_LWIP_SPI)}, //@const LWIP_USER0 number 使用LWIP协议栈的自定义网卡0, 2025.1.12新增 { "LWIP_USER0", ROREG_INT(NW_ADAPTER_INDEX_LWIP_USER0)}, //@const LWIP_USER1 number 使用LWIP协议栈的自定义网卡1, 2025.1.12新增 { "LWIP_USER1", ROREG_INT(NW_ADAPTER_INDEX_LWIP_USER1)}, //@const LWIP_USER2 number 使用LWIP协议栈的自定义网卡2, 2025.1.12新增 { "LWIP_USER2", ROREG_INT(NW_ADAPTER_INDEX_LWIP_USER2)}, //@const LWIP_USER3 number 使用LWIP协议栈的自定义网卡3, 2025.1.12新增 { "LWIP_USER3", ROREG_INT(NW_ADAPTER_INDEX_LWIP_USER3)}, //@const LWIP_USER4 number 使用LWIP协议栈的自定义网卡4, 2025.1.12新增 { "LWIP_USER4", ROREG_INT(NW_ADAPTER_INDEX_LWIP_USER4)}, //@const LWIP_USER5 number 使用LWIP协议栈的自定义网卡5, 2025.1.12新增 { "LWIP_USER5", ROREG_INT(NW_ADAPTER_INDEX_LWIP_USER5)}, //@const LWIP_USER6 number 使用LWIP协议栈的自定义网卡6, 2025.1.12新增 { "LWIP_USER6", ROREG_INT(NW_ADAPTER_INDEX_LWIP_USER6)}, //@const LWIP_USER7 number 使用LWIP协议栈的自定义网卡7, 2025.1.12新增 { "LWIP_USER7", ROREG_INT(NW_ADAPTER_INDEX_LWIP_USER7)}, //@const LWIP_GP_GW number 4G代理网关 { "LWIP_GP_GW", ROREG_INT(NW_ADAPTER_INDEX_LWIP_GP_GW)}, { NULL, ROREG_INT(0)} }; LUAMOD_API int luaopen_socket_adapter( lua_State *L ) { luat_newlib2(L, reg_socket_adapter); luaL_newmetatable(L, LUAT_NW_CTRL_TYPE); /* create metatable for file handles */ lua_pushcfunction(L, l_socket_gc); lua_setfield(L, -2, "__gc"); lua_pop(L, 1); /* pop new metatable */ return 1; } // #endif