文章目录
- 在openharmony的项目开发中大量使用到北向应用调取与真实物理环境交互数据的场景,比如农田检测,污染检测,甚至是一些机器人的控制和数据回收,这些功能的实现都必须要求北向使用ark编译器编译js的同时,在其中调用的js api必须由南向提供正常功能的实现。这些部分的功能实现在南向除了用户态内核态程序的维护外还需要实现与北向应用通信的接口,接口这部分将在本篇文章中仔细探讨。
- napi_generator: NAPI框架生成工具 (gitee.com)。 ace_napi: Development framework for extending the JS Native Module | 。 原生模块扩展开发框架 (gitee.com)。 OpenHarmony 源码解析之JavaScript API框架(NAPI)-51CTO.COM。 OpenHarmony 源码解析之 JavaScriptAPI NAPI-C 接口-51CTO.COM。 openharmony标准系统L2 JS、eTS 的napi socket 网络接口开发 TCP-开源基础软件社区-51CTO.COM。
- 造北向程序中导入一个jsapi接口即可实现数据的上传和下发。 在本文中采用简化描写 c->napi napi->c 来表示南向发送数据到北向和北向发送数据到南向。 其实还看到了很多种可能的南北向通讯机制,但是都没有更加详细的参数手册和操作方案,等到过段时间官方实现的更加完善后再做一些更深入的研究,包括通过c++实现Ability再通过北向调用ability进行数据收取,使用泛sensor子系统进行传感器和执行器的配置。或者是通过软总线,把l0/l1的物联网openharmony设备的信息转发到北向已经实现的支持网络协议和分布式软总线的设备上。 OpenHarmony上JS API实现方式有三种,分别是:JSI机制、Channel机制、NAPI机制。 JSI机制:L0~L1设备支持。 Channel机制:L3设备支持。 NAPI机制:目前仅L2-L3设备支持,后续须推广到L3~L5设备。 这部分中的工作目前JSI机制在小熊派的板子上有过尝试,而且教程和案例都还比较全面,channel机制目前在官方文档中没有看到,只闻其声不见其形,NAPI则是可以采用作为标准open harmony系统可以使用的比较好用的接口方式。 在完成JS API 北向的操作方案是仅需要调取js api完成一个接口函数的调用即可,在此处约定完成,北向调用的库是napi_demo。 import sample from '@ohos.napi_demo' ...... onTest: function () { console.log("napi_demo.test start") sample.test(2); //JS调用napi接口 console.log("sample.test end") }, 在调用过程中可以使用多线程模式,去开启其他连续任务。 在开发中尝试了使用北向app发送信息点灯,但是在子系统的线程中访问linux驱动核心遇到了一些问题,目前仍在解决中,本篇文章先总结了各个大佬的思路去完整展现一遍NAPI构造南北向桥梁的过程。
- 分别采用两种方案实现,第二种方案不是非常普遍适用,因为他只是调用了napi的接口且生成的是gn中定义的shared lib,如果其他的真实进程写在一起将产生一个奇怪的内存隔离现象,napi有一部分数据拿取不到。
- 代码和实现如下: 首先构建目录结构。 –参考系列文章3。 代码如下: // app.cpp#include <stdio.h>#include <string.h>#include "napi/native_api.h"#include "napi/native_node_api.h"struct AsyncCallbackInfo { napi_env env; napi_async_work asyncWork; napi_deferred deferred;};// getAppName对应的C/C++实现函数napi_value JSGetAppName(napi_env env, napi_callback_info info) { napi_deferred deferred; napi_value promise; // 创建promise NAPI_CALL(env, napi_create_promise(env, &deferred, &promise)); AsyncCallbackInfo* asyncCallbackInfo = new AsyncCallbackInfo { .env = env, .asyncWork = nullptr, .deferred = deferred, }; napi_value resourceName; napi_create_string_latin1(env, "GetAppName", NAPI_AUTO_LENGTH, &resourceName); // 创建异步任务队列 napi_create_async_work( env, nullptr, resourceName, // 异步任务的回调 [](napi_env env, void* data) {}, // 异步任务结束后的回调 [](napi_env env, napi_status status, void* data) { AsyncCallbackInfo* asyncCallbackInfo = (AsyncCallbackInfo*)data; napi_value appName; const char* str = "helloworld"; napi_create_string_utf8(env, str, strlen(str), &appName); // 触发回调 napi_resolve_deferred(asyncCallbackInfo->env, asyncCallbackInfo->deferred, appName); napi_delete_async_work(env, asyncCallbackInfo->asyncWork); delete asyncCallbackInfo; }, (void*)asyncCallbackInfo, &asyncCallbackInfo->asyncWork); napi_queue_async_work(env, asyncCallbackInfo->asyncWork); return promise;}// 模块导出入口函数static napi_value AppExport(napi_env env, napi_value exports){ static napi_property_descriptor desc[] = { DECLARE_NAPI_FUNCTION("getAppName", JSGetAppName), }; NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); return exports;}// app模块描述static napi_module appModule = { .nm_version = 1, .nm_flags = 0, .nm_filename = nullptr, .nm_register_func = AppExport, .nm_modname = "app", .nm_priv = ((void*)0), .reserved = {0}};// 注册模块extern "C" __attribute__((constructor)) void AppRegister(){ napi_module_register(&appModule);} 同组 BUILD.gn实现: // BUILD.gnimport("//build/ohos.gni")ohos_shared_library("app") { # 指定编译源文件 sources = [ "app.cpp", ] # 指定编译依赖 deps = [ "//foundation/ace/napi:ace_napi" ] # 指定库生成的路径 relative_install_dir = "module" subsystem_name = "ace" part_name = "napi"} 在编译后会生成一个动态链接库下载进入后即可从北向与南向完成交互(这是官方的解决方案)。 仔细看代码部分会发现有两个注册部分,一个是参数表一个是接口声明,这与说明文档一致。 // app模块描述static napi_module appModule = { .nm_version = 1, .nm_flags = 0, .nm_filename = nullptr, .nm_register_func = AppExport, .nm_modname = "app", .nm_priv = ((void*)0), .reserved = {0}};// 注册模块extern "C" __attribute__((constructor)) void AppRegister(){ napi_module_register(&appModule);}
- 建立目录,参考本系列第二篇文章中的代码和架构。 |-- BUILD.gn|-- app.cpp|-- ohos.build BUILD.gn: import("//build/ohos.gni") ohos_shared_library("myapp") { # 指定编译源文件 sources = [ "app.cpp", ] # 指定编译依赖,如果依赖第三方库,需要在此添加 deps = [ "//foundation/ace/napi:ace_napi" ] # 指定库生成的路径 relative_install_dir = "module" # 子系统及其组件,后面会引用 subsystem_name = "myapp" part_name = "myapp_part" } ohos.build: { "subsystem": "myapp", "parts": { "myapp_part": { "module_list": [ "//myapp:myapp" ], "test_list": [ ] } }} #include <assert.h>#include "napi/native_api.h"#include "napi/native_node_api.h"static napi_value Method(napi_env env, napi_callback_info info) { napi_status status; napi_value world; status = napi_create_string_utf8(env, "Hello, world!", 13, &world); assert(status == napi_ok); return world;}static napi_value Init(napi_env env, napi_value exports) { napi_status status; napi_property_descriptor desc[] = { DECLARE_NAPI_FUNCTION("hello", Method), }; status = napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); assert(status == napi_ok); return exports;}NAPI_MODULE(myapp, Init) 其他部分参考本系列第二篇文章进行配置,会产生一个新的myapp子系统,该子系统可以完成和北向的单向交互调用。 仔细看这部分c++代码发现并没有参数表和注册,只是调用了一个函数。 NAPI_MODULE(myapp, Init) 但是经过测试这些代码确实能够完成指定任务,这也是让人感觉些许迷惑的地方。
- 但是两部分除了模块注册之外,函数注册以及函数实现方式都是一致的,并没有明显区别。 具体的实现流程和操作。 首先要了解,NAPI的机制中实现都是异步机制,而异步模型包括两种常用的是callback模型,Promise模型还没有进行过测试。似乎也有一些同步机制的方法,但是没看到有很多讲解。 Promise、Callback 异步模型都是 OHOS 标准异步模型之一。 (以下两个小结摘录自大佬的文章,在官方文档中并没有查到相关的技术手册。)
-
- ES6原生提供了Promise对象,Promise是异步编程的一种解决方案,可以替代传统的解决方案回调函数和事件。 promise对象是一个异步操作的结果,提供了一些API使得异步执行可以按照同步的流表示出来,避免了层层嵌套的回调函数,保证了回调是以异步的方式进行调用的。 用户在调用这些接口的时候,接口实现将异步执行任务,同时返回一个 Promise 对象,其代表异步操作的结果。 在返回的结果的个数超过一个时,其以对象属性的形式返回。
- 对象的状态不受外界影响。 一旦状态改变了就不会再变,也就是说任何时候Promise都只有一种状态。
- 用户在调用这些接口的时候,接口实现将异步执行任务。 任务执行结果以参数的形式提供给用户注册的回调函数。 这些参数的第一个是 Error 或 undefined 类型,分别表示执行出错与正常。
- 首先napi的实现必须包括两个部分函数,第一个是模块初始化的部分,在其中要进行函数的定义。 static napi_value Init(napi_env env, napi_value exports) { napi_status status; napi_property_descriptor desc[] = { DECLARE_NAPI_FUNCTION("hello", Method), }; status = napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); assert(status == napi_ok); return exports;} 使用: DECLARE_NAPI_FUNCTION("hello", Method), 参数一是北向使用的js名称,参数二是南向需要实现的函数名称。 其他的部分一般不需要做改变。 另一个部分是具体的函数实现。 static napi_value Method(napi_env env, napi_callback_info info) { napi_status status; napi_value world; status = napi_create_string_utf8(env, "Hello, world!", 13, &world); assert(status == napi_ok); return world;} 以上代码就简单实现了一个赋值helloworld到变量池的操作,在北向调用就可以简介访问到这个变量。 关于异步实现我搬运大佬的代码过来大家一起看看然后保留一下以后学习用,目前还不是很理解,更不要说使用了,菜鸡保命,在之后的研究中心突破了这块会来更新文章。 ·```Cstatic napi_value Get(napi_env env, napi_callback_info info){size_t requireArgc = 1;size_t argc = 3; //参数个数napi_value argv[3] = { 0 }; //参数定义napi_value thisVar = nullptr; //JS对象的this参数void* data = nullptr; //回调数据指针/* 根据环境变量获取参数 */napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);NAPI_ASSERT(env, argc >= requireArgc, "requires 1 parameter");/* 异步接口上下文,用于接收JS接口传进来的环境变量、参数、回调函数、接口返回值等*/auto asyncContext = new StorageAsyncContext();asyncContext->env = env;for (size_t i = 0; i < argc; i++) { napi_valuetype valueType = napi_undefined; napi_typeof(env, argv[i], &valueType); if ((i == 0) && (valueType == napi_string)) { /* 根据JS字符串获取对应的UTF8编码格式的C/C++字符串 */ napi_get_value_string_utf8(env, argv[i], asyncContext->key, KEY_BUFFER_SIZE, &asyncContext->keyLen); } else if (valueType == napi_string) { napi_get_value_string_utf8(env, argv[i], asyncContext->value, VALUE_BUFFER_SIZE, &asyncContext->valueLen); } else if (valueType == napi_function) { /* 根据JS对象参数argv[i]新建引用 */ napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef); break; } else { NAPI_ASSERT(env, false, "type mismatch"); }}napi_value result = nullptr;if (asyncContext->callbackRef == nullptr) { /* Promise方式异步调用,创建延迟对象、JS Promise对象,使二者进行关联 */ napi_create_promise(env, &asyncContext->deferred, &result);} else { /* Callback方式异步调用,不需要返回Promise对象,返回一个JS未定义值 */ napi_get_undefined(env, &result);}/* 根据JS对象获取与之绑定的原生对象实例 */napi_unwrap(env, thisVar, (void**)&asyncContext->objectInfo);napi_value resource = nullptr;napi_create_string_utf8(env, "JSStorageGet", NAPI_AUTO_LENGTH, &resource); //获取JS异步资源名称/* 创建异步工作 */napi_create_async_work( env, nullptr, resource, /* 执行异步逻辑的原生函数 */ [](napi_env env, void* data) { StorageAsyncContext* asyncContext = (StorageAsyncContext*)data; auto itr = g_keyValueStorage.find(asyncContext->key); if (itr != g_keyValueStorage.end()) { if (strncpy_s(asyncContext->value, VALUE_BUFFER_SIZE, itr->second.c_str(), itr->second.length()) == -1) { asyncContext->status = 1; //失败 } else { asyncContext->status = 0; //成功 } } else { asyncContext->status = 1; //失败 } }, /* 异步函数执行完成或者取消后,需要执行的后处理函数 */ [](napi_env env, napi_status status, void* data) { StorageAsyncContext* asyncContext = (StorageAsyncContext*)data; napi_value result[2] = { 0 }; if (!asyncContext->status) { napi_get_undefined(env, &result[0]); napi_create_string_utf8(env, asyncContext->value, strlen(asyncContext->value), &result[1]); } else { napi_value message = nullptr; napi_create_string_utf8(env, "key does not exist", NAPI_AUTO_LENGTH, &message); napi_create_error(env, nullptr, message, &result[0]); napi_get_undefined(env, &result[1]); asyncContext->objectInfo->Emit(nullptr, "error"); } if (asyncContext->deferred) { if (!asyncContext->status) { /* 异步函数执行成功后,执行成功后处理函数 */ napi_resolve_deferred(env, asyncContext->deferred, result[1]); } else { /* 异步函数执行失败后,执行失败后处理函数 */ napi_reject_deferred(env, asyncContext->deferred, result[0]); } } else { napi_value callback = nullptr; napi_get_reference_value(env, asyncContext->callbackRef, &callback); napi_call_function(env, nullptr, callback, sizeof(result) / sizeof(result[0]), result, nullptr); napi_delete_reference(env, asyncContext->callbackRef); } /* 异步回调完成后进行资源释放 */ napi_delete_async_work(env, asyncContext->work); delete asyncContext; }, /* 用户数据上下文,此数据传递给异步执行函数与后处理函数 */ (void*)asyncContext, /* 生成的异步工作*/ &asyncContext->work);napi_queue_async_work(env, asyncContext->work); //异步工作入队列,排队执行return result;} 在c->js的过程中,重要的代码如下,主要以回调函数出现。 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data)); NAPI_ASSERT(env, argc >= 1, "JSCallback Wrong number of arguments"); //参数个数校验 napi_typeof(env, argv[0], &callbackType); napi_create_reference(env, argv[0], 1, &CallbackRef); //创建引用 napi_get_reference_value(env, CallbackRef, &callback); //根据引用获取回调函数callback js -> c的过程中,js拉起在南向实现的函数接口,并且南向需要做一次js参数到c参数的转换,重要的代码如下: NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data)); NAPI_ASSERT(env, argc >= 1, "JSTest Wrong number of arguments"); //参数个数校验 //参数类型校验 napi_valuetype valueType = napi_undefined; napi_typeof(env, argv[i], &valueType); napi_get_value_int32(env, argv[i], &cPara1);//参数转换
- 参考文献:openharmony标准系统L2 JS、eTS 的napi socket 网络接口开发 TCP-开源基础软件社区-51CTO.COM。 主要参考了上面这篇中大佬讲解的一些工作,然后在此基础上做了一些线程增加,在有相关数据回调之后再南向的硬件端除了tcpip协议拉起wifi组网完成信息发送外,也完成比如一些硬件驱动的使用。 原始代码来源于上文中大佬的文章。 /* * 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 "napi/native_api.h"#include "napi/native_node_api.h"#include "utils/log.h"#include <stdlib.h> // standard library 标准库函数头文件#include <stdio.h> // standard input output 标准输入输出函数#include <stdint.h> // 定义了扩展的整数类型和宏#include <unistd.h> // POSIX 系统 API 访问功能的头文件#include <fcntl.h> // unix标准中通用的头文件 define O_WRONLY and O_RDONLY #include <sys/socket.h>#include <sys/types.h>#include <netinet/in.h>#include <arpa/inet.h>#include <ctype.h>#include <string.h>#include <signal.h>#include <pthread.h> static char buf[1024];static size_t buflen;static int wflag;static int st;static int client_st;static napi_ref CallbackReff;static napi_env envs;void* socketserverthrd(void *ptr){ napi_value jsObj, prop1,prop2,prop3, callback = nullptr,undefine = nullptr; napi_get_reference_value(envs, CallbackReff, &callback); int port = 18000; st = socket(AF_INET, SOCK_STREAM, 0); int opt = SO_REUSEADDR; setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1) { HILOG_INFO("test0002 bind failed %s\n", strerror(errno)); return NULL; } if (listen(st, 20) == -1) { HILOG_INFO("test0002 listen failed %s\n", strerror(errno)); return NULL; } HILOG_INFO("test0002 listen success\n"); struct sockaddr_in client_addr; memset(&client_addr, 0, sizeof(client_addr)); socklen_t len = sizeof(client_addr); HILOG_INFO("test0002 waiting for client.......\n"); wflag = 1; char str[1024]; while(wflag) { client_st = accept(st, (struct sockaddr*) &client_addr, &len); if (client_st == -1) { HILOG_INFO("test0002 accept failed %s\n", strerror(errno)); return NULL; } HILOG_INFO("test0002 accept by %s\n", inet_ntoa(client_addr.sin_addr)); while (wflag) { memset(str, 0, sizeof(str)); int numbytes = recv(client_st, str, sizeof(str), 0); if (numbytes <= 0) break; //if(wflag == 0) break; strcpy(buf,str); if((int)str[0] == 170) { int cPara1 = (int)str[1]; int cPara2 = (int)str[2]; int cPara3 = (int)str[3]; napi_create_object(envs, &jsObj); //创建JS回调函数对应的参数对象 napi_create_int32(envs, cPara1, &prop1); napi_create_int32(envs, cPara2, &prop2); napi_create_int32(envs, cPara3, &prop3); napi_set_named_property(envs, jsObj, "prop1", prop1); //设置JS参数对象属性值 napi_set_named_property(envs, jsObj, "prop2", prop2); napi_set_named_property(envs, jsObj, "prop3", prop3); napi_call_function(envs, nullptr, callback, 1, &jsObj, &undefine); //使用生成的JS参数,调用对应的JS回调函数 } buflen = strlen(str); //send(client_st, str, strlen(str), 0); } } return NULL;}//启动static napi_value ServerStart(napi_env env, napi_callback_info info){ size_t argc = 1; //参数个数定义 napi_value argv[argc]; napi_value thisVar = nullptr; void *data = nullptr; envs = env; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data)); NAPI_ASSERT(env, argc >= 1, "JSCallback Wrong number of arguments"); //参数个数校验 napi_valuetype callbackType = napi_undefined; napi_typeof(env, argv[0], &callbackType); NAPI_ASSERT(env, callbackType == napi_function, "parameter 1 type mismatch"); //参数类型校验,传进来的须是函数类型 napi_create_reference(env, argv[0], 1, &CallbackReff); //创建引用 //napi_get_reference_value(env, CallbackRef, &callback); //根据引用获取回调函数callback pthread_t thrd; HILOG_INFO("test0002 thrs start!"); //pthread_create(&thrd1, NULL, recvsocket, &client_st); pthread_create(&thrd, NULL, socketserverthrd,NULL); HILOG_INFO("test0002 end!"); napi_value result = nullptr; napi_get_undefined(env, &result); return result;}//停止static napi_value ServerStop(napi_env env, napi_callback_info info){ close(st); wflag = 0; napi_value result = nullptr; napi_get_undefined(env, &result); return result;//发送static napi_value ServerWrite(napi_env env, napi_callback_info info){ size_t requireArgc = 3; size_t argc = 3; napi_value args[3] = { nullptr }; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments"); napi_valuetype valuetype0; NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); napi_valuetype valuetype1; NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1)); napi_valuetype valuetype2; NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2)); NAPI_ASSERT(env, valuetype0 == napi_number && valuetype1 == napi_number && valuetype2 == napi_number, "Wrong argument type. Numbers expected."); char str[4]; uint32_t a,b,c; NAPI_CALL(env, napi_get_value_uint32(env, args[0], &a)); NAPI_CALL(env, napi_get_value_uint32(env, args[1], &b)); NAPI_CALL(env, napi_get_value_uint32(env, args[2], &c)); str[0] = (char)0xAA; str[1] = (char)a; str[2] = (char)b; str[3] = (char)c; if (-1 == write(client_st, str,4)){ HILOG_INFO("test0002 okok servertest error"); } napi_value result = nullptr; napi_get_undefined(env, &result); return result;}EXTERN_C_START/* * function for module exports */ static napi_value Init(napi_env env, napi_value exports) { /* * Properties define */ napi_property_descriptor desc[] = { DECLARE_NAPI_FUNCTION("ServerStart", ServerStart), DECLARE_NAPI_FUNCTION("ServerStop", ServerStop), DECLARE_NAPI_FUNCTION("ServerWrite", ServerWrite) }; NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); return exports; }EXTERN_C_END/* * Module define */ static napi_module demoModule = { .nm_version = 1, .nm_flags = 0, .nm_filename = nullptr, .nm_register_func = Init, .nm_modname = "socketserver", .nm_priv = ((void*)0), .reserved = { 0 }, }; /* * Module register function */ extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&demoModule); } 使用的是上文中方案二提到的napi注册方案完成了有关的注册,并且构造了三个北向可以调用的接口函数,完成tcp ip的读写操作。 由于篇幅限制,另一版本完成的线程增加就不放代码了,有需要可留言联系。 分析一下代码中的线程拉起。 pthread_create(&thrd, NULL, socketserverthrd,NULL); 使用该函数创建了一个新的线程完成tcp servo的工作,但是在这个工作之前已经写好了有关的头文件和一些变量。 #include <pthread.h> 在我们所设定的项目中,tcp也是基于这部分的servo和client完成的数据交换。 在这部分代码中需要更改和注意的地方是port以及地址参数需要修正,设定值和代码中的值应当不同。 如果按照文中描述的方法把模块写在了ace的demo模组中编译时的参数一定不能写错,要使用这个参数才能成功编译。 ./build.sh --product-name Hi3516DV300 --build-target make_test TCPIP的协议帧组成和协议解析需要专门的库和函数处理,且使用的是16进制码,需要注意的是需要提前对所传输的数据进行处理,传出后也需要做相关的解析。
- 南北向接口的互相实现是openharmony程序中极为重要的一部分,在这部分当中既要启动底层驱动又要处理好留给北向的接口协议,对于整体的系统结构和代码解耦有比较大的要求,目前有些第三方库在调取extern_depend时无效,且没有给出说明,在napi子系统中启动hilog一直无法成功,有待解决后会补充一篇文章。 想了解更多关于开源的内容,请访问: 51CTO 开源基础软件社区 https://ost.51cto.com。

在openharmony的项目开发中大量使用到北向应用调取与真实物理环境交互数据的场景,比如农田检测,污染检测,甚至是一些机器人的控制和数据回收,这些功能的实现都必须要求北向使用ark编译器编译js的同时,在其中调用的js api必须由南向提供正常功能的实现。这些部分的功能实现在南向除了用户态内核态程序的维护外还需要实现与北向应用通信的接口,接口这部分将在本篇文章中仔细探讨。
- napi_generator: NAPI框架生成工具 (gitee.com)。
- ace_napi: Development framework for extending the JS Native Module | 。 原生模块扩展开发框架 (gitee.com)。
- OpenHarmony 源码解析之JavaScript API框架(NAPI)-51CTO.COM。
- OpenHarmony 源码解析之 JavaScriptAPI NAPI-C 接口-51CTO.COM。
- openharmony标准系统L2 JS、eTS 的napi socket 网络接口开发 TCP-开源基础软件社区-51CTO.COM。
造北向程序中导入一个jsapi接口即可实现数据的上传和下发。
在本文中采用简化描写 c->napi napi->c 来表示南向发送数据到北向和北向发送数据到南向。
其实还看到了很多种可能的南北向通讯机制,但是都没有更加详细的参数手册和操作方案,等到过段时间官方实现的更加完善后再做一些更深入的研究,包括通过c++实现Ability再通过北向调用ability进行数据收取,使用泛sensor子系统进行传感器和执行器的配置。或者是通过软总线,把l0/l1的物联网openharmony设备的信息转发到北向已经实现的支持网络协议和分布式软总线的设备上。
OpenHarmony上JS API实现方式有三种,分别是:JSI机制、Channel机制、NAPI机制。
- JSI机制:L0~L1设备支持。
- Channel机制:L3设备支持。
- NAPI机制:目前仅L2-L3设备支持,后续须推广到L3~L5设备。
这部分中的工作目前JSI机制在小熊派的板子上有过尝试,而且教程和案例都还比较全面,channel机制目前在官方文档中没有看到,只闻其声不见其形,NAPI则是可以采用作为标准open harmony系统可以使用的比较好用的接口方式。

在完成JS API 北向的操作方案是仅需要调取js api完成一个接口函数的调用即可,在此处约定完成,北向调用的库是napi_demo。
import sample from '@ohos.napi_demo'
......
onTest: function () {
console.log("napi_demo.test start")
sample.test(2); //JS调用napi接口
console.log("sample.test end")
},
在调用过程中可以使用多线程模式,去开启其他连续任务。
在开发中尝试了使用北向app发送信息点灯,但是在子系统的线程中访问linux驱动核心遇到了一些问题,目前仍在解决中,本篇文章先总结了各个大佬的思路去完整展现一遍NAPI构造南北向桥梁的过程。
分别采用两种方案实现,第二种方案不是非常普遍适用,因为他只是调用了napi的接口且生成的是gn中定义的shared lib,如果其他的真实进程写在一起将产生一个奇怪的内存隔离现象,napi有一部分数据拿取不到。
代码和实现如下:
首先构建目录结构。
–参考系列文章3。
代码如下:
// app.cpp
#include <stdio.h>
#include <string.h>
#include "napi/native_api.h"
#include "napi/native_node_api.h"
struct AsyncCallbackInfo {
napi_env env;
napi_async_work asyncWork;
napi_deferred deferred;
};
// getAppName对应的C/C++实现函数
napi_value JSGetAppName(napi_env env, napi_callback_info info) {
napi_deferred deferred;
napi_value promise;
// 创建promise
NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
AsyncCallbackInfo* asyncCallbackInfo = new AsyncCallbackInfo {
.env = env,
.asyncWork = nullptr,
.deferred = deferred,
};
napi_value resourceName;
napi_create_string_latin1(env, "GetAppName", NAPI_AUTO_LENGTH, &resourceName);
// 创建异步任务队列
napi_create_async_work(
env, nullptr, resourceName,
// 异步任务的回调
[](napi_env env, void* data) {},
// 异步任务结束后的回调
[](napi_env env, napi_status status, void* data) {
AsyncCallbackInfo* asyncCallbackInfo = (AsyncCallbackInfo*)data;
napi_value appName;
const char* str = "helloworld";
napi_create_string_utf8(env, str, strlen(str), &appName);
// 触发回调
napi_resolve_deferred(asyncCallbackInfo->env, asyncCallbackInfo->deferred, appName);
napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
delete asyncCallbackInfo;
},
(void*)asyncCallbackInfo, &asyncCallbackInfo->asyncWork);
napi_queue_async_work(env, asyncCallbackInfo->asyncWork);
return promise;
}
// 模块导出入口函数
static napi_value AppExport(napi_env env, napi_value exports)
{
static napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("getAppName", JSGetAppName),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
return exports;
}
// app模块描述
static napi_module appModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = AppExport,
.nm_modname = "app",
.nm_priv = ((void*)0),
.reserved = {0}
};
// 注册模块
extern "C" __attribute__((constructor)) void AppRegister()
{
napi_module_register(&appModule);
}
同组 BUILD.gn实现:
// BUILD.gn
import("//build/ohos.gni")
ohos_shared_library("app") {
# 指定编译源文件
sources = [
"app.cpp",
]
# 指定编译依赖
deps = [ "//foundation/ace/napi:ace_napi" ]
# 指定库生成的路径
relative_install_dir = "module"
subsystem_name = "ace"
part_name = "napi"
}
在编译后会生成一个动态链接库下载进入后即可从北向与南向完成交互(这是官方的解决方案)。
仔细看代码部分会发现有两个注册部分,一个是参数表一个是接口声明,这与说明文档一致。
// app模块描述
static napi_module appModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = AppExport,
.nm_modname = "app",
.nm_priv = ((void*)0),
.reserved = {0}
};
// 注册模块
extern "C" __attribute__((constructor)) void AppRegister()
{
napi_module_register(&appModule);
}
建立目录,参考本系列第二篇文章中的代码和架构。
|-- BUILD.gn
|-- app.cpp
|-- ohos.build
BUILD.gn:
import("//build/ohos.gni")
ohos_shared_library("myapp") {
# 指定编译源文件
sources = [
"app.cpp",
]
# 指定编译依赖,如果依赖第三方库,需要在此添加
deps = [ "//foundation/ace/napi:ace_napi" ]
# 指定库生成的路径
relative_install_dir = "module"
# 子系统及其组件,后面会引用
subsystem_name = "myapp"
part_name = "myapp_part"
}
ohos.build:
{
"subsystem": "myapp",
"parts": {
"myapp_part": {
"module_list": [
"//myapp:myapp"
],
"test_list": [ ]
}
}
}
#include <assert.h>
#include "napi/native_api.h"
#include "napi/native_node_api.h"
static napi_value Method(napi_env env, napi_callback_info info) {
napi_status status;
napi_value world;
status = napi_create_string_utf8(env, "Hello, world!", 13, &world);
assert(status == napi_ok);
return world;
}
static napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("hello", Method),
};
status = napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
assert(status == napi_ok);
return exports;
}
NAPI_MODULE(myapp, Init)
其他部分参考本系列第二篇文章进行配置,会产生一个新的myapp子系统,该子系统可以完成和北向的单向交互调用。
仔细看这部分c++代码发现并没有参数表和注册,只是调用了一个函数。
NAPI_MODULE(myapp, Init)
但是经过测试这些代码确实能够完成指定任务,这也是让人感觉些许迷惑的地方。
但是两部分除了模块注册之外,函数注册以及函数实现方式都是一致的,并没有明显区别。
具体的实现流程和操作。
首先要了解,NAPI的机制中实现都是异步机制,而异步模型包括两种常用的是callback模型,Promise模型还没有进行过测试。似乎也有一些同步机制的方法,但是没看到有很多讲解。
Promise、Callback 异步模型都是 OHOS 标准异步模型之一。
(以下两个小结摘录自大佬的文章,在官方文档中并没有查到相关的技术手册。)
- ES6原生提供了Promise对象,Promise是异步编程的一种解决方案,可以替代传统的解决方案回调函数和事件。
- promise对象是一个异步操作的结果,提供了一些API使得异步执行可以按照同步的流表示出来,避免了层层嵌套的回调函数,保证了回调是以异步的方式进行调用的。
- 用户在调用这些接口的时候,接口实现将异步执行任务,同时返回一个 Promise 对象,其代表异步操作的结果。
- 在返回的结果的个数超过一个时,其以对象属性的形式返回。
- 对象的状态不受外界影响。
- 一旦状态改变了就不会再变,也就是说任何时候Promise都只有一种状态。
- 用户在调用这些接口的时候,接口实现将异步执行任务。
- 任务执行结果以参数的形式提供给用户注册的回调函数。
- 这些参数的第一个是 Error 或 undefined 类型,分别表示执行出错与正常。
首先napi的实现必须包括两个部分函数,第一个是模块初始化的部分,在其中要进行函数的定义。
static napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("hello", Method),
};
status = napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
assert(status == napi_ok);
return exports;
}
使用:
DECLARE_NAPI_FUNCTION("hello", Method),
参数一是北向使用的js名称,参数二是南向需要实现的函数名称。
其他的部分一般不需要做改变。
另一个部分是具体的函数实现。
static napi_value Method(napi_env env, napi_callback_info info) {
napi_status status;
napi_value world;
status = napi_create_string_utf8(env, "Hello, world!", 13, &world);
assert(status == napi_ok);
return world;
}
以上代码就简单实现了一个赋值helloworld到变量池的操作,在北向调用就可以简介访问到这个变量。
关于异步实现我搬运大佬的代码过来大家一起看看然后保留一下以后学习用,目前还不是很理解,更不要说使用了,菜鸡保命,在之后的研究中心突破了这块会来更新文章。
·```C
static napi_value Get(napi_env env, napi_callback_info info)
{
size_t requireArgc = 1;
size_t argc = 3; //参数个数
napi_value argv[3] = { 0 }; //参数定义
napi_value thisVar = nullptr; //JS对象的this参数
void* data = nullptr; //回调数据指针
/* 根据环境变量获取参数 */
napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
NAPI_ASSERT(env, argc >= requireArgc, "requires 1 parameter");
/* 异步接口上下文,用于接收JS接口传进来的环境变量、参数、回调函数、接口返回值等*/
auto asyncContext = new StorageAsyncContext();
asyncContext->env = env;
for (size_t i = 0; i < argc; i++) {
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[i], &valueType);
if ((i == 0) && (valueType == napi_string)) {
/* 根据JS字符串获取对应的UTF8编码格式的C/C++字符串 */
napi_get_value_string_utf8(env, argv[i], asyncContext->key, KEY_BUFFER_SIZE, &asyncContext->keyLen);
} else if (valueType == napi_string) {
napi_get_value_string_utf8(env, argv[i], asyncContext->value, VALUE_BUFFER_SIZE, &asyncContext->valueLen);
} else if (valueType == napi_function) {
/* 根据JS对象参数argv[i]新建引用 */
napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef);
break;
} else {
NAPI_ASSERT(env, false, "type mismatch");
}
}
napi_value result = nullptr;
if (asyncContext->callbackRef == nullptr) {
/* Promise方式异步调用,创建延迟对象、JS Promise对象,使二者进行关联 */
napi_create_promise(env, &asyncContext->deferred, &result);
} else {
/* Callback方式异步调用,不需要返回Promise对象,返回一个JS未定义值 */
napi_get_undefined(env, &result);
}
/* 根据JS对象获取与之绑定的原生对象实例 */
napi_unwrap(env, thisVar, (void**)&asyncContext->objectInfo);
napi_value resource = nullptr;
napi_create_string_utf8(env, "JSStorageGet", NAPI_AUTO_LENGTH, &resource); //获取JS异步资源名称
/* 创建异步工作 */
napi_create_async_work(
env, nullptr, resource,
/* 执行异步逻辑的原生函数 */
[](napi_env env, void* data) {
StorageAsyncContext* asyncContext = (StorageAsyncContext*)data;
auto itr = g_keyValueStorage.find(asyncContext->key);
if (itr != g_keyValueStorage.end()) {
if (strncpy_s(asyncContext->value, VALUE_BUFFER_SIZE, itr->second.c_str(), itr->second.length()) ==
-1) {
asyncContext->status = 1; //失败
} else {
asyncContext->status = 0; //成功
}
} else {
asyncContext->status = 1; //失败
}
},
/* 异步函数执行完成或者取消后,需要执行的后处理函数 */
[](napi_env env, napi_status status, void* data) {
StorageAsyncContext* asyncContext = (StorageAsyncContext*)data;
napi_value result[2] = { 0 };
if (!asyncContext->status) {
napi_get_undefined(env, &result[0]);
napi_create_string_utf8(env, asyncContext->value, strlen(asyncContext->value), &result[1]);
} else {
napi_value message = nullptr;
napi_create_string_utf8(env, "key does not exist", NAPI_AUTO_LENGTH, &message);
napi_create_error(env, nullptr, message, &result[0]);
napi_get_undefined(env, &result[1]);
asyncContext->objectInfo->Emit(nullptr, "error");
}
if (asyncContext->deferred) {
if (!asyncContext->status) {
/* 异步函数执行成功后,执行成功后处理函数 */
napi_resolve_deferred(env, asyncContext->deferred, result[1]);
} else {
/* 异步函数执行失败后,执行失败后处理函数 */
napi_reject_deferred(env, asyncContext->deferred, result[0]);
}
} else {
napi_value callback = nullptr;
napi_get_reference_value(env, asyncContext->callbackRef, &callback);
napi_call_function(env, nullptr, callback, sizeof(result) / sizeof(result[0]), result, nullptr);
napi_delete_reference(env, asyncContext->callbackRef);
}
/* 异步回调完成后进行资源释放 */
napi_delete_async_work(env, asyncContext->work);
delete asyncContext;
},
/* 用户数据上下文,此数据传递给异步执行函数与后处理函数 */
(void*)asyncContext,
/* 生成的异步工作*/
&asyncContext->work);
napi_queue_async_work(env, asyncContext->work); //异步工作入队列,排队执行
return result;
}
在c->js的过程中,重要的代码如下,主要以回调函数出现。
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
NAPI_ASSERT(env, argc >= 1, "JSCallback Wrong number of arguments"); //参数个数校验
napi_typeof(env, argv[0], &callbackType);
napi_create_reference(env, argv[0], 1, &CallbackRef); //创建引用
napi_get_reference_value(env, CallbackRef, &callback); //根据引用获取回调函数callback
js -> c的过程中,js拉起在南向实现的函数接口,并且南向需要做一次js参数到c参数的转换,重要的代码如下:
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
NAPI_ASSERT(env, argc >= 1, "JSTest Wrong number of arguments"); //参数个数校验
//参数类型校验
napi_valuetype valueType = napi_undefined;
napi_typeof(env, argv[i], &valueType);
napi_get_value_int32(env, argv[i], &cPara1);//参数转换
参考文献:openharmony标准系统L2 JS、eTS 的napi socket 网络接口开发 TCP-开源基础软件社区-51CTO.COM。
主要参考了上面这篇中大佬讲解的一些工作,然后在此基础上做了一些线程增加,在有相关数据回调之后再南向的硬件端除了tcpip协议拉起wifi组网完成信息发送外,也完成比如一些硬件驱动的使用。
原始代码来源于上文中大佬的文章。
/*
* 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 "napi/native_api.h"
#include "napi/native_node_api.h"
#include "utils/log.h"
#include <stdlib.h> // standard library 标准库函数头文件
#include <stdio.h> // standard input output 标准输入输出函数
#include <stdint.h> // 定义了扩展的整数类型和宏
#include <unistd.h> // POSIX 系统 API 访问功能的头文件
#include <fcntl.h> // unix标准中通用的头文件 define O_WRONLY and O_RDONLY
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
static char buf[1024];
static size_t buflen;
static int wflag;
static int st;
static int client_st;
static napi_ref CallbackReff;
static napi_env envs;
void* socketserverthrd(void *ptr)
{
napi_value jsObj, prop1,prop2,prop3, callback = nullptr,undefine = nullptr;
napi_get_reference_value(envs, CallbackReff, &callback);
int port = 18000;
st = socket(AF_INET, SOCK_STREAM, 0);
int opt = SO_REUSEADDR;
setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)
{
HILOG_INFO("test0002 bind failed %s\n", strerror(errno));
return NULL;
}
if (listen(st, 20) == -1)
{
HILOG_INFO("test0002 listen failed %s\n", strerror(errno));
return NULL;
}
HILOG_INFO("test0002 listen success\n");
struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(client_addr));
socklen_t len = sizeof(client_addr);
HILOG_INFO("test0002 waiting for client.......\n");
wflag = 1;
char str[1024];
while(wflag)
{
client_st = accept(st, (struct sockaddr*) &client_addr, &len);
if (client_st == -1)
{
HILOG_INFO("test0002 accept failed %s\n", strerror(errno));
return NULL;
}
HILOG_INFO("test0002 accept by %s\n", inet_ntoa(client_addr.sin_addr));
while (wflag)
{
memset(str, 0, sizeof(str));
int numbytes = recv(client_st, str, sizeof(str), 0);
if (numbytes <= 0)
break;
//if(wflag == 0) break;
strcpy(buf,str);
if((int)str[0] == 170)
{
int cPara1 = (int)str[1];
int cPara2 = (int)str[2];
int cPara3 = (int)str[3];
napi_create_object(envs, &jsObj); //创建JS回调函数对应的参数对象
napi_create_int32(envs, cPara1, &prop1);
napi_create_int32(envs, cPara2, &prop2);
napi_create_int32(envs, cPara3, &prop3);
napi_set_named_property(envs, jsObj, "prop1", prop1); //设置JS参数对象属性值
napi_set_named_property(envs, jsObj, "prop2", prop2);
napi_set_named_property(envs, jsObj, "prop3", prop3);
napi_call_function(envs, nullptr, callback, 1, &jsObj, &undefine); //使用生成的JS参数,调用对应的JS回调函数
}
buflen = strlen(str);
//send(client_st, str, strlen(str), 0);
}
}
return NULL;
}
//启动
static napi_value ServerStart(napi_env env, napi_callback_info info)
{
size_t argc = 1; //参数个数定义
napi_value argv[argc];
napi_value thisVar = nullptr;
void *data = nullptr;
envs = env;
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
NAPI_ASSERT(env, argc >= 1, "JSCallback Wrong number of arguments"); //参数个数校验
napi_valuetype callbackType = napi_undefined;
napi_typeof(env, argv[0], &callbackType);
NAPI_ASSERT(env, callbackType == napi_function, "parameter 1 type mismatch"); //参数类型校验,传进来的须是函数类型
napi_create_reference(env, argv[0], 1, &CallbackReff); //创建引用
//napi_get_reference_value(env, CallbackRef, &callback); //根据引用获取回调函数callback
pthread_t thrd;
HILOG_INFO("test0002 thrs start!");
//pthread_create(&thrd1, NULL, recvsocket, &client_st);
pthread_create(&thrd, NULL, socketserverthrd,NULL);
HILOG_INFO("test0002 end!");
napi_value result = nullptr;
napi_get_undefined(env, &result);
return result;
}
//停止
static napi_value ServerStop(napi_env env, napi_callback_info info)
{
close(st);
wflag = 0;
napi_value result = nullptr;
napi_get_undefined(env, &result);
return result;
//发送
static napi_value ServerWrite(napi_env env, napi_callback_info info)
{
size_t requireArgc = 3;
size_t argc = 3;
napi_value args[3] = { nullptr };
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");
napi_valuetype valuetype0;
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
napi_valuetype valuetype1;
NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
napi_valuetype valuetype2;
NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
NAPI_ASSERT(env, valuetype0 == napi_number && valuetype1 == napi_number && valuetype2 == napi_number, "Wrong argument type. Numbers expected.");
char str[4];
uint32_t a,b,c;
NAPI_CALL(env, napi_get_value_uint32(env, args[0], &a));
NAPI_CALL(env, napi_get_value_uint32(env, args[1], &b));
NAPI_CALL(env, napi_get_value_uint32(env, args[2], &c));
str[0] = (char)0xAA;
str[1] = (char)a;
str[2] = (char)b;
str[3] = (char)c;
if (-1 == write(client_st, str,4)){
HILOG_INFO("test0002 okok servertest error");
}
napi_value result = nullptr;
napi_get_undefined(env, &result);
return result;
}
EXTERN_C_START
/*
* function for module exports
*/
static napi_value Init(napi_env env, napi_value exports)
{
/*
* Properties define
*/
napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("ServerStart", ServerStart),
DECLARE_NAPI_FUNCTION("ServerStop", ServerStop),
DECLARE_NAPI_FUNCTION("ServerWrite", ServerWrite)
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
return exports;
}
EXTERN_C_END
/*
* Module define
*/
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "socketserver",
.nm_priv = ((void*)0),
.reserved = { 0 },
};
/*
* Module register function
*/
extern "C" __attribute__((constructor)) void RegisterModule(void)
{
napi_module_register(&demoModule);
}
使用的是上文中方案二提到的napi注册方案完成了有关的注册,并且构造了三个北向可以调用的接口函数,完成tcp ip的读写操作。
由于篇幅限制,另一版本完成的线程增加就不放代码了,有需要可留言联系。
分析一下代码中的线程拉起。
pthread_create(&thrd, NULL, socketserverthrd,NULL);
使用该函数创建了一个新的线程完成tcp servo的工作,但是在这个工作之前已经写好了有关的头文件和一些变量。
#include <pthread.h>
在我们所设定的项目中,tcp也是基于这部分的servo和client完成的数据交换。
在这部分代码中需要更改和注意的地方是port以及地址参数需要修正,设定值和代码中的值应当不同。
如果按照文中描述的方法把模块写在了ace的demo模组中编译时的参数一定不能写错,要使用这个参数才能成功编译。
./build.sh --product-name Hi3516DV300 --build-target make_test

TCPIP的协议帧组成和协议解析需要专门的库和函数处理,且使用的是16进制码,需要注意的是需要提前对所传输的数据进行处理,传出后也需要做相关的解析。
南北向接口的互相实现是openharmony程序中极为重要的一部分,在这部分当中既要启动底层驱动又要处理好留给北向的接口协议,对于整体的系统结构和代码解耦有比较大的要求,目前有些第三方库在调取extern_depend时无效,且没有给出说明,在napi子系统中启动hilog一直无法成功,有待解决后会补充一篇文章。