diff --git a/FA/NAPI/tcpservermodule/ohos.build b/FA/NAPI/tcpservermodule/ohos.build new file mode 100644 index 0000000000000000000000000000000000000000..42dbb3bf91d0059a364f8f71be2119ddf1dcfd14 --- /dev/null +++ b/FA/NAPI/tcpservermodule/ohos.build @@ -0,0 +1,13 @@ +{ + "subsystem": "tcpservermodule", + "parts": { + "tcpserver" : { + "variants": [ + "phone" + ], + "module_list": [ + "//tcpservermodule/tcpserver/tcpserverapi:tcpserverapi" + ] + } + } +} diff --git a/FA/NAPI/tcpservermodule/tcpserver/tcpserverapi/BUILD.gn b/FA/NAPI/tcpservermodule/tcpserver/tcpserverapi/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..7c5e5f1fb27fa3427fec239dd78ffd167efa503f --- /dev/null +++ b/FA/NAPI/tcpservermodule/tcpserver/tcpserverapi/BUILD.gn @@ -0,0 +1,27 @@ +import("//build/ohos.gni") +ohos_shared_library("tcpserverapi") { + include_dirs = [ + "//foundation/ace/napi/interfaces/kits", + "//foundation/aafwk/standard/services/common/include", + "//foundation/aafwk/standard/interfaces/kits/napi/aafwk/inner/napi_common", + ] + + sources = [ + "tcpserverapi.cpp", + ] + + deps = [ + "//foundation/aafwk/standard/interfaces/innerkits/base:base", + "//foundation/aafwk/standard/interfaces/kits/napi/aafwk/inner/napi_common:napi_common", + "//foundation/ace/napi:ace_napi", + "//utils/native/base:utils", + ] + + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + ] + + relative_install_dir = "module" + subsystem_name = "tcpservermodule" + part_name = "tcpserver" +} diff --git a/FA/NAPI/tcpservermodule/tcpserver/tcpserverapi/tcpserverapi.cpp b/FA/NAPI/tcpservermodule/tcpserver/tcpserverapi/tcpserverapi.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cbe50911b610d4958321fbdd28b89a24a9e78c07 --- /dev/null +++ b/FA/NAPI/tcpservermodule/tcpserver/tcpserverapi/tcpserverapi.cpp @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hilog_wrapper.h" +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "securec.h" + +using namespace std; + +#define MAX_CONNECT 10 // how many pending connections queue will hold +#define SOCKET_BUF_SIZE 128 +#define IP_BUF_MAX 64 +#define EXIT_ERR (-1) +#define SOCKET_OK (0) +#define SOCKET_ERR (-1) +#define DEFAULT_PORT 5000 + +#define EXEC_SUCC 0 +#define EXEC_FAILED -1 + +static int g_threadFlag = 1; +char recvClientMsg[SOCKET_BUF_SIZE] = {}; + +typedef struct _stContrlParam { + unsigned int service_id; // 0:not connected + unsigned int enable; // socket connect status + unsigned int countTick; // Number of times that no heartbeat response is received +} stContrlParam; + +enum SOCKET_CONCTRL { + SOCKET_DISABLE = 0, + SOCKET_ENABLE +}; + +stContrlParam g_contrl[MAX_CONNECT] = {}; +int g_connAmount = 0; // current connection count + +enum ResponseState { + /** + * 控制命令响应状态值. + */ + SUCCESS = 200, + ERROR = 500 +}; + +// 响应值 +class Response { +public: + /** + * 响应状态 + */ + ResponseState state; + + /** + * 设备状态 + */ + std::string result; + + /** + * 响应时间 + */ + std::string time; +}; + +struct CommandStrData { + napi_async_work asyncWork = nullptr; + napi_deferred deferred = nullptr; + napi_ref callback = nullptr; + Response result; + int clientNum = 0; + char robotDogCommand[32] = {}; + char recvMessage[SOCKET_BUF_SIZE] = {}; +}; + +// ======================================tcpServerApi=============================================== + +/** + * @brief 向指定的设备发送控制信息 + * + * @param client accept设备的文件符 + * @param valueStr 有待传送的字符串 + * @return ssize_t 返回处理结果 + */ +static ssize_t send_msg_to_robotDog(string valueStr) +{ + for (int i = 0; i < g_connAmount; i++) { + char sendBuf[SOCKET_BUF_SIZE] = ""; + bzero(sendBuf, sizeof(sendBuf)); + valueStr.copy(sendBuf, valueStr.length(), 0); + + ssize_t sendNum = write(g_contrl[i].service_id, sendBuf, strlen(sendBuf)); + return sendNum; + } + return -1; +} + +// 业务逻辑处理函数,由worker线程池调度执行。 +static void sendRobotDogCommandExecuteCB(napi_env env, void *data) +{ + HILOG_INFO("=====%{public}s,called", __func__); + CommandStrData *commandStrData = (CommandStrData *)data; + + // 发送命令 + string valueStr = commandStrData->robotDogCommand; + + ssize_t sendNum = send_msg_to_robotDog(valueStr); + if(sendNum != -1) { + commandStrData->result.state = SUCCESS; + commandStrData->result.result = "SUCCESS"; + } else { + commandStrData->result.state = ERROR; + commandStrData->result.result = "NET ERROR"; + } +} + +static void sendRobotDogCommandPromiseCompleteCB(napi_env env, napi_status status, void *data) +{ + CommandStrData *commandStrData = (CommandStrData *)data; + + napi_value result = nullptr; + napi_value resState = nullptr; + napi_value resResult = nullptr; + napi_value resTime = nullptr; + + char tempChar[256] = {}; + napi_create_int32(env, (int32_t)commandStrData->result.state, &resState); + strcpy_s(tempChar, sizeof(tempChar), commandStrData->result.result.c_str()); + napi_create_string_utf8(env, tempChar, strlen(tempChar), &resResult); + + bzero(tempChar, strlen(tempChar)); + strcpy_s(tempChar, sizeof(tempChar), commandStrData->result.time.c_str()); + napi_create_string_utf8(env, tempChar, strlen(tempChar), &resTime); + + napi_create_object(env, &result); + napi_set_named_property(env, result, "state", resState); + napi_set_named_property(env, result, "result", resResult); + napi_set_named_property(env, result, "time", resTime); + + napi_resolve_deferred(env, commandStrData->deferred, result); + + // 删除napi_ref对象 + if(commandStrData->callback != nullptr) { + napi_delete_reference(env, commandStrData->callback); + } + + // 删除异步工作项 + napi_delete_async_work(env, commandStrData->asyncWork); + delete commandStrData; +} + +// 业务逻辑处理函数,由worker线程池调度执行。 +static void recvCompassMsgExecuteCB(napi_env env, void *data) +{ + CommandStrData *commandStrData = (CommandStrData *)data; + commandStrData->result.state = SUCCESS; + commandStrData->result.result = "SUCCESS"; + commandStrData->result.time = "1s"; + // 连接到服务器的设备数量传递到应用层,如果为0,说明客户端没有客户端连接 + commandStrData->clientNum = g_connAmount; + if(g_connAmount == 0) { + bzero(recvClientMsg, sizeof(recvClientMsg)); + } + // 接送到数据传递到应用层 + strcpy_s(commandStrData->recvMessage, sizeof(commandStrData->recvMessage), recvClientMsg); +} + +static void recvCompassMsgPromiseCompleteCB(napi_env env, napi_status status, void *data) +{ + CommandStrData *commandStrData = (CommandStrData *)data; + + napi_value result = nullptr; + napi_value resState = nullptr; + napi_value resResult = nullptr; + napi_value resTime = nullptr; + napi_value resMessage = nullptr; + napi_value resClientNumber = nullptr; + + char tempChar[256] = {}; + napi_create_int32(env, (int32_t)commandStrData->result.state, &resState); + + strcpy_s(tempChar, sizeof(tempChar), commandStrData->result.result.c_str()); + napi_create_string_utf8(env, tempChar, strlen(tempChar), &resResult); + + bzero(tempChar, strlen(tempChar)); + strcpy_s(tempChar, sizeof(tempChar), commandStrData->result.time.c_str()); + napi_create_string_utf8(env, tempChar, strlen(tempChar), &resTime); + + bzero(tempChar, strlen(tempChar)); + strcpy_s(tempChar, sizeof(tempChar), commandStrData->recvMessage); + napi_create_string_utf8(env, tempChar, strlen(tempChar), &resMessage); + + napi_create_int32(env, (int32_t)commandStrData->clientNum, &resClientNumber); + + napi_create_object(env, &result); + napi_set_named_property(env, result, "state", resState); + napi_set_named_property(env, result, "result", resResult); + napi_set_named_property(env, result, "time", resTime); + napi_set_named_property(env, result, "message", resMessage); + napi_set_named_property(env, result, "clientNumber", resClientNumber); + + napi_resolve_deferred(env, commandStrData->deferred, result); + + // 删除napi_ref对象 + if(commandStrData->callback != nullptr) { + napi_delete_reference(env, commandStrData->callback); + } + + // 删除异步工作项 + napi_delete_async_work(env, commandStrData->asyncWork); + delete commandStrData; +} + +static int SetConnectParam(int new_fd, int *maxsock) +{ + if(++g_connAmount == MAX_CONNECT) { + g_connAmount--; + HILOG_INFO("=====max connections arrived!\n"); + return -1; + } + for (int i = 0; i < MAX_CONNECT; i++) { + if(g_contrl[i].enable == SOCKET_DISABLE) { + g_contrl[i].enable = SOCKET_ENABLE; + g_contrl[i].service_id = new_fd; + break; + } + } + + if(new_fd > *maxsock) { + *maxsock = new_fd; + } + + return 0; +} + +static void SocketRelease(int sock_fd) +{ + for (int i = 0; i < MAX_CONNECT; i++) { + if(g_contrl[i].service_id != 0) { + close(g_contrl[i].service_id); + g_contrl[i].service_id = 0; + g_contrl[i].enable = SOCKET_DISABLE; + } + } + if(sock_fd) { + close(sock_fd); + } +} + +static int ReadHandle(stContrlParam *ctrl, fd_set *fdSet) +{ + char recvbuf[SOCKET_BUF_SIZE] = {0}; + memset(recvbuf, '\0', sizeof(recvbuf)); + int recvbytes = read(ctrl->service_id, recvbuf, sizeof(recvbuf) - 1); + if(recvbytes <= 0) { + HILOG_INFO("=====close client=====%{public}d===\n", ctrl->service_id); + close(ctrl->service_id); + FD_CLR(ctrl->service_id, fdSet); + ctrl->service_id = 0; + ctrl->enable = SOCKET_DISABLE; + g_connAmount--; + } else { + HILOG_INFO("=====server recv recvbytes=====%{public}d==%{public}s=\n", recvbytes, recvbuf); + bzero(recvClientMsg, sizeof(recvClientMsg)); + strcpy_s(recvClientMsg, sizeof(recvClientMsg), recvbuf); + } + return 0; +} + +static void HeartTick(fd_set *fdset) { + for(int i = 0; i < MAX_CONNECT; i++) { + if(g_contrl[i].service_id != 0) { + char bufr[SOCKET_BUF_SIZE] = {0}; + memset(bufr, '\0', sizeof(bufr)); + write(g_contrl[i].service_id, "{LIVE}", strlen("{LIVE}")); + int ret = read(g_contrl[i].service_id, bufr, sizeof(bufr)); + HILOG_INFO("=====heart tick recv=====%{public}s==%{public}d=\n", bufr, ret); + if(ret <= 0) { + close(g_contrl[i].service_id); + FD_CLR(g_contrl[i].service_id, fdset); + g_contrl[i].enable = SOCKET_DISABLE; + g_contrl[i].service_id = 0; + g_contrl[i].countTick = 0; + g_connAmount--; + HILOG_INFO("=====close client=====%{public}d===\n", i); + } + } + } +} + +static int InitServer(int *sockfd) +{ + memset_s(g_contrl, sizeof(stContrlParam), 0, sizeof(stContrlParam) * MAX_CONNECT); + g_connAmount = 0; + int sock_fd = 0; + int yes = 1; + struct sockaddr_in server_addr; + if((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERR) { + HILOG_INFO("===socket error=====\n"); + return -1; + } + + if(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == SOCKET_ERR) { + HILOG_INFO("=====setsockopt error=====\n"); + close(sock_fd); + return -1; + } + + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(DEFAULT_PORT); + server_addr.sin_addr.s_addr = INADDR_ANY; + memset_s(server_addr.sin_zero, sizeof(server_addr.sin_zero), '\0', sizeof(server_addr.sin_zero)); + + if(::bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == SOCKET_ERR) { + HILOG_INFO("=====bind error=====\n"); + close(sock_fd); + return -1; + } + + if(listen(sock_fd, MAX_CONNECT) == SOCKET_ERR) { + HILOG_INFO("======listen error=====\n"); + close(sock_fd); + return -1; + } + + HILOG_INFO("======listen port==%{public}d===\n", DEFAULT_PORT); + + *sockfd = sock_fd; + + return 0; +} + +static void *SocketServiceCreate(void *arg) { + int maxsock; + int sock_fd = 0; + struct sockaddr_in client_addr; + socklen_t sin_size; + fd_set fdset; + struct timeval tv; + + if(InitServer(&sock_fd) < 0) { + HILOG_INFO("=====InitServer failed=====\n"); + return nullptr; + } + + sin_size = sizeof(client_addr); + maxsock = sock_fd; + while (g_threadFlag) { + int ret, new_fd, i; + FD_ZERO(&fdset); + FD_SET(sock_fd, &fdset); + tv.tv_sec = 10; // 10 second + tv.tv_usec = 0; + for (i = 0; i < MAX_CONNECT; i++) { + if(g_contrl[i].service_id != 0){ + FD_SET(g_contrl[i].service_id, &fdset); + } + } + + ret = select(maxsock + 1, &fdset, nullptr, nullptr, &tv); + HILOG_INFO("=====select error===\n"); + if(ret < SOCKET_OK){ + HILOG_INFO("=====select error=====\n"); + break; + } + else if(ret == SOCKET_OK) { + HeartTick(&fdset); + continue; + } + + for (i = 0; i < MAX_CONNECT; i++) { + if(FD_ISSET(g_contrl[i].service_id, &fdset)) { + ReadHandle(&g_contrl[i], &fdset); + } + } + + if(FD_ISSET(sock_fd, &fdset)) { + new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size); + if(new_fd <= 0){ + continue; + } + if(SetConnectParam(new_fd, &maxsock) < 0) { + close(new_fd); + } + } + } + SocketRelease(sock_fd); + return (void *)EXEC_SUCC; +} + +// 业务逻辑处理函数,由worker线程池调度执行。 +static void initServerExecuteCB(napi_env env, void *data) +{ + CommandStrData *commandStrData = (CommandStrData *)data; + pthread_t tidSend; + if(pthread_create(&tidSend, nullptr, SocketServiceCreate, nullptr) != EXEC_SUCC) { + perror("pthread_create recv error"); + commandStrData->result.state = ERROR; + commandStrData->result.result = "ERROR"; + commandStrData->result.time = "0s"; + exit(1); + } + pthread_detach(tidSend); + commandStrData->result.state = SUCCESS; + commandStrData->result.result = "SUCCESS"; + commandStrData->result.time = "1s"; +} + +static void initServerPromiseCompleteCB(napi_env env, napi_status status, void *data) +{ + CommandStrData *commandStrData = (CommandStrData *)data; + + napi_value result = nullptr; + napi_value resState = nullptr; + napi_value resResult = nullptr; + napi_value resTime = nullptr; + + char tempChar[256] = {}; + napi_create_int32(env, (int32_t)commandStrData->result.state, &resState); + strcpy_s(tempChar, sizeof(tempChar), commandStrData->result.result.c_str()); + napi_create_string_utf8(env, tempChar, strlen(tempChar), &resResult); + + bzero(tempChar, strlen(tempChar)); + strcpy_s(tempChar, sizeof(tempChar), commandStrData->result.time.c_str()); + napi_create_string_utf8(env, tempChar, strlen(tempChar), &resTime); + + napi_create_object(env, &result); + napi_set_named_property(env, result, "state", resState); + napi_set_named_property(env, result, "result", resResult); + napi_set_named_property(env, result, "time", resTime); + + napi_resolve_deferred(env, commandStrData->deferred, result); + + // 删除napi_ref对象 + if(commandStrData->callback != nullptr) { + napi_delete_reference(env, commandStrData->callback); + } + + // 删除异步工作项 + napi_delete_async_work(env, commandStrData->asyncWork); + delete commandStrData; +} + +/** + * @brief 控制机器狗做动作 + * + * @param env 用于存储JS虚拟机的上下文 + * @param info 传递函数调用时的上下文信息,如this指针、参数列表值等 + * @return napi_value 返回处理结果 + */ +static napi_value sendRobotDogCommand(napi_env env, napi_callback_info info){ + // 获取JS传入的命令(1个参数),值的类型是js类型(napi_value) + size_t argc = 1; + napi_value command[1]; + napi_value thisArg = nullptr; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, command, &thisArg, nullptr)); + + // 获取并判断js参数类型 + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, command[0], &valuetype0)); + + if(valuetype0 != napi_string){ + napi_throw_type_error(env, nullptr, "Wrong arguments. string expected."); + return nullptr; + } + + // 创建promise + napi_value promise = nullptr; + napi_deferred deferred = nullptr; + NAPI_CALL(env, napi_create_promise(env, &deferred, &promise)); + + // 异步工作项上下文用户数据,传递到异步工作项的execute、complete之间传递数据 + auto commandStrData = new CommandStrData{ + .asyncWork = nullptr, + .deferred = deferred, + }; + + // 将接收到的参数传入 + size_t getStrRes; + NAPI_CALL(env, napi_get_value_string_utf8(env, command[0], commandStrData->robotDogCommand, + (size_t)sizeof(commandStrData->robotDogCommand), &getStrRes)); + + HILOG_INFO("=====socket=%{public}s,called,=======%{public}s", __func__, commandStrData->robotDogCommand); + // 创建async work,创建成功后通过最后一个参数(commandStrData->asyncWork)返回async work的handle + napi_value resourceName = nullptr; + napi_create_string_utf8(env, "sendRobotDogCommand", NAPI_AUTO_LENGTH, &resourceName); + napi_create_async_work(env, nullptr, resourceName, sendRobotDogCommandExecuteCB, sendRobotDogCommandPromiseCompleteCB, + (void *)commandStrData, &commandStrData->asyncWork); + + // 将刚创建的async work加到队列,由底层去调度执行 + napi_queue_async_work(env, commandStrData->asyncWork); + + // 返回promise + return promise; +} + +/** + * @brief 接受设备信息 + * + * @param env 用于存储JS虚拟机的上下文 + * @param info 传递函数调用时的上下文信息,如this指针、参数列表值等 + * @return napi_value 返回处理结果 + */ +static napi_value recvMsg(napi_env env, napi_callback_info info){ + // 创建promise + napi_value promise = nullptr; + napi_deferred deferred = nullptr; + NAPI_CALL(env, napi_create_promise(env, &deferred, &promise)); + + // 异步工作项上下文用户数据,传递到异步工作项的execute、complete之间传递数据 + auto commandStrData = new CommandStrData{ + .asyncWork = nullptr, + .deferred = deferred, + }; + + // 创建async work,创建成功后通过最后一个参数(commandStrData->asyncWork)返回async work的handle + napi_value resourceName = nullptr; + napi_create_string_utf8(env, "recvCompassMsg", NAPI_AUTO_LENGTH, &resourceName); + napi_create_async_work(env, nullptr, resourceName, recvCompassMsgExecuteCB, recvCompassMsgPromiseCompleteCB, + (void *)commandStrData, &commandStrData->asyncWork); + + // 将刚创建的async work加到队列,由底层去调度执行 + napi_queue_async_work(env, commandStrData->asyncWork); + + // 返回promise + return promise; +} + +/** + * @brief 初始化服务端 + * + * @param env 用于存储JS虚拟机的上下文 + * @param info 传递函数调用时的上下文信息,如this指针、参数列表值等 + * @return napi_value 返回处理结果 + */ +static napi_value initServer(napi_env env, napi_callback_info info){ + // 创建promise + napi_value promise = nullptr; + napi_deferred deferred = nullptr; + NAPI_CALL(env, napi_create_promise(env, &deferred, &promise)); + + // 异步工作项上下文用户数据,传递到异步工作项的execute、complete之间传递数据 + auto commandStrData = new CommandStrData{ + .asyncWork = nullptr, + .deferred = deferred, + }; + + // 创建async work,创建成功后通过最后一个参数(commandStrData->asyncWork)返回async work的handle + napi_value resourceName = nullptr; + napi_create_string_utf8(env, "initServer", NAPI_AUTO_LENGTH, &resourceName); + napi_create_async_work(env, nullptr, resourceName, initServerExecuteCB, initServerPromiseCompleteCB, + (void *)commandStrData, &commandStrData->asyncWork); + + // 将刚创建的async work加到队列,由底层去调度执行 + napi_queue_async_work(env, commandStrData->asyncWork); + + // 返回promise + return promise; +} + +// ---------------------------------------------------------------------------------- +// napi_addon_register_func +static napi_value registerFunc(napi_env env, napi_value exports){ + static napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("initServer", initServer), + DECLARE_NAPI_FUNCTION("recvMsg", recvMsg), + DECLARE_NAPI_FUNCTION("sendRobotDogCommand", sendRobotDogCommand), + + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + return exports; +} + +/* 定义napi module + * nm_modname: 模块名称,对应eTS使用为import nm_modname from '@ohos.ohos_shared_library_name' + * 示例对应为:import hellonapi from '@ohos.hellonapi' + * nm_register_func:API注册函数 + */ +static napi_module tcpServerApi = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = registerFunc, + .nm_modname = "tcpserverapi", + .nm_priv = ((void *)0), + .reserved = {0}, +}; + +// ability module register +extern "C" __attribute__((constructor)) void tcpServerApiRegister() { napi_module_register(&tcpServerApi); }