diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/.gitignore b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/AppScope/app.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..9593bcb4fd7f1f673c583a3f182c94b1cfd63cc8 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "bundleName": "com.samples.http_case", + "vendor": "samples", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/AppScope/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..ef41077141739282d67f6a2016357947c3b6b795 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "HTTP_case" + } + ] +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/AppScope/resources/base/media/app_icon.png b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/AppScope/resources/base/media/app_icon.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/README_zh.md b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..a5cbc24249410f95c0e4045cb2121a0dd8577bfe --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/README_zh.md @@ -0,0 +1,105 @@ +# Http_case(网络共享) + +### 介绍 + +应用通过HTTP发起一个数据请求,支持常见的GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT方法。本项目的构建依据[HTTP数据请求](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/network/http-request.md)示例代码,构建了一个HTTP数据请求的示例应用,它实现了通过按钮实现request接口开发步骤、requestInStream接口开发步骤的功能,使用了[@ohos.net.http](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/reference/apis-network-kit/js-apis-net-connection.md)接口。 + +**注意:**本示例需要用户输入一个实际的URL,方能获得通过的结果。 + +### 效果预览 + +| 程序启动 | 发送HTTP请求 | 发送流式HTTP连接 | +| ----------------------------------------- | ------------------------------------------- | ----------------------------------------------------- | +| | | | + + +使用说明 + +1. 前置条件:需要用户在 + + ``` + const TARGET_URL: string = ''; + ``` + + 这个位置的单引号之间输入一个实际URL(参考格式:https://www.example.com/) + +2. 点击 "普通 HTTP 请求" 按钮,发送一个 GET 请求到 TARGET_URL 并显示测试结果。 + +3. 点击 "流式 HTTP 请求" 按钮,发送一个 POST 请求到TARGET_URL,接收流式数据并显示测试结果。 + + 注1:日志输出使用 `Logger` 进行调试,可以查看 HTTP 请求的详细信息,包括请求头、响应数据、状态码等。 + + 注2:公共网址的访问不需要证书,故注释了代码中的证书部分 + +### 工程目录 + +``` +entry/src/main/ets/ +|---common +| |---Logger.ets // 日志工具 +|---entryability +| │---EntryAbility.ets +|---entrybackupability +│ |---EntryBackupAbility.ets +|---pages +│ |---Index.ets // 主页 +``` + +### 具体实现 + +1. **普通 HTTP 请求 (`sendHttpRequest`)** + - 使用 `http.createHttp()` 创建一个 HTTP 请求对象 `httpRequest`。 + - 发起 GET 请求,并设置请求头为 `Content-Type: application/json`。 + - 配置请求的超时参数:连接超时(`connectTimeout`)和读取超时(`readTimeout`)均设置为 60 秒。 + - 设置 `expectDataType: http.HttpDataType.STRING`,期望服务器返回的数据是字符串类型。 + - 配置使用缓存(`usingCache: true`)和 HTTP 协议版本为 HTTP/1.1(`usingProtocol: http.HttpProtocol.HTTP1_1`)。 + - 监听 HTTP 响应头事件(`headersReceive`),日志记录请求返回的 header 信息。 + - 在请求完成时,成功响应会将结果展示在 UI 中 + - 请求完成后销毁 `httpRequest` 实例,确保资源释放。 +2. **流式 HTTP 请求 (`sendStreamHttpRequest`)** + - 使用 `http.createHttp()` 创建一个 HTTP 请求对象 `httpRequest`,并初始化一个空的 `ArrayBuffer`(`res`)来接收流式响应数据。 + - 订阅 `headersReceive` 事件,输出响应头信息。 + - 订阅 `dataReceive` 事件,每次接收到流数据时,更新 `res`(即通过拼接 `ArrayBuffer`)以逐步存储响应内容,并记录当前接收的字节数。 + - 订阅 `dataEnd` 事件,指示流数据接收完毕,输出日志标明没有更多数据。 + - 订阅 `dataReceiveProgress` 事件,输出接收进度(已接收的数据大小和总大小)。 + - 通过 `httpRequest.requestInStream` 发送一个流式请求(POST 请求),请求体内容为 `"data to send"`。 + - 完成请求后,取消对各个事件的订阅,并销毁 `httpRequest` 实例。 +3. **HTTP 请求参数配置** + - **请求方法**:普通请求使用 `http.RequestMethod.GET`,流式请求使用 `http.RequestMethod.POST`。 + - **请求头**:两种请求都设置了 `Content-Type: application/json`,说明请求体为 JSON 数据。 + - **超时设置**:连接和读取超时均为 60 秒,防止请求长时间挂起。 + - **协议类型**:请求使用 `http.HttpProtocol.HTTP1_1`,即 HTTP 1.1 协议。 + - **缓存使用**:使用缓存(`usingCache: true`),减少重复请求时的延迟。 +4. **异常处理与状态更新** + - **普通 HTTP 请求异常处理**:在请求失败时,`err` 会被捕获,并将 UI 显示为“失败”,同时在日志中记录错误信息。 + - **流式请求异常处理**:在流式请求中,使用 `catch` 捕获错误并更新 `testResult_002` 为“失败”,并输出详细错误信息。 +5. **资源释放与请求销毁** + - 每次请求结束后,无论是普通请求还是流式请求,都调用 `httpRequest.destroy()` 销毁请求对象,释放相关资源。 + - 在流式请求中,响应数据的接收进度、完成和错误事件都通过订阅事件处理,确保最终销毁请求对象,防止内存泄漏。 + +### 相关权限 + +[ohos.permission.INTERNET](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-all.md#ohospermissioninternet) + +### 依赖 + +不涉及。 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:华为手机。 +2. HarmonyOS系统:HarmonyOS 5.0.2 Release及以上。 +3. HarmonyOS SDK版本:HarmonyOS 5.0.2 Release及以上。 +4. 本示例需要使用DevEco Studio Release(5.0.5.306)及以上版本才可编译运行。 + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/ > .git/info/sparse-checkout +git remote add origin https://gitee.com/harmonyos_samples/guide-snippets.git +git pull origin master +``` \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/build-profile.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ed831f1db622c015dcc8fd8326adc7b9ff1ce4fc --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/build-profile.json5 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.2(14)", + "targetSdkVersion": "5.0.2(14)", + "runtimeOS": "HarmonyOS" + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/.gitignore b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/build-profile.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..94fd644fb4f5957c6a5d0d689f9cf664ee984212 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/build-profile.json5 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default", + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/hvigorfile.ts b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..7128d1d0c15753e77eafb1e6cfd3b730ecac0561 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/obfuscation-rules.txt b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..69c4d6a8a5531548e4886fa766090c5c157a87d9 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/oh-package.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c9cb6c8174858277c9b0d465a51547dcab16d5ff --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/common/CommonConstant.ets b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/common/CommonConstant.ets new file mode 100644 index 0000000000000000000000000000000000000000..2db14185b25fa9851ae6d1f9b4a1aa46324a2a62 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/common/CommonConstant.ets @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 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. + */ + +export enum ComponentId { + NORMAL_RESULT_ID = 'normalReqResult', + STREAM_RESULT_ID = 'streamReqResult', + HTTP_BUTTON = 'HttpButton', + STREAM_HTTP_BUTTON = 'StreamHttpButton', + HTTP_TITLE = 'HttpTitleId' +}; \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/common/Logger.ets b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/common/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..33c8b8c7434ba7ceb292d430d15e72a6bfd9ad7d --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/common/Logger.ets @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; + +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0x0001; // 此处应该使用业务方自己的domain,此处使用0x0001作为示例 + } + + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } + + fatal(...args: string[]) { + hilog.fatal(this.domain, this.prefix, this.format, args); + } + + isLoggable(level: number) { + hilog.isLoggable(this.domain, this.prefix, level); + } +} + +export default new Logger('[Sample_HTTP_CASE]'); diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/entryability/EntryAbility.ets b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..c595cb8cba1a651570b3afa0e920f2be1bdf35e4 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 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. + */ + +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; +import Logger from '../common/Logger'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + Logger.info('Ability onCreate'); + } + + onDestroy(): void { + Logger.info('Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + Logger.info('Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + Logger.error(`Failed to load the content. Cause: ${JSON.stringify(err)}`); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + Logger.info('Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + Logger.info('Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + Logger.info('Ability onBackground'); + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..f1047bd4d077992ee5ef3098291ecd3ada054198 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; +import Logger from '../common/Logger'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + Logger.info('onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + Logger.info(`onRestore ok ${JSON.stringify(bundleVersion)}`); + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/pages/Index.ets b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..5960636a134f2a48ececb6f0ed40ce10857abe8f --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2025 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. + */ + +import { http } from '@kit.NetworkKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import Logger from '../common/Logger'; +import { ComponentId } from '../common/CommonConstant'; +import { promptAction } from '@kit.ArkUI'; + +// 常量定义 +const LOG_TAG: string = '[Sample_HttpRequestDemo]'; +const TARGET_URL: string = ''; + +@Entry +@Component +struct HttpRequestDemo { + @State normalReqResult: ResourceStr = ''; + @State streamReqResult: ResourceStr = ''; + + build() { + Column() { + Text($r('app.string.HTTP_Request_Example')) + .fontSize($r('app.float.size20')) + .id(ComponentId.HTTP_TITLE) + .margin({ bottom: $r('app.float.margin20') }) + + // 第一个按钮:普通 HTTP 请求 + Button($r('app.string.HTTP_Request_Button')) + .onClick(() => { + this.sendHttpRequest(); + }) + .width('80%') + .height($r('app.float.height50')) + .margin({ top: $r('app.float.margin20') }) + .backgroundColor(Color.Blue) + .fontColor(Color.White) + .fontSize($r('app.float.size20')) + .borderRadius($r('app.float.borderRadius')) + .id(ComponentId.HTTP_BUTTON) + + // 第二个按钮:流式 HTTP 请求 + Button($r('app.string.Stream_HTTP_Request_Button')) + .onClick(() => { + this.sendStreamHttpRequest(); + }) + .width('80%') + .height($r('app.float.height50')) + .margin({ top: $r('app.float.margin20') }) + .backgroundColor(Color.Green) + .fontColor(Color.White) + .fontSize($r('app.float.size20')) + .borderRadius($r('app.float.borderRadius')) + .id(ComponentId.STREAM_HTTP_BUTTON) + } + .width('100%') + .height('100%') + .justifyContent(FlexAlign.Center) + } + + // [Start request_interface_development] + // 封装普通的 HTTP 请求逻辑 + private sendHttpRequest(): void { + this.normalReqResult = $r('app.string.testing'); + let httpRequest = http.createHttp(); + + // 订阅HTTP响应头 + httpRequest.on('headersReceive', (header) => { + Logger.info(`${LOG_TAG} header: ${JSON.stringify(header)}`); + }); + + httpRequest.request( + // 填写HTTP请求的URL地址,可以带参数也可以不带参数。URL地址需要开发者自定义。请求的参数可以在extraData中指定 + TARGET_URL, + { + method: http.RequestMethod.POST, // 可选,默认为http.RequestMethod.GET + // 开发者根据自身业务需要添加header字段 + header: { + 'contentType': 'application/json' + }, + // 当使用POST请求时此字段用于传递请求体内容,具体格式与服务端协商确定 + extraData: 'data to send', + expectDataType: http.HttpDataType.STRING, // 可选,指定返回数据的类型 + usingCache: true, // 可选,默认为true + priority: 1, // 可选,默认为1 + connectTimeout: 60000, // 可选,默认为60000ms + readTimeout: 60000, // 可选,默认为60000ms + usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定 + usingProxy: false, // 可选,默认不使用网络代理,自API 10开始支持该属性 + // caPath:'/path/to/cacert.pem', // 可选,默认使用系统预制证书,自API 10开始支持该属性 + // clientCert: { // 可选,默认不使用客户端证书,自API 11开始支持该属性 + // certPath: '/path/to/client.pem', // 默认不使用客户端证书,自API 11开始支持该属性 + // keyPath: '/path/to/client.key', // 若证书包含Key信息,传入空字符串,自API 11开始支持该属性 + // certType: http.CertType.PEM, // 可选,默认使用PEM,自API 11开始支持该属性 + // keyPassword: "passwordToKey" // 可选,输入key文件的密码,自API 11开始支持该属性 + // }, + multiFormDataList: [ // 可选,仅当Header中,'content-Type'为'multipart/form-data'时生效,自API 11开始支持该属性 + { + name: 'Part1', // 数据名,自API 11开始支持该属性 + contentType: 'text/plain', // 数据类型,自API 11开始支持该属性 + data: 'Example data', // 可选,数据内容,自API 11开始支持该属性 + remoteFileName: 'example.txt' // 可选,自API 11开始支持该属性 + }, { + name: 'Part2', // 数据名,自API 11开始支持该属性 + contentType: 'text/plain', // 数据类型,自API 11开始支持该属性 + // data/app/el2/100/base/com.example.myapplication/haps/entry/files/fileName.txt + filePath: `${getContext(this).filesDir}/fileName.txt`, // 可选,传入文件路径,自API 11开始支持该属性 + remoteFileName: 'fileName.txt' // 可选,自API 11开始支持该属性 + } + ] + }, (err: BusinessError, data: http.HttpResponse) => { + if (!err) { + // 请求成功,处理响应结果 + this.normalReqResult = $r('app.string.httpSendSuccess'); + promptAction.showToast({ + message: this.normalReqResult, + duration: 4000, // 持续时间 + bottom: 300 // 与底间隔 + }); + Logger.info(`${LOG_TAG} Result: ${JSON.stringify(data.result)}`); + Logger.info(`${LOG_TAG} code: ${JSON.stringify(data.responseCode)}`); + Logger.info(`${LOG_TAG} header: ${JSON.stringify(data.header)}`); + Logger.info(`${LOG_TAG} cookies: ${JSON.stringify(data.cookies)}`); + + httpRequest.destroy(); + } else { + this.normalReqResult = 'error:' + JSON.stringify(err); + promptAction.showToast({ + message: this.normalReqResult, + duration: 4000, // 持续时间 + bottom: 300 // 与底间隔 + }); + // 请求失败,处理错误 + Logger.error(`${LOG_TAG} error: ${JSON.stringify(err)}`); + // 取消订阅HTTP响应头事件 + httpRequest.off('headersReceive'); + + // 当该请求使用完毕时,调用destroy方法主动销毁 + httpRequest.destroy(); + } + } + ); + } + // [End request_interface_development] + + // [Start request_in_stream_interface_development] + // 封装流式 HTTP 请求逻辑 + private sendStreamHttpRequest(): void { + this.streamReqResult = $r('app.string.testing'); + let httpRequest = http.createHttp(); + let res = new ArrayBuffer(0); + + // 订阅HTTP响应头事件 + httpRequest.on('headersReceive', (header: Object) => { + Logger.info(`${LOG_TAG} header: ${JSON.stringify(header)}`); + }); + + // 订阅HTTP流式响应数据接收事件 + httpRequest.on('dataReceive', (data: ArrayBuffer) => { + const newRes = new ArrayBuffer(res.byteLength + data.byteLength); + const resView = new Uint8Array(newRes); + resView.set(new Uint8Array(res)); + resView.set(new Uint8Array(data), res.byteLength); + res = newRes; + Logger.info(`${LOG_TAG} res length: ${res.byteLength}`); + }); + + // 订阅HTTP流式响应数据接收完毕事件 + httpRequest.on('dataEnd', () => { + Logger.info(`${LOG_TAG} No more data in response, data receive end`); + }); + + class Data { + public receiveSize: number = 0; + public totalSize: number = 0; + } + + // 订阅HTTP流式响应数据接收进度事见 + httpRequest.on('dataReceiveProgress', (data: Data) => { + Logger.info(`${LOG_TAG} dataReceiveProgress receiveSize: ${data.receiveSize},totalSize: ${data.totalSize}`); + }); + + let streamInfo: http.HttpRequestOptions = { + method: http.RequestMethod.POST, // 使用POST请求 + header: { + 'contentType': 'application/json' + }, + extraData: 'data to send', // 请求体内容 + expectDataType: http.HttpDataType.STRING, // 指定返回数据的类型 + usingCache: true, // 使用缓存 + priority: 1, // 请求优先级 + connectTimeout: 60000, // 连接超时 + readTimeout: 60000, // 读取超时 + usingProtocol: http.HttpProtocol.HTTP1_1 // 协议类型 + }; + + // 发送流式请求 + httpRequest.requestInStream(TARGET_URL, streamInfo) + .then((data: number) => { + this.streamReqResult = $r('app.string.testSuccess'); // 测试通过 + promptAction.showToast({ + message: this.streamReqResult, + duration: 4000, // 持续时间 + bottom: 300 // 与底间隔 + }); + Logger.info(`${LOG_TAG} requestInStream OK!`); + Logger.info(`${LOG_TAG} ResponseCode : ${JSON.stringify(data)}`); + + // 取消订阅事件 + httpRequest.off('headersReceive'); + httpRequest.off('dataReceive'); + httpRequest.off('dataReceiveProgress'); + httpRequest.off('dataEnd'); + }) + .catch((err: Error) => { + this.streamReqResult = 'error:' + JSON.stringify(err); // 测试失败 + promptAction.showToast({ + message: this.streamReqResult, + duration: 4000, // 持续时间 + bottom: 300 // 与底间隔 + }); + Logger.error(`${LOG_TAG} requestInStream ERROR : err = ${JSON.stringify(err)}`); + }) + .finally(() => { + // 销毁请求对象 + httpRequest.destroy(); + }); + } + // [End request_in_stream_interface_development] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/module.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..9f8ba45ee9bf3e6c7f6e8bdc9e253966db28e87f --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/module.json5 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.INTERNET", + "reason": "$string:grant_internet", + "usedScene": { + "abilities": [ + "MainAbility" + ], + "when": "inuse" + } + } + ] + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/element/color.json b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/element/float.json b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/element/float.json new file mode 100644 index 0000000000000000000000000000000000000000..ff5e75398a8f17b6d4574c76ee2f123fe31b1c4f --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/element/float.json @@ -0,0 +1,32 @@ +{ + "float": [ + { + "name": "margin20", + "value": "20" + }, + { + "name": "margin18", + "value": "18" + }, + { + "name": "margin10", + "value": "10" + }, + { + "name": "size18", + "value": "18" + }, + { + "name": "size20", + "value": "20" + }, + { + "name": "height50", + "value": "50" + }, + { + "name": "borderRadius", + "value": "5" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..928fa3fb37b7b646a004828c83c087c2433db777 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/element/string.json @@ -0,0 +1,44 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "HTTP_case" + }, + { + "name": "HTTP_Request_Button", + "value": "HTTP_Request_Button" + }, + { + "name": "HTTP_Request_Example", + "value": "HTTP_Request" + }, + { + "name": "Stream_HTTP_Request_Button", + "value": "Stream_HTTP_Request_Button" + }, + { + "name": "testSuccess", + "value": "Success" + }, + { + "name": "testFail", + "value": "Failure" + }, + { + "name": "testing", + "value": "Testing" + }, + { + "name": "httpSendSuccess", + "value": "httpSendSuccess" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/media/background.png b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/media/background.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/media/foreground.png b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/media/foreground.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/media/layered_image.json b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/media/startIcon.png b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/media/startIcon.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/profile/backup_config.json b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/profile/main_pages.json b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/en_US/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..61754923b63889aa0b145d172b2100e4d78ce611 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,48 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "HTTP_case" + }, + { + "name": "HTTP_Request_Button", + "value": "HTTP_Request_Button" + }, + { + "name": "HTTP_Request_Example", + "value": "HTTP_Request" + }, + { + "name": "Stream_HTTP_Request_Button", + "value": "Stream_HTTP_Request_Button" + }, + { + "name": "testSuccess", + "value": "httpFlowSendSuccess" + }, + { + "name": "testFail", + "value": "Failure" + }, + { + "name": "testing", + "value": "Testing" + }, + { + "name": "httpSendSuccess", + "value": "httpSendSuccess" + }, + { + "name": "grant_internet", + "value": "grant_internet" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/zh_CN/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..e8ed9dd362d7b2bdbaca625aa53088ebc60dce83 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,48 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "HTTP_case" + }, + { + "name": "HTTP_Request_Button", + "value": "发送HTTP请求" + }, + { + "name": "HTTP_Request_Example", + "value": "HTTP请求示例" + }, + { + "name": "Stream_HTTP_Request_Button", + "value": "发送流式HTTP请求" + }, + { + "name": "testSuccess", + "value": "发送流式HTTP请求成功" + }, + { + "name": "testFail", + "value": "失败" + }, + { + "name": "testing", + "value": "测试中" + }, + { + "name": "httpSendSuccess", + "value": "发送HTTP请求成功" + }, + { + "name": "grant_internet", + "value": "网络使用权限" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/ohosTest/ets/test/Ability.test.ets b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..14964bc9657f0360cf87693d4ac4ff87c2165798 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2025 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. + */ + +import { describe, it } from '@ohos/hypium'; +import { abilityDelegatorRegistry, Driver, ON } from '@kit.TestKit'; +import { Want } from '@kit.AbilityKit'; +import { ComponentId } from '../../../main/ets/common/CommonConstant'; +import Logger from '../../../main/ets/common/Logger'; + +const delegator = abilityDelegatorRegistry.getAbilityDelegator(); + +const BUNDLE = 'HTTPCASE_'; +const BUNDLE_NAME = 'com.samples.http_case'; +const ABILITY_NAME: string = 'EntryAbility'; +const DELAY_MS = 3000; +let driver: Driver = Driver.create(); + +export default function AbilityTest() { + describe('AbilityTest', () => { + /** + * @tc.number StartAbility_001 + * @tc.name StartAbility_001 + * @tc.desc 启动Ability + */ + it(BUNDLE + 'StartAbility_001', 0, async (done: Function) => { + Logger.info(`${BUNDLE}StartAbility_001 begin`); + const want: Want = { + bundleName: BUNDLE_NAME, + abilityName: ABILITY_NAME + }; + await delegator.startAbility(want); + await driver.assertComponentExist(ON.id(ComponentId.HTTP_TITLE)); + await driver.delayMs(DELAY_MS); + done(); + Logger.info(`${BUNDLE}StartAbility_001 end`); + }) + + /** + * @tc.number Http_case_001 + * @tc.name Http_case_001 + * @tc.desc 测试 发送HTTP请求 按钮 + */ + it(BUNDLE + 'Http_case_001', 0, async (done: Function) => { + Logger.info(`${BUNDLE_NAME} Http_case_001 begin`); + await driver.delayMs(DELAY_MS); + await driver.assertComponentExist(ON.id(ComponentId.HTTP_BUTTON)); + let stack = await driver.findComponent(ON.id(ComponentId.HTTP_BUTTON)); + await stack.click(); + await driver.delayMs(DELAY_MS); + Logger.info(`${BUNDLE_NAME}Http_case_001 end`); + done(); + }); + + /** + * @tc.number Http_case_002 + * @tc.name Http_case_002 + * @tc.desc 测试 发送流式 HTTP 请求 按钮 + */ + it(BUNDLE + 'Http_case_002', 0, async (done: Function) => { + Logger.info(`${BUNDLE_NAME}Http_case_002 begin`); + await driver.delayMs(DELAY_MS); + await driver.assertComponentExist(ON.id(ComponentId.STREAM_HTTP_BUTTON)); + let stack = await driver.findComponent(ON.id(ComponentId.STREAM_HTTP_BUTTON)); + await stack.click(); + await driver.delayMs(DELAY_MS); + Logger.info(`${BUNDLE_NAME}Http_case_002 end`); + done(); + }) + }) +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/ohosTest/ets/test/List.test.ets b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..82bf8f6d76edc305cb895fc09c63a5c6eaadfba7 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ + +import AbilityTest from './Ability.test'; + +export default function testsuite() { + AbilityTest(); +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/ohosTest/module.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c3fd9dda3040d888d9d8b0b62bcb5d3b6fbeb614 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/hvigor/hvigor-config.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..719070152cb19fc7e009202046a3cf462e4f2a4b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/hvigor/hvigor-config.json5 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + "stacktrace": true /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/hvigorfile.ts b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/oh-package.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..864e638bc7be95d743d488823c0ebf7e6568cff8 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/oh-package.json5 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "description": "Please describe the basic information.", + + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.18", + "@ohos/hamock": "1.0.0" + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/ohosTest.md b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..be91abd0289c6296765f4841e307626ef3c284f7 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/ohosTest.md @@ -0,0 +1,8 @@ +# 测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +| -------------------- | ------------ | ---- | ---------------------------------------- | -------- | -------- | +| 发送HTTP请求按钮 | 设备正常运行 | | 发送HTTP请求并成功响应 | 是 | pass | +| 发送流式HTTP请求按钮 | 设备正常运行 | | 成功发送流式HTTP请求,并进行流式响应测试 | 是 | pass | diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/screenshots/Program_Startup.jpg b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/screenshots/Program_Startup.jpg new file mode 100644 index 0000000000000000000000000000000000000000..897690ae5052accab50c0ff19c1f9f470c2e5954 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/screenshots/Program_Startup.jpg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/screenshots/Send_HTTP_Request.jpg b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/screenshots/Send_HTTP_Request.jpg new file mode 100644 index 0000000000000000000000000000000000000000..71189ec5a81da16d6c348f28ef0b88fd28824a61 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/screenshots/Send_HTTP_Request.jpg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/screenshots/Send_Streaming_HTTP_Request.jpg b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/screenshots/Send_Streaming_HTTP_Request.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4187b79cec913bdedbaa39e5239cc2fe0acf86ce Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/screenshots/Send_Streaming_HTTP_Request.jpg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/.gitignore b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/AppScope/app.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c096444809a9de3bcd615d3c161c396eed53b348 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "bundleName": "com.samples.mdns_case", + "vendor": "samples", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/AppScope/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..1245c067a76e37e04da96f9c720401cac85e8bdd --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "MDNS_case" + } + ] +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/AppScope/resources/base/media/app_icon.png b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/AppScope/resources/base/media/app_icon.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/README_zh.md b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..63674b492eb8147bceb2fc90664c96312df80178 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/README_zh.md @@ -0,0 +1,120 @@ +# MDNS管理 + +### 介绍 + +本项目的构建依据[MDNS](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/network/net-mdns.md)示例代码,MDNS即多播DNS(Multicast DNS),提供局域网内的本地服务添加、移除、发现、解析等能力。 + +- 本地服务:局域网内服务的提供方,比如打印机、扫描器等。 + +MDNS管理的典型场景有: + +- 管理本地服务,通过对本地服务的创建,删除和解析等管理本地服务。 +- 发现本地服务,通过DiscoveryService对象,对指定类型的本地服务状态变化进行监听。 + +使用了[@ohos.net.mdns](https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/reference/apis-network-kit/js-apis-net-connection.md)接口。 + +本应用支持在同一设备或不同设备的局域网环境中运行。在单一设备中,客户端可以注册本地服务,同时作为服务端发现局域网中的服务;在多设备环境下,一台设备充当客户端注册服务,另一台设备充当服务端进行服务发现。 + +### 效果预览 + +| 启动mDNS服务发现 | 添加本地服务 | 解析本地服务 | 移除本地服务 | +| -------------------------------------------------------- | --------------------------------------------- | ------------------------------------------------- | ------------------------------------------------ | +| ![image](./screenshots/Start_mDNS_Service_Discovery.jpg) | ![image](./screenshots/Add_Local_Service.jpg) | ![image](./screenshots/Resolve_Local_Service.jpg) | ![image](./screenshots/Remove_Local_Service.jpg) | + +使用说明 + +1. 启动项目后,使用以下功能按钮: + - **启动 mDNS 服务发现**:开始发现局域网内的服务。 + - **添加本地服务**:注册一个服务到局域网。 + - **解析本地服务**:解析并查看服务详细信息。 + - **移除本地服务**:取消注册本地服务。 + - **停止 mDNS 服务发现**:停止发现操作。 + + + + + +### 工程目录 + +``` +entry/src/main/ets/ +|---common +| |---ComponentId.ets // 标识特定的服务状态或操作按钮 +| |---Logger.ets // 日志工具 +| |---ServiceOperateStatus.ets // 标识特定的服务操作状态 +|---entryability +| │---EntryAbility.ets +|---entrybackupability +│ |---EntryBackupAbility.ets +|---pages +│ |---Index.ets // 主页 +``` + +### 具体实现 + +1. **启动 mDNS 服务发现** + + - 使用 `mdns.createDiscoveryService(context, serviceType)` 创建一个 mDNS 服务发现对象 `discoveryService`。 + - 监听以下事件: + - **`discoveryStart`**:服务发现启动事件,记录日志并输出服务发现已启动的信息。 + - **`discoveryStop`**:服务发现停止事件,记录日志并输出服务发现已停止的信息。 + - **`serviceFound`**:当发现新服务时触发,调用 `onServiceFound` 方法,解析服务并将信息添加到设备列表中。 + - **`serviceLost`**:当服务丢失时触发,调用 `onServiceLost` 方法,从设备列表中移除该服务。 + - 调用 `discoveryService.startSearchingMDNS()` 启动服务发现,若成功,更新 `discoveryStatus` 为 "已启动"。 + +2. **停止 mDNS 服务发现** + + - 使用 `discoveryService.stopSearchingMDNS()` 停止 mDNS 服务发现。 + - 停止后,移除以下事件监听: + - **`discoveryStart`** + - **`discoveryStop`** + - **`serviceFound`** + - **`serviceLost`** + - 更新 `discoveryStatus` 为 "已停止",确保 UI 显示正确状态。 + +3. **添加本地服务 (`addLocalService`)** + + - 调用 `mdns.addLocalService(context, localServiceInfo)` 注册本地服务。 + - 在服务添加成功时,更新状态为 "添加成功" 并记录成功日志信息。 + - 在服务添加失败时,捕获 `BusinessError`,更新状态为 "添加失败" 并记录错误日志。 + +4. **解析本地服务 (`resolveLocalService`)** + + - 调用 `mdns.resolveLocalService(context, localServiceInfo)` 解析指定服务信息。 + - 成功解析后,更新状态为 "解析成功",并将解析到的服务信息添加到设备列表中。 + - 如果解析失败,捕获异常,更新状态为 "解析失败",并输出详细错误信息。 + +5. **移除本地服务 (`removeLocalService`)** + + - 调用 `mdns.removeLocalService(context, localServiceInfo)` 取消注册本地服务。 + - 成功移除时,更新状态为 "移除成功",记录日志并确认操作完成。 + - 移除失败时,捕获异常,更新状态为 "移除失败",同时记录错误信息。 + + + +### 相关权限 + +无 + +### 依赖 + +不涉及。 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:华为手机。 +2. HarmonyOS系统:HarmonyOS 5.0.2 Release及以上。 +3. HarmonyOS SDK版本:HarmonyOS 5.0.2 Release及以上。 +4. 本示例需要使用DevEco Studio Release(5.0.5.306)及以上版本才可编译运行。 + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo NetWork_Kit/NetWorkKit_Datatransmission/HTTP_case/ > .git/info/sparse-checkout +git remote add origin https://gitee.com/harmonyos_samples/guide-snippets.git +git pull origin master +``` \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/build-profile.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..445b20c1cb403eae2c74682a09d133499130294c --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/build-profile.json5 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.2(14)", + "targetSdkVersion": "5.0.2(14)", + "runtimeOS": "HarmonyOS" + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ], + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/.gitignore b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/build-profile.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..eed6635f664fa08b992d84590bf62be7e0bb5381 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/build-profile.json5 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/hvigorfile.ts b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4f43d54667f8327c367c8096bd08bb8c75aff54 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/obfuscation-rules.txt b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..69c4d6a8a5531548e4886fa766090c5c157a87d9 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/oh-package.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c9cb6c8174858277c9b0d465a51547dcab16d5ff --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/common/ComponentId.ets b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/common/ComponentId.ets new file mode 100644 index 0000000000000000000000000000000000000000..6dea2cbdadf8655bb818367dfc4b5bc05f6ec331 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/common/ComponentId.ets @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ + +export enum ComponentId { + SERVICE_ADD_STATUS = 'addService', + SERVICE_REMOVE_STATUS = 'removeService', + SERVICE_RESOLVE_STATUS = 'resolveService', + SERVICE_DISCOVERY_STATUS = 'discoveryService', + ADD_SERVICE_BTN = 'addLocal', + RESOLVE_SERVICE_BTN = 'resolveLocal', + REMOVE_SERVICE_BTN = 'removeLocal', + START_DISCOVERY_SERVICE_BTN = 'startDiscovery', + STOP_DISCOVERY_SERVICE_BTN = 'stopDiscovery' +}; \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/common/Logger.ets b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/common/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..3b898183aa87f3e6a6ea3ff56bec9b49fb8b18a4 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/common/Logger.ets @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from "@kit.PerformanceAnalysisKit"; + +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0x0001; // 示例域 + } + + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } + + fatal(...args: string[]) { + hilog.fatal(this.domain, this.prefix, this.format, args); + } + + isLoggable(level: number) { + hilog.isLoggable(this.domain, this.prefix, level); + } +} + +export default new Logger('[Sample_MDNS_Case]'); diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/common/ServiceOperateStatus.ets b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/common/ServiceOperateStatus.ets new file mode 100644 index 0000000000000000000000000000000000000000..11cee8e34ed7464a793d7fa6df3b49eb19b732d6 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/common/ServiceOperateStatus.ets @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025 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. + */ + +export function resourceToString(resource: Resource): string { + return getContext().resourceManager.getStringSync(resource); +} + +interface ServiceOperateStatus { + ADD_SUCCESS: string; + ADD_FAIL: string; + ADDING: string; + RESOLVE_SUCCESS: string; + RESOLVE_FAIL: string; + RESOLVING: string; + REMOVE_SUCCESS: string; + REMOVE_FAIL: string; + REMOVING: string; + DISCOVERY_STARTED: string; + DISCOVERY_FAIL1: string; + DISCOVERY_FAIL2: string; + DISCOVERY_FAIL3: string; + DISCOVERY_STARTING: string; + STOPPED: string; + STOP_FAIL: string; + STOPPING: string; +} + +export const serviceOperateStatus:ServiceOperateStatus = { + ADD_SUCCESS: resourceToString($r('app.string.ADD_SUCCESS')), + ADD_FAIL: resourceToString($r('app.string.ADD_FAIL')), + ADDING: resourceToString($r('app.string.ADDING')), + RESOLVE_SUCCESS: resourceToString($r('app.string.RESOLVE_SUCCESS')), + RESOLVE_FAIL: resourceToString($r('app.string.RESOLVE_FAIL')), + RESOLVING: resourceToString($r('app.string.RESOLVING')), + REMOVE_SUCCESS: resourceToString($r('app.string.REMOVE_SUCCESS')), + REMOVE_FAIL: resourceToString($r('app.string.REMOVE_FAIL')), + REMOVING: resourceToString($r('app.string.REMOVING')), + DISCOVERY_STARTED: resourceToString($r('app.string.DISCOVERY_STARTED')), + DISCOVERY_FAIL1: resourceToString($r('app.string.DISCOVERY_FAIL1')), + DISCOVERY_FAIL2: resourceToString($r('app.string.DISCOVERY_FAIL2')), + DISCOVERY_FAIL3: resourceToString($r('app.string.DISCOVERY_FAIL3')), + DISCOVERY_STARTING: resourceToString($r('app.string.DISCOVERY_STARTING')), + STOPPED: resourceToString($r('app.string.STOPPED')), + STOP_FAIL: resourceToString($r('app.string.STOP_FAIL')), + STOPPING: resourceToString($r('app.string.STOPPING')) +}; \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/entryability/EntryAbility.ets b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..d4acfaed2b4e28920e14fe596506c5f5dfbf4ba9 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 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. + */ + +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { window } from '@kit.ArkUI'; +import Logger from '../common/Logger'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + Logger.info('Ability onCreate'); + } + + onDestroy(): void { + Logger.info( 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + Logger.info('Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + Logger.error(`Failed to load the content. Cause: ${JSON.stringify(err)}`); + return; + } + Logger.info('Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + Logger.info('Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + Logger.info('Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + Logger.info('Ability onBackground'); + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..f1047bd4d077992ee5ef3098291ecd3ada054198 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; +import Logger from '../common/Logger'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + Logger.info('onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + Logger.info(`onRestore ok ${JSON.stringify(bundleVersion)}`); + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/pages/Index.ets b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..5357956d01b1f38cfccd3c4b5d9a25b94b58b08b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2025 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. + */ + +import { mdns } from '@kit.NetworkKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import Logger from '../common/Logger'; +import { serviceOperateStatus } from '../common/ServiceOperateStatus'; +import { ComponentId } from '../common/ComponentId'; + +// 创建Context对象 +let context = getContext(this) as Context; + +// 定义设备信息的接口 +interface DeviceInfo { + id: string; // 唯一标识符 + text: string; // 设备信息文本 +} + +export function resourceToString(resource: Resource): string { + return getContext().resourceManager.getStringSync(resource); +} + +@Entry +@Component +struct MdnsServiceDemo { + @State addServiceStatus: string = resourceToString($r('app.string.not_added')); // 添加本地服务状态展示 + @State resolveServiceStatus: string = resourceToString($r('app.string.not_resolved')); // 解析本地服务状态展示 + @State removeServiceStatus: string = resourceToString($r('app.string.not_removed')); // 移除本地服务状态展示 + @State discoveryStatus: string = resourceToString($r('app.string.not_started')); // mDNS服务发现状态展示 + @State discoveredDevices: Array = []; // 保存发现的设备信息 + private localServiceInfo: mdns.LocalServiceInfo = { + serviceType: '_print._tcp', + serviceName: 'servicename', + port: 5555, + host: { + address: '127.0.0.1' + }, + serviceAttribute: [{ key: '111', value: [1] }] + }; + + private onDiscoveryStart(data: mdns.DiscoveryEventInfo): void { + Logger.info(`Discovery Started: ${JSON.stringify(data)}`); + } + + private onDiscoveryStop(data: mdns.DiscoveryEventInfo): void { + Logger.info(`Discovery Stopped: ${JSON.stringify(data)}`); + } + + private onServiceFound(data: mdns.LocalServiceInfo): void { + Logger.info(`Service Found: ${JSON.stringify(data)}`); + // 检查设备是否已经存在于列表中,避免重复推送 + const deviceExists = this.discoveredDevices.some((device) => device.text === JSON.stringify(data)); + if (!deviceExists) { + // 只有在设备未存在的情况下,才会推送 + mdns.resolveLocalService(context, data).then((resolvedData) => { + Logger.info(`Resolved Service: ${JSON.stringify(resolvedData)}`); + // 推送新的设备信息到列表 + this.discoveredDevices.push({ id: Date.now().toString(), text: JSON.stringify(resolvedData) }); + }).catch((err: BusinessError) => { + Logger.error(`Error resolving service: ${JSON.stringify(err)}`); + }); + } else { + Logger.info('Service already exists, skipping push.'); + } + } + + private onServiceLost(data: mdns.LocalServiceInfo): void { + Logger.info(`Service Lost: ${JSON.stringify(data)}`); + const index = this.discoveredDevices.findIndex( + (device) => device.text === JSON.stringify(data) + ); + if (index !== -1) { + this.discoveredDevices.splice(index, 1); + } + } + + build() { + Column() { + Text($r('app.string.mDNS_Service_Example')) + .fontSize($r('app.float.size20')) + .margin({ bottom: $r('app.float.margin20') }) + + // 显示当前发现的设备 + Text($r('app.string.Device_Discovered')) + .fontSize($r('app.float.size18')) + .fontColor(Color.Black) + .margin({ top: $r('app.float.margin10') }) + + // 遍历设备信息并显示 + ForEach(this.discoveredDevices, (device: DeviceInfo) => { + Text(device.text) + .fontSize($r('app.float.size16')) + .fontColor(Color.Gray) + .margin({ top: $r('app.float.margin5'), bottom: $r('app.float.margin5') }) + }); + + + // 显示添加本地服务状态的文本 + Text(resourceToString($r('app.string.Add_Local_Service')) + ': ' + this.addServiceStatus) + .fontSize($r('app.float.size18')) + .fontColor(Color.Black) + .margin({ top: $r('app.float.margin10') }) + .id(ComponentId.SERVICE_ADD_STATUS) + + // 显示解析本地服务状态的文本 + Text(resourceToString($r('app.string.Resolve_Local_Service')) + ': ' + this.resolveServiceStatus) + .fontSize($r('app.float.size18')) + .fontColor(Color.Black) + .margin({ top: $r('app.float.margin10') }) + .id(ComponentId.SERVICE_RESOLVE_STATUS) + + // 显示移除本地服务状态的文本 + Text(resourceToString($r('app.string.remove_Local_Service')) + ': ' + this.removeServiceStatus) + .fontSize($r('app.float.size18')) + .fontColor(Color.Black) + .margin({ top: $r('app.float.margin10') }) + .id(ComponentId.SERVICE_REMOVE_STATUS) + + // 显示mDNS服务发现状态的文本 + Text(resourceToString($r('app.string.Start_mDNS_Service_Discovery')) + ': ' + this.discoveryStatus) + .fontSize($r('app.float.size18')) + .fontColor(Color.Black) + .margin({ top: $r('app.float.margin10') }) + .id(ComponentId.SERVICE_DISCOVERY_STATUS) + + // 第一个按钮:添加本地服务 + Button($r('app.string.Add_Local_Service')) + .onClick(() => { + this.addLocalService(); + }) + .width('80%') + .height($r('app.float.height50')) + .margin({ top: $r('app.float.margin20') }) + .backgroundColor(Color.Blue) + .fontColor(Color.White) + .fontSize($r('app.float.size20')) + .borderRadius($r('app.float.borderRadius')) + .id(ComponentId.ADD_SERVICE_BTN) + + // 第二个按钮:解析本地服务 + Button($r('app.string.Resolve_Local_Service')) + .onClick(() => { + this.resolveLocalService(); + }) + .width('80%') + .height($r('app.float.height50')) + .margin({ top: $r('app.float.margin20') }) + .backgroundColor(Color.Green) + .fontColor(Color.White) + .fontSize($r('app.float.size20')) + .borderRadius($r('app.float.borderRadius')) + .id(ComponentId.RESOLVE_SERVICE_BTN) + + // 第三个按钮:移除本地服务 + Button($r('app.string.remove_Local_Service')) + .onClick(() => { + this.removeLocalService(); + }) + .width('80%') + .height($r('app.float.height50')) + .margin({ top: $r('app.float.margin20') }) + .backgroundColor(Color.Red) + .fontColor(Color.White) + .fontSize($r('app.float.size20')) + .borderRadius($r('app.float.borderRadius')) + .id(ComponentId.REMOVE_SERVICE_BTN) + + // 第四个按钮:启动 mDNS 服务发现 + Button($r('app.string.Start_mDNS_Service_Discovery')) + .onClick(() => { + this.startServiceDiscovery(); + }) + .width('80%') + .height($r('app.float.height50')) + .margin({ top: $r('app.float.margin20') }) + .backgroundColor(Color.Orange) + .fontColor(Color.White) + .fontSize($r('app.float.size20')) + .borderRadius($r('app.float.borderRadius')) + .id(ComponentId.START_DISCOVERY_SERVICE_BTN) + + // 第五个按钮:停止 mDNS 服务发现 + Button($r('app.string.Stop_mDNS_Service_Discovery')) + .onClick(() => { + this.stopServiceDiscovery(); + }) + .width('80%') + .height($r('app.float.height50')) + .margin({ top: $r('app.float.margin20') }) + .fontColor(Color.White) + .fontSize($r('app.float.size20')) + .borderRadius($r('app.float.borderRadius')) + .id(ComponentId.STOP_DISCOVERY_SERVICE_BTN) + } + .width('100%') + .height('100%') + .justifyContent(FlexAlign.Center); + } + + // [Start manage_local_services] + private addLocalService(): void { + this.addServiceStatus = serviceOperateStatus.ADDING; + mdns.addLocalService(context, this.localServiceInfo).then((data) => { + this.addServiceStatus = serviceOperateStatus.ADD_SUCCESS; + Logger.info(`Local Service Added: ${JSON.stringify(data)}`); + }).catch((err: BusinessError) => { + this.addServiceStatus = serviceOperateStatus.ADD_FAIL; + Logger.error(`Error adding local service: ${JSON.stringify(err)}`); + }); + } + + private resolveLocalService(): void { + this.resolveServiceStatus = serviceOperateStatus.RESOLVING; + mdns.resolveLocalService(context, this.localServiceInfo).then((data) => { + this.resolveServiceStatus = serviceOperateStatus.RESOLVE_SUCCESS; + Logger.info(`Resolved Local Service: ${JSON.stringify(data)}`); + }).catch((err: BusinessError) => { + this.resolveServiceStatus = serviceOperateStatus.RESOLVE_FAIL; + Logger.error(`Error resolving local service: ${JSON.stringify(err)}`); + }); + } + + private removeLocalService(): void { + this.removeServiceStatus = serviceOperateStatus.REMOVING; + mdns.removeLocalService(context, this.localServiceInfo).then((data) => { + this.removeServiceStatus = serviceOperateStatus.REMOVE_SUCCESS; + Logger.info(`Local Service Removed: ${JSON.stringify(data)}`); + }).catch((err: BusinessError) => { + this.removeServiceStatus = serviceOperateStatus.REMOVE_FAIL; + Logger.error(`Error removing local service: ${JSON.stringify(err)}`); + }); + } + // [End manage_local_services] + + // [Start discover_local_services] + private async startServiceDiscovery(): Promise { + this.discoveryStatus = serviceOperateStatus.DISCOVERY_STARTING; + if (!context) { + this.discoveryStatus = serviceOperateStatus.DISCOVERY_FAIL3; + Logger.error('Context is undefined or null.'); + return; + } + + let serviceType = '_print._tcp'; + let discoveryService = mdns.createDiscoveryService(context, serviceType); + if (!discoveryService) { + this.discoveryStatus = serviceOperateStatus.DISCOVERY_FAIL2; + Logger.error('Failed to create discovery service.'); + return; + } + + discoveryService.on('discoveryStart', (data) => this.onDiscoveryStart(data)); + discoveryService.on('discoveryStop', (data) => this.onDiscoveryStop(data)); + discoveryService.on('serviceFound', (data) => this.onServiceFound(data)); + discoveryService.on('serviceLost', (data) => this.onServiceLost(data)); + + try { + discoveryService.startSearchingMDNS(); + this.discoveryStatus = serviceOperateStatus.DISCOVERY_STARTED; + } catch (error) { + this.discoveryStatus = serviceOperateStatus.DISCOVERY_FAIL1; + Logger.error(`Error starting mDNS service discovery: ${JSON.stringify(error)}`); + } + } + + private async stopServiceDiscovery(): Promise { + this.discoveryStatus = serviceOperateStatus.STOPPING; + let serviceType = '_print._tcp'; + let discoveryService = mdns.createDiscoveryService(context, serviceType); + + try { + discoveryService.stopSearchingMDNS(); + discoveryService.off('discoveryStart', (data) => this.onDiscoveryStart(data)); + discoveryService.off('discoveryStop', (data) => this.onDiscoveryStop(data)); + discoveryService.off('serviceFound', (data) => this.onServiceFound(data)); + discoveryService.off('serviceLost', (data) => this.onServiceLost(data)); + this.discoveryStatus = serviceOperateStatus.STOPPED; + } catch (err) { + this.discoveryStatus = serviceOperateStatus.STOP_FAIL; + Logger.info(`Error stopping mDNS service discovery: ${JSON.stringify(err)}`); + } + } + // [End discover_local_services] +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/module.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..9deddb7dec7ee08ea3fea42236e640756faa9e43 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/module.json5 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:app_name", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/element/color.json b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/element/float.json b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/element/float.json new file mode 100644 index 0000000000000000000000000000000000000000..701caa6df3be590ee24d1384f1992030152a4d89 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/element/float.json @@ -0,0 +1,40 @@ +{ + "float": [ + { + "name": "margin20", + "value": "20" + }, + { + "name": "margin18", + "value": "18" + }, + { + "name": "margin5", + "value": "5" + }, + { + "name": "margin10", + "value": "10" + }, + { + "name": "size16", + "value": "16" + }, + { + "name": "size18", + "value": "18" + }, + { + "name": "size20", + "value": "20" + }, + { + "name": "height50", + "value": "50" + }, + { + "name": "borderRadius", + "value": "5" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..6b8fafa6fff14a2c5ebcca381fc12779504bf4c3 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/element/string.json @@ -0,0 +1,148 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "Device_Discovered", + "value": "Device_Discovered" + }, + { + "name": "mDNS_Service_Example", + "value": "mDNS_Service_Example" + }, + { + "name": "Add_Local_Service", + "value": "Add_Local_Service" + }, + { + "name": "Resolve_Local_Service", + "value": "Resolve_Local_Service" + }, + { + "name": "Start_mDNS_Service_Discovery", + "value": "Start_mDNS_Service_Discovery" + }, + { + "name": "Stop_mDNS_Service_Discovery", + "value": "Stop_mDNS_Service_Discovery" + }, + { + "name": "remove_Local_Service", + "value": "Remove_Local_Service" + }, + { + "name": "Add_Status", + "value": "添加本地服务: 添加成功" + }, + { + "name": "Parse_Status", + "value": "解析本地服务: 解析成功" + }, + { + "name": "Remove_Status", + "value": "移除本地服务: 移除成功" + }, + { + "name": "mDNS_Start", + "value": "启动mDNS服务发现: 已启动" + }, + { + "name": "mDNS_Stop", + "value": "启动mDNS服务发现: 已停止" + }, + { + "name": "not_added", + "value": "未添加" + }, + { + "name": "not_resolved", + "value": "未解析" + }, + { + "name": "not_removed", + "value": "未移除" + }, + { + "name": "not_started", + "value": "未移除" + }, + { + "name": "ADD_SUCCESS", + "value": "添加成功" + }, + { + "name": "ADD_FAIL", + "value": "添加失败" + }, + { + "name": "ADDING", + "value": "添加中" + }, + { + "name": "RESOLVE_SUCCESS", + "value": "解析成功" + }, + { + "name": "RESOLVE_FAIL", + "value": "解析失败" + }, + { + "name": "RESOLVING", + "value": "解析中" + }, + { + "name": "REMOVE_SUCCESS", + "value": "移除成功" + }, + { + "name": "REMOVE_FAIL", + "value": "移除失败" + }, + { + "name": "REMOVING", + "value": "移除中" + }, + { + "name": "DISCOVERY_STARTED", + "value": "已启动" + }, + { + "name": "DISCOVERY_FAIL1", + "value": "启动失败" + }, + { + "name": "DISCOVERY_FAIL2", + "value": "启动失败,创建DiscoveryService失败" + }, + { + "name": "DISCOVERY_FAIL3", + "value": "启动失败,Context无效" + }, + { + "name": "DISCOVERY_STARTING", + "value": "启动中" + }, + { + "name": "STOPPED", + "value": "已停止" + }, + { + "name": "STOP_FAIL", + "value": "停止失败" + }, + { + "name": "STOPPING", + "value": "停止中" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/media/background.png b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/media/background.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/media/foreground.png b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/media/foreground.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/media/layered_image.json b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/media/startIcon.png b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/media/startIcon.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/profile/backup_config.json b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/profile/main_pages.json b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/en_US/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..5c0563eed32bfbc3dc70d52b8f07a60221995bd0 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,148 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "Device_Discovered", + "value": "Device_Discovered" + }, + { + "name": "mDNS_Service_Example", + "value": "mDNS_Service_Example" + }, + { + "name": "Add_Local_Service", + "value": "Add_Local_Service" + }, + { + "name": "Resolve_Local_Service", + "value": "Resolve_Local_Service" + }, + { + "name": "Start_mDNS_Service_Discovery", + "value": "Start_mDNS_Service_Discovery" + }, + { + "name": "Stop_mDNS_Service_Discovery", + "value": "Stop_mDNS_Service_Discovery" + }, + { + "name": "remove_Local_Service", + "value": "Remove_Local_Service" + }, + { + "name": "Add_Status", + "value": "Add_Status" + }, + { + "name": "Parse_Status", + "value": "Parse_Status" + }, + { + "name": "Remove_Status", + "value": "Remove_Status" + }, + { + "name": "mDNS_Start", + "value": "mDNS_Start" + }, + { + "name": "mDNS_Stop", + "value": "mDNS_Stop" + }, + { + "name": "not_added", + "value": "not_added" + }, + { + "name": "not_resolved", + "value": "not_resolved" + }, + { + "name": "not_removed", + "value": "not_removed" + }, + { + "name": "not_started", + "value": "not_started" + }, + { + "name": "ADD_SUCCESS", + "value": "ADD_SUCCESS" + }, + { + "name": "ADD_FAIL", + "value": "ADD_FAIL" + }, + { + "name": "ADDING", + "value": "ADDING" + }, + { + "name": "RESOLVE_SUCCESS", + "value": "RESOLVE_SUCCESS" + }, + { + "name": "RESOLVE_FAIL", + "value": "RESOLVE_FAIL" + }, + { + "name": "RESOLVING", + "value": "RESOLVING" + }, + { + "name": "REMOVE_SUCCESS", + "value": "REMOVE_SUCCESS" + }, + { + "name": "REMOVE_FAIL", + "value": "REMOVE_FAIL" + }, + { + "name": "REMOVING", + "value": "REMOVING" + }, + { + "name": "DISCOVERY_STATED", + "value": "DISCOVERY_STATED" + }, + { + "name": "DISCOVERY_FAIL1", + "value": "DISCOVERY_FAIL1" + }, + { + "name": "DISCOVERY_FAIL2", + "value": "DISCOVERY_FAIL2" + }, + { + "name": "DISCOVERY_FAIL3", + "value": "DISCOVERY_FAIL3" + }, + { + "name": "DISCOVERY_STARTING", + "value": "DISCOVERY_STARTING" + }, + { + "name": "STOPPED", + "value": "STOPPED" + }, + { + "name": "STOP_FAIL", + "value": "STOP_FAIL" + }, + { + "name": "STOPPING", + "value": "STOPPING" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/zh_CN/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..cc8eb460ecd10c6e50f17723e0dc6e4662ebb44e --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,148 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "Device_Discovered", + "value": "已发现设备" + }, + { + "name": "mDNS_Service_Example", + "value": "mDNS服务示例" + }, + { + "name": "Add_Local_Service", + "value": "添加本地服务" + }, + { + "name": "Resolve_Local_Service", + "value": "解析本地服务" + }, + { + "name": "Start_mDNS_Service_Discovery", + "value": "启动mDNS服务发现" + }, + { + "name": "Stop_mDNS_Service_Discovery", + "value": "停止mDNS服务发现" + }, + { + "name": "remove_Local_Service", + "value": "移除本地服务" + }, + { + "name": "Add_Status", + "value": "添加本地服务: 添加成功" + }, + { + "name": "Parse_Status", + "value": "解析本地服务: 解析成功" + }, + { + "name": "Remove_Status", + "value": "移除本地服务: 移除成功" + }, + { + "name": "mDNS_Start", + "value": "启动mDNS服务发现: 已启动" + }, + { + "name": "mDNS_Stop", + "value": "启动mDNS服务发现: 已停止" + }, + { + "name": "not_added", + "value": "未添加" + }, + { + "name": "not_resolved", + "value": "未解析" + }, + { + "name": "not_removed", + "value": "未移除" + }, + { + "name": "not_started", + "value": "未启动" + }, + { + "name": "ADD_SUCCESS", + "value": "添加成功" + }, + { + "name": "ADD_FAIL", + "value": "添加失败" + }, + { + "name": "ADDING", + "value": "添加中" + }, + { + "name": "RESOLVE_SUCCESS", + "value": "解析成功" + }, + { + "name": "RESOLVE_FAIL", + "value": "解析失败" + }, + { + "name": "RESOLVING", + "value": "解析中" + }, + { + "name": "REMOVE_SUCCESS", + "value": "移除成功" + }, + { + "name": "REMOVE_FAIL", + "value": "移除失败" + }, + { + "name": "REMOVING", + "value": "移除中" + }, + { + "name": "DISCOVERY_STATED", + "value": "已启动" + }, + { + "name": "DISCOVERY_FAIL1", + "value": "启动失败" + }, + { + "name": "DISCOVERY_FAIL2", + "value": "启动失败,创建DiscoveryService失败" + }, + { + "name": "DISCOVERY_FAIL3", + "value": "启动失败,Context无效" + }, + { + "name": "DISCOVERY_STARTING", + "value": "启动中" + }, + { + "name": "STOPPED", + "value": "已停止" + }, + { + "name": "STOP_FAIL", + "value": "停止失败" + }, + { + "name": "STOPPING", + "value": "停止中" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/ohosTest/ets/test/Ability.test.ets b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..2233f21c552d42ec128efe14b6e9af6de664289a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2025 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. + */ + +import { describe, it, expect } from '@ohos/hypium'; +import { abilityDelegatorRegistry, Driver, ON } from '@kit.TestKit'; +import { UIAbility, Want } from '@kit.AbilityKit'; +import Logger from '../../../main/ets/common/Logger'; +import { ComponentId } from '../../../main/ets/common/ComponentId'; + +const delegator = abilityDelegatorRegistry.getAbilityDelegator(); + +const abilityDelegator = abilityDelegatorRegistry.getAbilityDelegator(); + +function sleep(time: number) { + return new Promise((resolve: Function) => setTimeout(resolve, time)); +} + +async function getResourceString(resource: Resource): Promise { + let manage = abilityDelegator.getAppContext().resourceManager; + let text = await manage.getStringValue(resource); + return text; +} + +const BUNDLE: string = 'MDNSCASE'; +const BUNDLE_NAME: string = 'com.samples.mdns_case'; +const ABILITY_NAME: string = 'EntryAbility'; + +const delayTime: number = 2000; + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + it(BUNDLE + 'assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + Logger.info('it begin'); + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }) + + + /** + * @tc.number StartAbility_001 + * @tc.name StartAbility_001 + * @tc.desc 启动Ability + */ + it(BUNDLE + 'StartAbility_001', 0, async (done: Function) => { + Logger.info(`${BUNDLE}StartAbility_001 begin`); + const want: Want = { + bundleName: BUNDLE_NAME, + abilityName: ABILITY_NAME + }; + await delegator.startAbility(want); + await sleep(delayTime); + const ability: UIAbility = await delegator.getCurrentTopAbility(); + expect(ability.context.abilityInfo.name).assertEqual(ABILITY_NAME); + done(); + Logger.info(`${BUNDLE}StartAbility_001 end`); + }); + + /** + * @tc.number Mdns_StartDiscovery_001 + * @tc.name Mdns_StartDiscovery_001 + * @tc.desc 测试启动 mDNS 服务发现按钮 + */ + it(BUNDLE + 'Mdns_StartDiscovery_001', 0, async (done: Function) => { + Logger.info(`${BUNDLE_NAME} Mdns_StartDiscovery_001 begin`); + let driver = Driver.create(); + await driver.delayMs(delayTime); + await driver.assertComponentExist(ON.id(ComponentId.START_DISCOVERY_SERVICE_BTN)); + + let stack = await driver.findComponent(ON.id(ComponentId.START_DISCOVERY_SERVICE_BTN)); + await stack.click(); + await driver.delayMs(delayTime); + + let str = await getResourceString($r('app.string.mDNS_Start')); + let resultText = await driver.findComponent(ON.id(ComponentId.SERVICE_DISCOVERY_STATUS)); + let result = await resultText.getText(); + expect(result).assertEqual(str); + + Logger.info(`${BUNDLE_NAME} Mdns_StartDiscovery_001 end`); + done(); + }); + + /** + * @tc.number Mdns_AddLocalService_001 + * @tc.name Mdns_AddLocalService_001 + * @tc.desc 测试添加本地服务按钮 + */ + it(BUNDLE + 'Mdns_AddLocalService_001', 0, async (done: Function) => { + Logger.info(`${BUNDLE_NAME} Mdns_AddLocalService_001 begin`); + let driver = Driver.create(); + await driver.delayMs(delayTime); + await driver.assertComponentExist(ON.id(ComponentId.ADD_SERVICE_BTN)); + + let stack = await driver.findComponent(ON.id(ComponentId.ADD_SERVICE_BTN)); + await stack.click(); + await driver.delayMs(delayTime); + + let str = await getResourceString($r('app.string.Add_Status')); + let resultText = await driver.findComponent(ON.id(ComponentId.SERVICE_ADD_STATUS)); + let result = await resultText.getText(); + expect(result).assertEqual(str); + + Logger.info(`${BUNDLE_NAME} Mdns_AddLocalService_001 end`); + done(); + }); + + /** + * @tc.number Mdns_ResolveLocalService_001 + * @tc.name Mdns_ResolveLocalService_001 + * @tc.desc 测试解析本地服务按钮 + */ + it(BUNDLE + 'Mdns_ResolveLocalService_001', 0, async (done: Function) => { + Logger.info(`${BUNDLE_NAME} Mdns_ResolveLocalService_001 begin`); + let driver = Driver.create(); + await driver.delayMs(delayTime); + await driver.assertComponentExist(ON.id(ComponentId.RESOLVE_SERVICE_BTN)); + + let stack = await driver.findComponent(ON.id(ComponentId.RESOLVE_SERVICE_BTN)); + await stack.click(); + await driver.delayMs(delayTime); + + let str = await getResourceString($r('app.string.Parse_Status')); + let resultText = await driver.findComponent(ON.id(ComponentId.SERVICE_RESOLVE_STATUS)); + let result = await resultText.getText(); + + expect(result).assertEqual(str); + + Logger.info(`${BUNDLE_NAME} Mdns_ResolveLocalService_001 end`); + done(); + }); + + /** + * @tc.number Mdns_RemoveLocalService_001 + * @tc.name Mdns_RemoveLocalService_001 + * @tc.desc 测试移除本地服务按钮 + */ + it(BUNDLE + 'Mdns_RemoveLocalService_001', 0, async (done: Function) => { + Logger.info(`${BUNDLE_NAME} Mdns_RemoveLocalService_001 begin`); + let driver = Driver.create(); + await driver.delayMs(delayTime); + await driver.assertComponentExist(ON.id(ComponentId.REMOVE_SERVICE_BTN)); + + let stack = await driver.findComponent(ON.id(ComponentId.REMOVE_SERVICE_BTN)); + await stack.click(); + await driver.delayMs(delayTime); + + let str = await getResourceString($r('app.string.Remove_Status')); + let resultText = await driver.findComponent(ON.id(ComponentId.SERVICE_REMOVE_STATUS)); + let result = await resultText.getText(); + + expect(result).assertEqual(str); + + Logger.info(`${BUNDLE_NAME} Mdns_RemoveLocalService_001 end`); + done(); + }); + + /** + * @tc.number Mdns_StopDiscovery_001 + * @tc.name Mdns_StopDiscovery_001 + * @tc.desc 测试停止 mDNS 服务发现按钮 + */ + it(BUNDLE + 'Mdns_StopDiscovery_001', 0, async (done: Function) => { + Logger.info(`${BUNDLE_NAME } Mdns_StopDiscovery_001 begin`); + let driver = Driver.create(); + await driver.delayMs(delayTime); + await driver.assertComponentExist(ON.id(ComponentId.STOP_DISCOVERY_SERVICE_BTN)); + + let stack = await driver.findComponent(ON.id(ComponentId.STOP_DISCOVERY_SERVICE_BTN)); + await stack.click(); + await driver.delayMs(delayTime); + + let str = await getResourceString($r('app.string.mDNS_Stop')); + let resultText = await driver.findComponent(ON.id(ComponentId.SERVICE_DISCOVERY_STATUS)); + let result = await resultText.getText(); + + expect(result).assertEqual(str); + + Logger.info(`${BUNDLE_NAME } Mdns_StopDiscovery_001 end`); + done(); + }); + }) +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/ohosTest/ets/test/List.test.ets b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..1eac52fcebe8958e19a7b8fed2e8f39c520a3e42 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ + +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/ohosTest/module.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c3fd9dda3040d888d9d8b0b62bcb5d3b6fbeb614 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/hvigor/hvigor-config.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..43beb743cbd25c3507b1cf8a744bf8197b3bf2fb --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/hvigor/hvigor-config.json5 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/hvigorfile.ts b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/oh-package.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..952ab183532e81c9c50e5de1e64393704e1e48fe --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.18", + "@ohos/hamock": "1.0.0" + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/ohosTest.md b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..4e4f49d73a996d3308a34275b2bc0399a22b87e7 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/ohosTest.md @@ -0,0 +1,11 @@ +# 测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +| -------------------- | ------------ | ---- | -------------------------- | -------- | -------- | +| 启动mDNS服务发现按钮 | 设备正常运行 | | 显示“mDNS服务发现开始”状态 | 是 | pass | +| 添加本地服务按钮 | 设备正常运行 | | 显示“本地服务添加成功”状态 | 是 | pass | +| 解析本地服务按钮 | 设备正常运行 | | 显示“解析本地服务成功”状态 | 是 | pass | +| 移除本地服务按钮 | 设备正常运行 | | 显示“本地服务移除成功”状态 | 是 | pass | +| 停止mDNS服务发现按钮 | 设备正常运行 | | 显示“mDNS服务发现停止”状态 | 是 | pass | diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/screenshots/Add_Local_Service.jpg b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/screenshots/Add_Local_Service.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7b463078a0ad7850b05474ebdc11eee8fdd0c646 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/screenshots/Add_Local_Service.jpg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/screenshots/Remove_Local_Service.jpg b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/screenshots/Remove_Local_Service.jpg new file mode 100644 index 0000000000000000000000000000000000000000..afd1409b22e3e409f41818119ebc820d92b33556 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/screenshots/Remove_Local_Service.jpg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/screenshots/Resolve_Local_Service.jpg b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/screenshots/Resolve_Local_Service.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a0d4ec482244c56be380f3f000298276abe04e4a Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/screenshots/Resolve_Local_Service.jpg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/screenshots/Start_mDNS_Service_Discovery.jpg b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/screenshots/Start_mDNS_Service_Discovery.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d3350bf8c33a90d27441d1e75621a37c4d5b3161 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/MDNS_case/screenshots/Start_mDNS_Service_Discovery.jpg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/.gitignore b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/AppScope/app.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..fc516e06d493955a2d27a553866116517f2d4007 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "bundleName": "com.samples.Socket", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/AppScope/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..6ec2ef9e5d6283d2affe4374f5bd4f46434b3d21 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "Socket" + } + ] +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/AppScope/resources/base/media/app_icon.png b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/AppScope/resources/base/media/app_icon.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/README_zh.md b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..c3bf6a7ce26e9ce043336e53883b30481a44ccf2 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/README_zh.md @@ -0,0 +1,216 @@ +# Socket 连接 + +### 介绍 + +本示例依据指南《开发->系统->网络->Network Kit》(网络服务->Network Kit数据传输能力->[Socket连接](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/network/socket-connection.md))进行编写。 + +Socket 连接主要是通过 Socket 进行数据传输,支持 TCP/UDP/Multicast/TLS 协议。 + +主要场景有: + +- 应用作为TCP客户端进行数据传输 + +- 应用作为TCP服务端进行数据传输 + +- 应用作为UDP客户端&服务端进行数据传输 + +- 应用通过 Multicast Socket 进行数据传输 + +- 应用通过 Local Socket进行数据传输 + +- 应用通过 Local Socket Server 进行数据传输 + +- 应用通过 单向 TLS Socket 进行加密数据传输 + +- 应用通过 双向 TLS Socket 进行加密数据传输 + +- 应用通过将 TCP Socket 升级为 单向TLS Socket 进行加密数据传输 + +- 应用通过将 TCP Socket 升级为 双向TLS Socket 进行加密数据传输 + +- 应用通过 TLS Socket Server 进行加密数据传输 + + + +### 效果预览 + +| 主界面 | TCP客户端 | TCP服务端 | UDP | +| ----------------------------------- | ------------------------------------ | ------------------------------------ | ------------------------------ | +| ![image](screenshots/MainPage.jpeg) | ![image](screenshots/TcpClient.jpeg) | ![image](screenshots/TcpServer.jpeg) | ![image](screenshots/UDP.jpeg) | + +使用说明 + +注:以下所有端口号默认为5555 + +1. Tcp客户端使用说明 + + (1)需要程序启动端设备与服务端设备连接同一个热点 + + (2)在服务端启动Readme同路径下的Tools(以下简称为Tools)中的Tcp_Server.py(需要在脚本中加入服务端的实际IP地址) + + (3)后启动程序作为客户端,输入服务端IP与Port以及Message,点击Connect即可连接并发送 + +2. Tcp服务端使用说明 + + (1)需要程序启动端设备与服务端设备连接同一个热点 + + (2)启动程序作为服务端,输入本地IP与Port + + (3)在另一台设备上启动Tools中的Tcp_Client.py + + (4)后去可以进行发送消息和断开连接等操作 + +3. Udp使用说明 + + (1)需要程序启动端设备与服务端设备连接同一个热点 + + (2)该功能既可作为客户端又可作为服务端,需要输入本地IP和Port,再点击Start Server,即可启动消息监听,后续选择通信对方的的IP和Port,即可发送消息 + +4. 多播通信使用说明 + + 点击按钮,即可加入到多播组并发送消息 + +5. Local数据传输 + + 点击首个按钮启动LocalSocketServer,点击次按钮启动LocalSocket客户端对象 + +6. 单向TLSSocket加密数据传输使用说明 + + 详情请参阅Tools/建立TLS单向与双向认证流程文档_zh.md + +7. 双向TLSSocket加密数据传输使用说明 + + 详情请参阅Tools/建立TLS单向与双向认证流程文档_zh.md + +8. TcpSocket升级为单向TLS Socket加密数据传输使用说明 + + 详情请参阅Tools/建立TLS单向与双向认证流程文档_zh.md + +9. TcpSocket升级为双向TLSSocket加密数据传输使用说明 + + 详情请参阅Tools/建立TLS单向与双向认证流程文档_zh.md + +10. TLSSocketServer加密数据传输使用说明 + 详情请参阅Tools/建立TLS单向与双向认证流程文档_zh.md + +### 工程目录 + +``` +entry/src/main/ +│---common +| |---Logger.ets // 日志组件 +|---components +| |---TitleBar.ets // 标题栏组件 +|---connect +| |---LocalSocket.ets // Local数据传输 +| |---Multicast.ets // 多播通信 +| |---OneWayTls.ets // 单向TLSSocket加密数据传输 +| |---Tcp2OneWayTls.ets // TcpSocket升级为单向TLS Socket加密数据传输 +| |---Tcp2TwoWayTls.ets // TcpSocket升级为双向TLSSocket加密数据传输 +| |---TcpClient.ets // Tcp客户端 +| |---TcpServer.ets // Tcp服务端 +| |---TlsServer.ets // Tls服务端 +| |---TwoWayTls.ets // 双向TLSSocket加密数据传输 +| |---UdpClient_Server.ets // Udp客户端、服务端 +|---entryability +| │---EntryAbility.ets +|---entrybackupability +│ |---EntryBackupAbility.ets +|---pages +│ |---Index.ets // 主页 +|---workers +| |---LocalSocketWorker.ets // Local数据传输——Worker线程部分 +| |---MulticastWorker.ets // 多播通信——Worker线程部分 +| |---OneWayTlsWorker.ets // 单向TLSSocket加密数据传输——Worker线程部分 +| |---Tcp2OneWayTlsWorker.ets // TcpSocket升级为单向TLS Socket加密数据传输——Worker线程部分 +| |---Tcp2TwoWayTlsWorker.ets // TcpSocket升级为双向TLSSocket加密数据传输——Worker线程部分 +| |---TcpClientWorker.ets // Tcp客户端——Worker线程部分 +| |---TcpServerWorker.ets // Tcp服务端——Worker线程部分 +| |---TlsServerWorker.ets // Tls服务端——Worker线程部分 +| |---TwoWayTlsWorker.ets // 双向TLSSocket加密数据传输——Worker线程部分 +| |---UdpWorker.ets // Udp客户端、服务端——Worker线程部分 +``` + +### 具体实现 + +**TCP 客户端实现:** + +- 创建一个 TCP 套接字,绑定本地 IP 地址与端口,并连接至目标服务端。 +- 发送数据至服务端,并接收来自服务端的响应消息。 +- 在连接使用完毕后,主动关闭套接字连接。 + +**TCP 服务端实现:** + +- 创建一个 TCP 服务端套接字,绑定本地 IP 地址与端口,开始监听客户端连接。 +- 接收到客户端连接后,使用一个 `TCPSocketConnection` 与客户端进行数据交换。 +- 接收来自客户端的数据,并向客户端发送响应数据。 +- 处理客户端的消息事件,并在通信结束后关闭连接。 + +**UDP 客户端与服务端实现:** + +- 在 UDP 中,应用程序可同时作为客户端与服务端,使用相同的端口进行通信。 +- 启动 UDP 服务,绑定本地 IP 地址与端口,进行消息监听。 +- 发送消息至目标地址与端口,等待接收消息。 + +**多播通信:** + +- 创建多播套接字并加入指定的多播组,开启对消息的监听。 +- 发送消息到多播组,组内所有的成员设备都会收到该消息。 +- 在使用完毕后,退出多播组并关闭消息监听。 + +**Local Socket 数据传输:** + +- 创建 Local Socket 客户端与服务端,支持同一设备内的进程间通信。 +- 服务端通过 `LocalSocketServer` 监听本地套接字,接收客户端的连接请求。 +- 客户端通过 `LocalSocket` 连接服务端,发送数据并接收来自服务端的响应。 + +**TLS 协议加密数据传输:** + +- 支持单向和双向 TLS 认证,通过 `TLSSocket` 与服务端建立安全连接。 +- 在客户端与服务端之间建立安全的加密连接后,进行数据传输。 +- 可选择双向认证,通过提供客户端和服务器的证书实现双向认证。 + +**TCP 升级为 TLS Socket:** + +- 在 TCP 连接建立之后,可以将 TCP 套接字升级为 TLS 套接字,从而实现数据的加密传输。 +- 配置 TLS 安全选项,包含证书、私钥以及 CA 证书等信息。 + +**TLS Socket 服务端:** + +- 创建 TLS 服务端,监听指定端口,等待客户端的连接。 +- 一旦客户端连接成功,使用 TLS 套接字进行加密通信。 + +### 相关权限 + +[ohos.permission.INTERNET](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-all.md#ohospermissioninternet) + +[ ohos.permission.GET_WIFI_INFO](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-all.md#ohospermissionget_wifi_info) + +### 依赖 + +不涉及。 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:华为手机。 + +2. HarmonyOS系统:HarmonyOS 5.0.2 Release及以上。 +3. HarmonyOS SDK版本:HarmonyOS 5.0.2 Release及以上。 +4. 本示例需要使用DevEco Studio Release(5.0.5.306)及以上版本才可编译运行。 + +5. 本示例需要设备联网使用。 + +6. 本示例涉及的多个接口需在服务端设备与客户端设备连接至同一热点的前提下运行。 + + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo NetWork_Kit/NetWorkKit_Datatransmission/Socket > .git/info/sparse-checkout +git remote add origin https://gitee.com/harmonyos_samples/guide-snippets.git +git pull origin master +``` \ No newline at end of file diff --git "a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/Tools/TLS\345\215\225\345\220\221\350\256\244\350\257\201\344\270\216\345\217\214\345\220\221\350\256\244\350\257\201\346\265\201\347\250\213_zh.md" "b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/Tools/TLS\345\215\225\345\220\221\350\256\244\350\257\201\344\270\216\345\217\214\345\220\221\350\256\244\350\257\201\346\265\201\347\250\213_zh.md" new file mode 100644 index 0000000000000000000000000000000000000000..959a8fdf03ee23eb3fa9807d21aa8937440f9687 --- /dev/null +++ "b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/Tools/TLS\345\215\225\345\220\221\350\256\244\350\257\201\344\270\216\345\217\214\345\220\221\350\256\244\350\257\201\346\265\201\347\250\213_zh.md" @@ -0,0 +1,604 @@ +# TLS单向认证与双向认证流程: + +说明:此流程用于Socket连接示例项目中的TLS相关操作,需在具备Linux的设备上完成证书配置。配置完成后,证书需根据需求部署到两台能够连接同一热点的设备上,其中一台作为服务端,另一台作为客户端。 + + + +## 一.单向认证流程:适用于(单向TLS Socket加密数据传输、TCP升级为单向TLS Socket加密数据传输) + +#### 1.生成所需要的证书配置文件(文件名:server_cert.cnf) + +输入以下命令: + +``` +vi server_cert.cnf +``` + +填入以下内容: + +``` +[ req ] +default_bits = 2048 +default_keyfile = server.key +distinguished_name = req_distinguished_name +x509_extensions = v3_req +prompt = no + +[ req_distinguished_name ] +C = XX +ST = XX +L = XX +O = XX +OU = XX +CN = 192.168.xxx.xxx +emailAddress = xxxx@example.com + +[ v3_req ] +subjectAltName = @alt_names + +[ alt_names ] +IP.1 = 192.168.xxx.xxx + + +##需要修改把两个ip地址均修改为服务端ip地址 +``` + + + +#### 2.进行服务端与客户端的证书配置(依次执行以下命令) + +``` +#客户端 +#生成ca私钥 +openssl genrsa -out ca_key.pem 2048 + +#生成自签名 CA 证书 +openssl req -x509 -new -nodes -key ca_key.pem -sha256 -days 365 -out ca_cert.pem -subj "/C=CN/ST=State/L=City/O=MyCA/CN=root" + + +#服务端 +#生成服务器私钥 +openssl genrsa -out server_key.pem 2048 + +#根据配置文件server_cert.cnf生成CSR文件 +openssl req -new -key server_key.pem -out server_csr.pem -config server_cert.cnf + +#使用 CA 签发服务器证书 +openssl x509 -req -in server_csr.pem -CA ca_cert.pem -CAkey ca_key.pem -CAcreateserial -out server_cert.pem -days 365 -extensions v3_req -extfile server_cert.cnf +``` + + + +#### 3.需要使用的文件 + +``` +服务端: +server_cert.pem + +server_key.pem + +客户端: +ca_cert.pem +``` + +#### 4.分配服务端和客户端所需要的文件(需要另外一台有python环境的设备作为服务端) + +服务端: + +``` +服务端在此可以选择使用python脚本启动,脚本文件需要与以下文件处于同一路径下: + +server_cert.pem + +server_key.pem + +Server.py(脚本文件) +``` + + + +脚本文件如下(需要修改IP地址为服务端实际IP地址): + +``` +import socket +import ssl + +# 服务端的 IP 和端口 +server_ip = "192.168.xxx.xxx" # 绑定到服务端的实际 IP +server_port = 5555 # 使用的端口号 + +# 服务端证书和私钥文件路径 +cert_file = "server_cert.pem" # 替换为实际服务端证书路径 +key_file = "server_key.pem" # 替换为实际服务端私钥路径 + +# 创建套接字 +server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +# 设置 TLS/SSL 上下文 +context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) +context.load_cert_chain(certfile=cert_file, keyfile=key_file) # 加载服务端证书和私钥 + +# 绑定服务端地址和端口 +server_socket.bind((server_ip, server_port)) +server_socket.listen(5) # 设置等待连接队列的最大长度 +print(f"服务端已启动,监听 {server_ip}:{server_port} ...") + +try: + while True: + # 等待客户端连接 + client_socket, client_address = server_socket.accept() + print(f"客户端 {client_address} 已连接") + + # 将普通套接字包装为 TLS 套接字 + tls_client_socket = context.wrap_socket(client_socket, server_side=True) + + try: + # 接收消息 + message = tls_client_socket.recv(1024).decode('utf-8') + print(f"收到消息: {message}") + + # 发送响应 + response = f"服务端已收到: {message}" + tls_client_socket.sendall(response.encode('utf-8')) + print("响应已发送") + + except Exception as e: + print(f"通信错误: {e}") + + finally: + tls_client_socket.close() + print(f"客户端 {client_address} 已断开连接") + +except KeyboardInterrupt: + print("\n服务端已停止") +finally: + server_socket.close() + +``` + + + +客户端: + +``` +程序启动端作为客户端,在使用前需要传入以下文件: + +ca_cert.pem +``` + + + +#### 5.程序运行流程 + +令服务端设备与客户端设备连接同一热点,服务端先启动python脚本,客户端运行程序,输入服务端地址(上文python中的实际IP)与服务端端口(5555),点击选择按钮,此时会打开文件资源管理器,选择传入的ca_cert.pem,后点击加载和连接,即可连接成功,后续可以发送消息 + + + +## 二.双向认证流程:适用于(双向TLS Socket加密数据传输、TCP升级为双向TLS Socket加密数据传输) + +#### 1.生成所需要的证书配置文件(文件名:server_cert.cnf、client_cert.cnf) + +输入以下命令: + +``` +vi server_cert.cnf +``` + +填入以下内容: + +``` +[ req ] +default_bits = 2048 +default_keyfile = server.key +distinguished_name = req_distinguished_name +x509_extensions = v3_req +prompt = no + +[ req_distinguished_name ] +C = XX +ST = XX +L = XX +O = XX +OU = XX +CN = 192.168.xxx.xxx +emailAddress = xxxx@example.com + +[ v3_req ] +subjectAltName = @alt_names + +[ alt_names ] +IP.1 = 192.168.xxx.xxx + + +##需要修改两个ip地址为服务端ip地址 +``` + +输入以下命令: + +``` +vi client_cert.cnf +``` + +填入以下内容: + +``` +[ req ] +default_bits = 2048 +default_keyfile = client.key +distinguished_name = req_distinguished_name +x509_extensions = v3_req +prompt = no + +[ req_distinguished_name ] +C = XX +ST = XX +L = XX +O = XX +OU = XX +CN = client +emailAddress = xxxx@example.com + +[ v3_req ] +``` + + + +#### 2.进行服务端与客户端的证书配置(依次执行以下命令) + +``` +#生成服务端私钥: +openssl genrsa -out server_key.pem 2048 + +#生成客户端私钥: +openssl genrsa -out client_key.pem 2048 + +#生成服务端 CSR: +openssl req -new -key server_key.pem -out server_csr.pem -config server_cert.cnf + +#生成客户端 CSR: +openssl req -new -key client_key.pem -out client_csr.pem -config client_cert.cnf + +#生成 CA 私钥: +openssl genrsa -out ca_key.pem 2048 + +#生成 CA 自签名证书: +openssl req -x509 -new -nodes -key ca_key.pem -sha256 -days 365 -out ca_cert.pem -subj "/C=CN/ST=State/L=City/O=MyCA/CN=root" + +#签署服务端CSR +openssl x509 -req -in server_csr.pem -CA ca_cert.pem -CAkey ca_key.pem -CAcreateserial -out server_cert.pem -days 365 -sha256 + +#签署客户端CSR +openssl x509 -req -in client_csr.pem -CA ca_cert.pem -CAkey ca_key.pem -CAcreateserial -out client_cert.pem -days 365 -sha256 +``` + + + +#### 3.需要使用的文件 + +``` +服务端: +server_key.pem +server_cert.pem +ca_cert.pem + +客户端: +client_key.pem +client_cert.pem +ca_cert.pem +``` + + + +#### 4.分配服务端和客户端所需要的文件(需要另外一台有python环境的设备作为服务端) + +服务端: + +``` +服务端在此可以选择使用python脚本启动,脚本文件需要与以下文件处于同一路径下: + +server_cert.pem + +server_key.pem + +ca_cert.pem + +Server.py(脚本文件) +``` + +脚本文件如下(需要修改IP地址为服务端实际IP地址): + +``` +import socket +import ssl + +# 服务端的 IP 和端口 +server_ip = "192.168.xxx.xxx" # 服务端实际绑定的 IP +server_port = 5555 # 服务端监听的端口 + +# 服务端证书、私钥和 CA 证书路径 +server_cert = "server_cert.pem" # 服务端证书路径 +server_key = "server_key.pem" # 服务端私钥路径 +ca_cert = "ca_cert.pem" # 用于验证客户端证书的 CA 证书 + +# 创建普通 TCP 套接字 +server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +# 设置 TLS/SSL 上下文 +context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) +context.load_cert_chain(certfile=server_cert, keyfile=server_key) # 加载服务端证书和私钥 +context.load_verify_locations(cafile=ca_cert) # 加载 CA 证书 +context.verify_mode = ssl.CERT_REQUIRED # 强制要求客户端提供证书 + +# 绑定服务端地址和端口 +server_socket.bind((server_ip, server_port)) +server_socket.listen(5) # 设置等待连接队列的最大长度 +print(f"服务端已启动,监听 {server_ip}:{server_port} ...") + +try: + while True: + try: + # 等待客户端连接 + client_socket, client_address = server_socket.accept() + print(f"客户端 {client_address} 已连接") + + # 将普通套接字包装为 TLS 套接字 + tls_client_socket = context.wrap_socket(client_socket, server_side=True) + + try: + # 接收消息 + message = tls_client_socket.recv(1024).decode('utf-8') + print(f"收到消息: {message}") + + # 发送响应 + response = f"服务端已收到: {message}" + tls_client_socket.sendall(response.encode('utf-8')) + print("响应已发送") + + except Exception as e: + print(f"通信错误: {e}") + + finally: + tls_client_socket.close() + print(f"客户端 {client_address} 已断开连接") + + except ssl.SSLError as e: + print(f"SSL 错误: {e}") + except Exception as e: + print(f"未知错误: {e}") + +except KeyboardInterrupt: + print("\n服务端已停止") +finally: + server_socket.close() +``` + + + +客户端: + +``` +程序启动端作为客户端,在使用前需要传入以下文件: + +ca_cert.pem + +client_key.pem + +client_cert.pem +``` + + + +#### 5.程序运行流程 + +令服务端设备与客户端设备连接同一热点,服务端先启动python脚本,客户端运行程序,输入服务端地址(上文python中的实际IP)与服务端端口(5555),点击选择按钮,选择传入的ca_cert.pem,后点击加载;点击选择按钮,选择传入的client_cert.pem,后点击加载;点击选择按钮,选择传入的client_key.pem,后点击加载,再点击连接功能,即可连接成功,后续可发送消息 + + + +## 三.双向认证流程:适用于(TLS Socket Server加密数据传输) + +介绍:在此将程序运行端作为服务端,流程类似(二.双向认证流程) + +#### 1.生成所需要的证书配置文件(文件名:server_cert.cnf、client_cert.cnf) + +输入以下命令: + +``` +vi server_cert.cnf +``` + +填入以下内容: + +``` +[ req ] +default_bits = 2048 +default_keyfile = server.key +distinguished_name = req_distinguished_name +x509_extensions = v3_req +prompt = no + +[ req_distinguished_name ] +C = XX +ST = XX +L = XX +O = XX +OU = XX +CN = 192.168.xxx.xxx +emailAddress = xxxx@example.com + +[ v3_req ] +subjectAltName = @alt_names + +[ alt_names ] +IP.1 = 192.168.xxx.xxx + + +##需要修改两个ip地址为程序启动端IP地址 +``` + +输入以下命令: + +``` +vi client_cert.cnf +``` + +填入以下内容: + +``` +[ req ] +default_bits = 2048 +default_keyfile = client.key +distinguished_name = req_distinguished_name +x509_extensions = v3_req +prompt = no + +[ req_distinguished_name ] +C = XX +ST = XX +L = XX +O = XX +OU = XX +CN = client +emailAddress = xxxx@example.com + +[ v3_req ] +``` + + + +#### 2.进行服务端与客户端的证书配置(依次执行以下命令) + +``` +#生成服务端私钥: +openssl genrsa -out server_key.pem 2048 + +#生成客户端私钥: +openssl genrsa -out client_key.pem 2048 + +#生成服务端 CSR: +openssl req -new -key server_key.pem -out server_csr.pem -config server_cert.cnf + +#生成客户端 CSR: +openssl req -new -key client_key.pem -out client_csr.pem -config client_cert.cnf + +#生成 CA 私钥: +openssl genrsa -out ca_key.pem 2048 + +#生成 CA 自签名证书: +openssl req -x509 -new -nodes -key ca_key.pem -sha256 -days 365 -out ca_cert.pem -subj "/C=CN/ST=State/L=City/O=MyCA/CN=root" + +#签署服务端CSR +openssl x509 -req -in server_csr.pem -CA ca_cert.pem -CAkey ca_key.pem -CAcreateserial -out server_cert.pem -days 365 -sha256 + +#签署客户端CSR +openssl x509 -req -in client_csr.pem -CA ca_cert.pem -CAkey ca_key.pem -CAcreateserial -out client_cert.pem -days 365 -sha256 +``` + + + +#### 3.需要使用的文件 + +``` +服务端: +server_key.pem +server_cert.pem +ca_cert.pem + +客户端: +client_key.pem +client_cert.pem +ca_cert.pem +``` + + + +#### 4.分配服务端和客户端所需要的文件(需要另外一台有python环境的设备作为服务端) + +服务端: + +``` +程序启动端作为服务端,在使用前需要传入以下文件: + +server_cert.pem + +server_key.pem + +ca_cert.pem +``` + + + +客户端: + +``` +程序启动端作为客户端,在使用前需要传入以下文件: + +ca_cert.pem + +client_key.pem + +client_cert.pem + +Client.py +``` + + + + + +脚本文件如下(需要修改IP地址为程序启动端实际IP地址): + +``` +import socket +import ssl + +# 服务端的 IP 和端口 +server_ip = "192.168.xxx.xxx" # 服务端的 IP 地址 +server_port = 5555 # 服务端的端口号 + +# 客户端证书、私钥和 CA 证书路径 +client_cert = "client_cert.pem" # 客户端证书路径 +client_key = "client_key.pem" # 客户端私钥路径 +ca_cert = "ca_cert.pem" # 用于验证服务端证书的 CA 证书 + +# 创建普通 TCP 套接字 +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +# 设置 TLS/SSL 上下文 +context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) +context.load_cert_chain(certfile=client_cert, keyfile=client_key) # 加载客户端证书和私钥 +context.load_verify_locations(cafile=ca_cert) # 加载 CA 证书 + +# 将普通套接字包装为 TLS 套接字 +wrapped_socket = context.wrap_socket(sock, server_hostname=server_ip) + +try: + # 连接服务端 + wrapped_socket.connect((server_ip, server_port)) + print(f"已连接到服务端 {server_ip}:{server_port}") + + # 发送消息 + message = "Hello, server! 这是客户端的消息。" + wrapped_socket.sendall(message.encode('utf-8')) + print("消息已发送") + + # 接收服务端的响应 + response = wrapped_socket.recv(1024) + print(f"收到服务端响应: {response.decode('utf-8')}") + +except ssl.SSLError as e: + print(f"SSL 错误: {e}") +except ConnectionError as e: + print(f"连接错误: {e}") +except Exception as e: + print(f"未知错误: {e}") + +finally: + #wrapped_socket.close() + print("连接已关闭") +``` + + + +#### 5.程序运行流程 + +令服务端设备与客户端设备连接同一热点,服务端启动该程序,输入服务端地址(上文python中的实际IP)与服务端端口(5555),程序启动段依次传入server_key.pem、server_cert.pem、ca_cert.pem,后启动上述脚本文件(作为客户端),后点击连接功能,即可连接成功 \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/Tools/Tcp_Client.py b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/Tools/Tcp_Client.py new file mode 100644 index 0000000000000000000000000000000000000000..526bf885d387af1a91488313909164664807a129 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/Tools/Tcp_Client.py @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 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. + */ + +import socket + +def tcp_client(): + # 创建一个 TCP 套接字 + client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + # 服务器地址和端口 + server_ip = '192.168.xxx.xxx' # 服务端 IP + server_port = 5555 # 服务端端口 + + try: + # 连接到服务端 + client_socket.connect((server_ip, server_port)) + print(f"Connected to server {server_ip}:{server_port}") + + while True: + # 发送消息到服务端 + message = input("Enter message to send to server: ") + client_socket.sendall(message.encode()) + print(f"Sent message: {message}") + + # 接收来自服务端的响应 + response = client_socket.recv(1024).decode() + print(f"Received from server: {response}") + + if message.lower() == "exit": + print("Exiting communication.") + break + + except Exception as e: + print(f"Error: {e}") + finally: + # 关闭连接 + client_socket.close() + print("Connection closed.") + +if __name__ == "__main__": + tcp_client() \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/Tools/Tcp_Server.py b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/Tools/Tcp_Server.py new file mode 100644 index 0000000000000000000000000000000000000000..cf3e9760aab023efaa9f564492b979b35ff38ae6 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/Tools/Tcp_Server.py @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2025 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. + */ + +import socket + +def tcp_server(): + # 创建一个 TCP 套接字 + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + # 绑定地址和端口 + host = "192.168.xxx.xxx" # 本地主机地址 + port = 5555 # 监听端口 + server_socket.bind((host, port)) + + # 开始监听 + server_socket.listen(5) + print(f"Server listening on {host}:{port}") + + try: + while True: + # 接受客户端连接 + client_socket, client_address = server_socket.accept() + print(f"Connection from {client_address}") + + # 接收客户端发送的数据 + data = client_socket.recv(1024) # 缓冲区大小为 1024 字节 + print(f"Received data: {data.decode()}") + + # 向客户端发送响应 + client_socket.send("Hello from server!".encode()) + + # 关闭客户端连接 + #client_socket.close() + except KeyboardInterrupt: + print("Server is shutting down.") + finally: + server_socket.close() + +if __name__ == "__main__": + tcp_server() \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/Tools/UDP_Client&Server.py b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/Tools/UDP_Client&Server.py new file mode 100644 index 0000000000000000000000000000000000000000..f693c9dee83435b1240ddf68966f0e39d57f948f --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/Tools/UDP_Client&Server.py @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 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. + */ + +import socket + +# 定义服务器地址和端口 +server_ip = "192.168.xxx.xxx" # 服务器 IP 地址 +server_port = 5555 # 服务器端口 + +# 创建一个 UDP 套接字 +udp_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + +# 绑定本地端口(如果需要接收响应的话) +udp_client.bind(('192.168.xxx.xxx', 5555)) # 监听本地 5555 端口 + +# 要发送的消息 +message = "Hello from client!" + +# 发送消息到服务器 +udp_client.sendto(message.encode(), (server_ip, server_port)) +print(f"Sent message: {message} to {server_ip}:{server_port}") + +# 接收服务器的响应 +data, server = udp_client.recvfrom(1024) # 接收最大 1024 字节的数据 +print(f"Received response from server: {data.decode()}") + +# 关闭客户端套接字 +udp_client.close() \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/build-profile.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ed831f1db622c015dcc8fd8326adc7b9ff1ce4fc --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/build-profile.json5 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.2(14)", + "targetSdkVersion": "5.0.2(14)", + "runtimeOS": "HarmonyOS" + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/.gitignore b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/build-profile.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..7fe9a2c63e4b25187bae5868cdd9c8b9b7fe4688 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/build-profile.json5 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "apiType": "stageMode", + "buildOption": { + "sourceOption": { + "workers": [ + './src/main/ets/workers/OneWayTlsWorker.ets', + './src/main/ets/workers/TwoWayTlsWorker.ets', + './src/main/ets/workers/MulticastWorker.ets', + './src/main/ets/workers/Tcp2OneWayTlsWorker.ets', + './src/main/ets/workers/Tcp2TwoWayTlsWorker.ets', + './src/main/ets/workers/LocalSocketWorker.ets', + './src/main/ets/workers/TcpClientWorker.ets', + './src/main/ets/workers/TcpServerWorker.ets', + './src/main/ets/workers/UdpWorker.ets', + './src/main/ets/workers/TlsServerWorker.ets' + ] + } + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/hvigorfile.ts b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4f43d54667f8327c367c8096bd08bb8c75aff54 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/obfuscation-rules.txt b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/oh-package.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c9cb6c8174858277c9b0d465a51547dcab16d5ff --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/common/Logger.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/common/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..bca2cc102cc3f335ab0710964ca0d7fe0632a9f4 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/common/Logger.ets @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; + +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0x0001; + } + + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } + + fatal(...args: string[]) { + hilog.fatal(this.domain, this.prefix, this.format, args); + } + + isLoggable(level: number) { + hilog.isLoggable(this.domain, this.prefix, level); + } +} + +export default new Logger('[Sample_Socket1]'); diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/components/TitleBar.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/components/TitleBar.ets new file mode 100644 index 0000000000000000000000000000000000000000..06d946b831879ce4cceef968ad2a5fcdf088c35c --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/components/TitleBar.ets @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025 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. + */ + +import router from '@ohos.router'; + +@Component +export default struct TitleBar { + private title: string | Resource = $r('app.string.Socket'); + private hasBackPress: boolean = false; + + build() { + Row() { + if (this.hasBackPress) { + Row() { + Image($r('app.media.back')) + .id('btnBack') + .width(12) + .height(12) + } + .height('100%') + .aspectRatio(1) + .margin({ left: 24 }) + .onClick(() => { + router.back(); + }) + } + Text(this.title) + .fontSize(20) + .fontColor(Color.Black) + .margin(this.hasBackPress ? {} : { left: 24 }) + Blank() + } + .width('100%') + .height(56) + .backgroundColor(Color.Transparent) + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/LocalSocket.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/LocalSocket.ets new file mode 100644 index 0000000000000000000000000000000000000000..11047607ef18d18cb8e04b6df5c90ee50d0eb271 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/LocalSocket.ets @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2025 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. + */ + +import { ErrorEvent, MessageEvents, worker } from '@kit.ArkTS'; // 引入 Worker +import Logger from '../common/Logger'; + + +const BUTTON_HEIGHT = 50; // 按钮高度 +const BUTTON_FONT_SIZE = 20; // 按钮字体大小 +const COMPONENT_MARGIN = 20; // 组件间隔 +const BUTTON_BORDER_RADIUS = 5; // 按钮边缘角度 +const TIMEOUT = 6000; // 设置超时时间 +const CLIENT_CONNECTION_CLOSE_TIME = 30 * 1000; // 连接关闭时间 + +// 创建 Worker 实例 +const workerPort = new worker.ThreadWorker('entry/ets/workers/LocalSocketWorker.ets'); + +function resourceToString(resource: Resource): string { + return getContext().resourceManager.getStringSync(resource); +} + +@Entry +@Component +struct SocketDemo { + @State clientStatus: string = 'Not Started'; // 客户端状态 + @State serverStatus: string = 'Not Started'; // 服务端状态 + + aboutToDisappear(): void { + workerPort.terminate(); + } + + build() { + Column() { + Text($r('app.string.Socket_Communication_Example')) + .fontSize(BUTTON_FONT_SIZE) + .margin({ bottom: COMPONENT_MARGIN }) + + Text(`${resourceToString($r('app.string.Server_Status'))}${this.serverStatus}`) + .fontSize(BUTTON_FONT_SIZE) + .margin({ bottom: COMPONENT_MARGIN }) + .id('serverStatus') + + Text(`${resourceToString($r('app.string.Client_Status'))}${this.clientStatus}`) + .fontSize(BUTTON_FONT_SIZE) + .margin({ bottom: COMPONENT_MARGIN }) + .id('clientStatus') + + Button($r('app.string.Start_Local_Socket_Server')) + .onClick(() => { + this.startLocalSocketServer(); // 让 Worker 线程执行服务器逻辑 + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: COMPONENT_MARGIN }) + .backgroundColor(Color.Green) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_BORDER_RADIUS) + .id('Start_Local_Socket_Server') + + Button($r('app.string.Start_Local_Socket_Communication')) + .onClick(() => { + this.startLocalSocket(); // 让 Worker 线程执行客户端逻辑 + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: COMPONENT_MARGIN }) + .backgroundColor(Color.Blue) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_BORDER_RADIUS) + .id('Start_Local_Socket_Communication') + } + .width('100%') + .height('100%') + .justifyContent(FlexAlign.Center) + } + + startLocalSocket() { + this.clientStatus = 'Starting'; + const sandboxPath: string = getContext(this).filesDir + '/testSocket'; + + // 发送消息到 Worker 线程执行 TCP 连接 + workerPort.postMessage({ + type: 'startLocalSocket', + address: sandboxPath, + timeout: TIMEOUT, + closeTime: CLIENT_CONNECTION_CLOSE_TIME + }); + + // 监听 Worker 返回的结果 + workerPort.onmessage = (e: MessageEvents) => { + if (e.data.type === 'clientStatus') { + this.clientStatus = e.data.status; + } + }; + + workerPort.onerror = (e: ErrorEvent) => { + Logger.error(`Error from Worker: ${e}`); + }; + } + + startLocalSocketServer() { + this.serverStatus = 'Starting'; + const sandboxPath: string = getContext(this).filesDir + '/testSocket'; + + // 发送消息到 Worker 线程执行服务器监听 + workerPort.postMessage({ + type: 'startLocalSocketServer', + address: sandboxPath + }); + + // 监听 Worker 返回的结果 + workerPort.onmessage = (e: MessageEvents) => { + if (e.data.type === 'serverStatus') { + this.serverStatus = e.data.status; + } + }; + + workerPort.onerror = (e: ErrorEvent) => { + Logger.error(`Error from Worker: ${e}`); + }; + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/Multicast.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/Multicast.ets new file mode 100644 index 0000000000000000000000000000000000000000..f6bc2bee3232a65a1efd5fa132eadb73641a6c1b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/Multicast.ets @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2025 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. + */ + +import { socket } from '@kit.NetworkKit'; +import Logger from '../common/Logger'; +import { ErrorEvent, MessageEvents, worker } from '@kit.ArkTS'; // 引入 worker + +// 创建 Worker 实例 +const workerPort = new worker.ThreadWorker('entry/ets/workers/MulticastWorker.ets'); + +// 按钮相关常量 +const BUTTON_HEIGHT = 50; //按钮高度 +const BUTTON_FONT_SIZE = 20; //按钮字体大小 +const COMPONENT_MARGIN = 20; // 组件间隔 +const BUTTON_BORDER_RADIUS = 5; // 按钮边缘角度 + +export interface Message { + type: string; + status: string; + message?: string; // message 是可选的 +} + +function resourceToString(resource: Resource): string { + return getContext().resourceManager.getStringSync(resource); +} + +@Entry +@Component +struct TCPSocketDemo { + @State startMulticast: string = ''; + + aboutToDisappear(): void { + workerPort.terminate(); + } + + build() { + Column() { + Text(`${resourceToString($r('app.string.Start_multicast_communication'))}${this.startMulticast}`) + .fontSize(BUTTON_FONT_SIZE) + .margin({ top: COMPONENT_MARGIN }) + .id('multicast_communication') + + Button($r('app.string.StartMulticast')) + .onClick(() => { + this.startMulticastCommunication(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: COMPONENT_MARGIN }) + .backgroundColor(Color.Orange) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_BORDER_RADIUS) + .id('StartMulticast') + } + .width('100%') + .height('100%') + .justifyContent(FlexAlign.Center) + } + + startMulticastCommunication() { + this.startMulticast = 'In Progress'; + // 定义多播配置数据 + const multicastData: socket.NetAddress = { + address: '224.0.0.1', // 地址IP + port: 32123, // 端口号 + family: 1 + }; + + const message: Message = { + type: 'startMulticast', + status: 'Starting multicast', + message: JSON.stringify(multicastData) + }; + + workerPort.postMessage(message); + + workerPort.onmessage = (e: MessageEvents) => { + const response: Message = e.data; + if (response.type === 'multicastStatus') { + this.startMulticast = response.status; + } + if (response.type === 'multicastMessage') { + Logger.info(`Multicast message: ${response.message}`); + } + }; + + workerPort.onerror = (e: ErrorEvent) => { + Logger.error(`Error from Worker: ${e}`); + }; + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/OneWayTls.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/OneWayTls.ets new file mode 100644 index 0000000000000000000000000000000000000000..9ffb6af0f2df0ff56885165590fc8dedd42c4d52 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/OneWayTls.ets @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2025 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. + */ + +import { MessageEvents, worker } from '@kit.ArkTS'; +import wifiManager from '@ohos.wifiManager'; +import picker from '@ohos.file.picker'; + +const workerPort = new worker.ThreadWorker('entry/ets/workers/OneWayTlsWorker.ets'); + +interface TlsOneWayMessage { + type: string; + fileUri?: string; // 用于传递 CA 文件路径 + serverIp?: string; + serverPort?: number; + ca?: string; + message?: string; + caContent?: string; +} + +let ipNum = wifiManager.getIpInfo().ipAddress; +let localIp = (ipNum >>> 24) + '.' + (ipNum >> 16 & 0xFF) + '.' + (ipNum >> 8 & 0xFF) + '.' + (ipNum & 0xFF); +let caFileUri = ''; + +@Entry +@Component +struct Index { + @State msgHistory: string = ''; + @State sendMsg: string = ''; + @State serverIp: string = ''; // 服务端IP地址 + @State serverPort: number = 5555; // 服务端端口号 + @State canLoad: boolean = false; + @State canConnect: boolean = false; + @State canSend: boolean = false; + @State ca: string = ''; + scroller: Scroller = new Scroller(); + + aboutToDisappear(): void { + workerPort.terminate(); + } + + build() { + Row() { + Column() { + Text($r('app.string.TLS_Communication_Example')) + .fontSize(14) + .fontWeight(FontWeight.Bold) + .width('100%') + .textAlign(TextAlign.Center) + .padding(10) + + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Local_IP_Address')) + .width(100) + .fontSize(14) + .flexGrow(0) + Text(localIp) + .width(110) + .fontSize(12) + .flexGrow(1) + }.width('100%') + .padding(10) + + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Server_Address')) + .fontSize(14) + .width(90) + .flexGrow(1) + + TextInput({ text: this.serverIp }) + .onChange((value) => { + this.serverIp = value; + }) + .width(110) + .fontSize(12) + .flexGrow(4) + + Text(':') + .width(5) + .flexGrow(0) + + TextInput({ text: this.serverPort.toString() }) + .type(InputType.Number) + .onChange((value) => { + this.serverPort = parseInt(value); + }) + .fontSize(12) + .flexGrow(2) + .width(50) + } + .width('100%') + .padding(10) + + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.CA')) + .fontSize(14) + .width(90) + .flexGrow(1) + + Button($r('app.string.Select')) + .onClick(() => { + this.selectCA(); + }) + .width(70) + .fontSize(14) + .flexGrow(0) + + Button($r('app.string.Load')) + .onClick(() => { + this.loadCA(); + }) + .enabled(this.canLoad) + .width(70) + .fontSize(14) + .flexGrow(0) + + Button($r('app.string.Connect')) + .onClick(() => { + this.connect2Server(); + }) + .enabled(this.canConnect) + .width(70) + .fontSize(14) + .flexGrow(0) + } + .width('100%') + .padding(10) + + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + TextArea({ text: this.ca }).onChange((value) => { + this.ca = value; + }) + .flexGrow(1) + .height(200) + } + .width('100%') + .padding(10) + + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + TextInput({ placeholder: $r('app.string.Enter_The_Message_To_Send') }).onChange((value) => { + this.sendMsg = value; + }) + .width(200) + .flexGrow(1) + + Button($r('app.string.Send')) + .enabled(this.canSend) + .width(70) + .fontSize(14) + .flexGrow(0) + .onClick(() => { + this.sendMsg2Server(); + }) + } + .width('100%') + .padding(10) + + Scroll(this.scroller) { + Text(this.msgHistory) + .textAlign(TextAlign.Start) + .padding(10) + .width('100%') + .backgroundColor(0xeeeeee) + } + .align(Alignment.Top) + .backgroundColor(0xeeeeee) + .height(300) + .flexGrow(1) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.On) + .scrollBarWidth(20) + } + .width('100%') + .justifyContent(FlexAlign.Start) + .height('100%') + } + .height('100%') + } + + // 发送消息到服务端 + sendMsg2Server() { + if (!this.sendMsg.trim()) { + return; + } + workerPort.postMessage({ type: 'sendMessage', message: this.sendMsg }); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsOneWayMessage = e.data; + + if (response.type === 'sendMessageSuccess') { + this.msgHistory += 'Me: ' + response.message + getCurrentTimeString() + '\r\n'; + } else if (response.type === 'sendMessageFailure') { + this.msgHistory += 'Send failed: ' + response.message + '\r\n'; + } + } + } + + // 选择 CA 文件,并通过 worker 加载 CA 证书 + selectCA() { + let documentPicker = new picker.DocumentViewPicker(); + documentPicker.select().then((result) => { + if (result.length > 0) { + caFileUri = result[0]; + this.msgHistory += 'select file: ' + caFileUri + '\r\n'; + this.canLoad = true; + } + }).catch((e: Error) => { + this.msgHistory += 'DocumentViewPicker.select failed ' + e.message + '\r\n'; + }); + } + + loadCA() { + if (!caFileUri) { + this.msgHistory += 'Error: CA file URI is empty.\r\n'; + return; + } + + workerPort.postMessage({ type: 'loadCA', fileUri: caFileUri }); + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsOneWayMessage = e.data; // 确保类型明确 + + if (response.type === 'caLoaded' && response.caContent) { + this.ca = response.caContent; + this.msgHistory += 'CA file loaded successfully.\r\n'; + this.canConnect = true; + } else if (response.type === 'error') { + this.msgHistory += 'Error loading CA file: ' + response.message + '\r\n'; + } + } + } + + // 连接服务端 + connect2Server() { + if (!this.ca) { + this.msgHistory += 'Error: CA certificate is empty. Please load CA before connecting.\r\n'; + return; + } + // 服务端地址 + workerPort.postMessage({ + type: 'connectServer', + serverIp: this.serverIp, + serverPort: this.serverPort, + ca: this.ca + }); + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsOneWayMessage = e.data; // 确保类型明确 + + if (response.type === 'connectSuccess') { + this.msgHistory += 'Connected to server successfully.\r\n'; + this.canSend = true; + } else if (response.type === 'connectFailure') { + this.msgHistory += 'Failed to connect to server: ' + response.message + '\r\n'; + } + }; + } +} + +// 同步获取当前时间的字符串形式 +function getCurrentTimeString() { + const now = new Date(); + const hours = now.getHours().toString().padStart(2, '0'); + const minutes = now.getMinutes().toString().padStart(2, '0'); + const seconds = now.getSeconds().toString().padStart(2, '0'); + + return `[${hours}:${minutes}:${seconds}]`; +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/Tcp2OneWayTls.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/Tcp2OneWayTls.ets new file mode 100644 index 0000000000000000000000000000000000000000..fbbc0deee89117efcbc57c5dd821e207fdca5197 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/Tcp2OneWayTls.ets @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2025 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. + */ + +import picker from '@ohos.file.picker'; +import wifiManager from '@ohos.wifiManager'; +import { MessageEvents, worker } from '@kit.ArkTS'; + +export interface TlsOneWayMessage { + type: string; // 消息类型,如 'loadCA', 'connectToServer', 'sendMessage' + fileUri?: string; // 文件路径,用于传递 CA 文件路径等 + serverIp?: string; // 服务器的 IP 地址 + serverPort?: number; // 服务器的端口 + ca?: string; // CA 证书内容 + message?: string; // 要发送的消息 + caContent?: string; // 加载后的 CA 证书内容 +} + +let ipNum = wifiManager.getIpInfo().ipAddress; +let localIp = (ipNum >>> 24) + '.' + (ipNum >> 16 & 0xFF) + '.' + (ipNum >> 8 & 0xFF) + '.' + (ipNum & 0xFF); +let caFileUri = ''; + +// 创建 Worker 实例 +const workerPort = new worker.ThreadWorker('entry/ets/workers/Tcp2OneWayTlsWorker.ets'); + +@Entry +@Component +struct SingleTLSAuth { + @State msgHistory: string = ''; // 通信历史记录 + @State sendMsg: string = ''; // 要发送的消息 + @State serverIp: string = ''; // 服务端IP + @State serverPort: number = 5555; // 服务端端口 + @State ca: string = ''; // CA 证书内容 + @State canLoad: boolean = false; // 是否可以加载证书 + @State canConnect: boolean = false; // 是否可以连接 + @State canSend: boolean = false; // 是否可以发送消息 + scroller: Scroller = new Scroller(); + + aboutToDisappear(): void { + workerPort.terminate(); + } + + build() { + Row() { + Column() { + Text($r('app.string.Tcp2OneWayTls')) + .fontSize(14) + .fontWeight(FontWeight.Bold) + .width('100%') + .textAlign(TextAlign.Center) + .padding(10) + + // 显示本地 IP 地址 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Local_IP_Address')) + .fontSize(14) + .width(100) + Text(localIp) + .fontSize(12) + .width(150) + } + .width('100%') + .padding(10) + + // 服务端 IP 和端口输入框 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Server_Address')) + .fontSize(14) + .width(100) + TextInput({ text: this.serverIp }) + .onChange((value) => { + this.serverIp = value; + }) + .fontSize(12) + .width(150) + + Text(':') + .width(5) + TextInput({ text: this.serverPort.toString() }) + .type(InputType.Number) + .onChange((value) => { + this.serverPort = parseInt(value); + }) + .fontSize(12) + .width(80) + } + .width('100%') + .padding(10) + + // 选择和加载 CA 证书 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.CA_File')) + .fontSize(14) + .width(120) + Button($r('app.string.Select')) + .onClick(() => { + this.selectCA(); + }) + .width(70) + .fontSize(14) + Button($r('app.string.Load')) + .onClick(() => { + this.loadCA(); + }) + .enabled(this.canLoad) + .width(70) + .fontSize(14) + Button($r('app.string.Connect')) + .onClick(() => { + this.connectToServer(); + }) + .enabled(this.canConnect) + .width(70) + .fontSize(14) + } + .width('100%') + .padding(10) + + // 显示加载的 CA 内容 + TextArea({ text: this.ca }) + .onChange((value) => { + this.ca = value; + }) + .fontSize(12) + .height(150) + .padding(10) + + // 发送消息输入框 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + TextInput({ placeholder: $r('app.string.Enter_Message') }) + .onChange((value) => { + this.sendMsg = value; + }) + .fontSize(12) + .width(200) + Button($r('app.string.Send')) + .onClick(() => { + this.sendMessage(); + }) + .enabled(this.canSend) + .width(70) + .fontSize(14) + } + .width('100%') + .padding(10) + + // 消息历史展示 + Scroll(this.scroller) { + Text(this.msgHistory) + .textAlign(TextAlign.Start) + .padding(10) + .width('100%') + .backgroundColor(0xeeeeee) + } + .align(Alignment.Top) + .backgroundColor(0xeeeeee) + .height(300) + .flexGrow(1) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.On) + .scrollBarWidth(20) + } + .width('100%') + .justifyContent(FlexAlign.Start) + .height('100%') + } + .height('100%') + } + + // 选择 CA 文件 + selectCA() { + let documentPicker = new picker.DocumentViewPicker(); + documentPicker.select().then((result) => { + if (result.length > 0) { + caFileUri = result[0]; + this.msgHistory += `CA selected: ${caFileUri}\n`; + this.canLoad = true; + } + }).catch((err: Error) => { + this.msgHistory += `Fail to select CA: ${err.message}\n`; + }); + } + + // 加载 CA 文件内容 + loadCA() { + if (!caFileUri) { + this.msgHistory += 'Error: CA file URI is empty.\r\n'; + return; + } + + workerPort.postMessage({ + type: 'loadCA', + fileUri: caFileUri + }); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsOneWayMessage = e.data; + + if (response.type === 'caLoaded') { + if (response.caContent) { + this.ca = response.caContent; // 确保 caContent 存在并赋值给 this.ca + this.canConnect = true; + this.msgHistory += 'CA Loaded\n'; + } else { + this.msgHistory += 'CA is empty\n'; // 如果没有加载 CA 内容,进行错误提示 + } + } else { + this.msgHistory += `Load CA file: ${response.message}\n`; + } + }; + } + + // 连接到服务器 + connectToServer() { + workerPort.postMessage({ + type: 'connectToServer', + serverIp: this.serverIp, + serverPort: this.serverPort, + ca: this.ca + }); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsOneWayMessage = e.data; + if (response.type === 'connectSuccess') { + this.msgHistory += 'TCP connect success\n'; + this.upgradeToTLSSocket(); + } else { + this.msgHistory += `TCP connect fail: ${response.message}\n`; + } + }; + } + + // 将 TCP Socket 升级为 TLS Socket + upgradeToTLSSocket() { + workerPort.postMessage({ + type: 'upgradeToTLS', + serverIp: this.serverIp, + serverPort: this.serverPort, + ca: this.ca + }); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsOneWayMessage = e.data; + if (response.type === 'tlsConnectSuccess') { + this.msgHistory += 'TLS connect success\n'; + this.canSend = true; + } else { + this.msgHistory += `TLS connect fail: ${response.message}\n`; + } + }; + } + + // 发送消息到服务器 + sendMessage() { + workerPort.postMessage({ + type: 'sendMessage', + message: this.sendMsg + }); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsOneWayMessage = e.data; + if (response.type === 'sendMessageSuccess') { + this.msgHistory += `Me: ${response.message}\n`; + } else if (response.type === 'sendMessageFailure') { + this.msgHistory += `Send fail: ${response.message}\n`; + } else if (response.type === 'connectionClosed') { + this.msgHistory += `TLS connection closed\n`; + this.canSend = false; // 连接关闭后,禁用发送按钮 + } + }; + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/Tcp2TwoWayTls.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/Tcp2TwoWayTls.ets new file mode 100644 index 0000000000000000000000000000000000000000..ae2cf53e43905503c5cac25a172e1a6d45d52cb9 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/Tcp2TwoWayTls.ets @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2025 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. + */ + +import { MessageEvents, worker } from '@kit.ArkTS'; +import wifiManager from '@ohos.wifiManager'; +import picker from '@ohos.file.picker'; + +// 创建 Worker 实例 +const workerPort = new worker.ThreadWorker('entry/ets/workers/Tcp2TwoWayTlsWorker.ets'); + +interface TlsTwoWayMessage { + type: string; + fileUri?: string; // 用于传递文件路径 + serverIp?: string; // 服务器 IP 地址 + serverPort?: number; // 服务器端口 + ca?: string; // CA 证书内容 + cert?: string; // 客户端证书内容 + privateKey?: string; // 客户端私钥内容 + message?: string; // 要发送的消息 + caContent?: string; // 从文件中读取的 CA 内容 + certContent?: string; // 从文件中读取的证书内容 + privateKeyContent?: string; // 从文件中读取的私钥内容 + content?: string; // 通用的文件加载返回内容(可选) +} + +// 全局状态 +let ipNum = wifiManager.getIpInfo().ipAddress; +let localIp = (ipNum >>> 24) + '.' + (ipNum >> 16 & 0xFF) + '.' + (ipNum >> 8 & 0xFF) + '.' + (ipNum & 0xFF); +let caFileUri = ''; +let certFileUri = ''; +let keyFileUri = ''; + +@Entry +@Component +struct Index { + @State msgHistory: string = ''; + @State sendMsg: string = ''; + @State serverIp: string = ''; // 服务端IP地址 + @State serverPort: number = 5555; // 服务端端口号 + @State canLoad: boolean = false; + @State canConnect: boolean = false; + @State canSend: boolean = false; + @State ca: string = ''; + @State cert: string = ''; + @State privateKey: string = ''; + scroller: Scroller = new Scroller(); + + aboutToDisappear(): void { + workerPort.terminate(); + } + + build() { + Row() { + Column() { + Text($r('app.string.Tcp2TwoWayTls')) + .fontSize(14) + .fontWeight(FontWeight.Bold) + .width('100%') + .textAlign(TextAlign.Center) + .padding(10) + + // 本地 IP 地址 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Local_IP_Address')) + .width(100) + .fontSize(14) + .flexGrow(0) + Text(localIp) + .width(110) + .fontSize(12) + .flexGrow(1) + }.width('100%') + .padding(10) + + // 服务端 IP 和端口输入框 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Server_Address')) + .fontSize(14) + .width(90) + .flexGrow(1) + TextInput({ text: this.serverIp }) + .onChange((value) => { + this.serverIp = value; + }) + .width(110) + .fontSize(12) + .flexGrow(4) + Text(':') + .width(5) + .flexGrow(0) + TextInput({ text: this.serverPort.toString() }) + .type(InputType.Number) + .onChange((value) => { + this.serverPort = parseInt(value); + }) + .fontSize(12) + .flexGrow(2) + .width(50) + }.width('100%') + .padding(10) + + // 选择和加载 CA 文件 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.CA_File')) + .fontSize(14) + .width(90) + .flexGrow(1) + Button($r('app.string.Select')) + .onClick(() => { + this.selectCA(); + }) + .width(70) + .fontSize(14) + Button($r('app.string.Load')) + .onClick(() => { + this.loadCA(); + }) + .enabled(this.canLoad) + .width(70) + .fontSize(14) + }.width('100%') + .padding(10) + + // 选择和加载客户端证书 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Client_Cert')) + .fontSize(14) + .width(90) + .flexGrow(1) + Button($r('app.string.Select')) + .onClick(() => { + this.selectCert(); + }) + .width(70) + .fontSize(14) + Button($r('app.string.Load')) + .onClick(() => { + this.loadCert(); + }) + .enabled(this.canLoad) + .width(70) + .fontSize(14) + }.width('100%') + .padding(10) + + // 选择和加载客户端私钥 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Client_Key')) + .fontSize(14) + .width(90) + .flexGrow(1) + Button($r('app.string.Select')) + .onClick(() => { + this.selectKey(); + }) + .width(70) + .fontSize(14) + Button($r('app.string.Load')) + .onClick(() => { + this.loadKey(); + }) + .enabled(this.canLoad) + .width(70) + .fontSize(14) + }.width('100%') + .padding(10) + + // 连接和输入发送消息 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Button($r('app.string.Connect')) + .onClick(() => { + this.connect2Server(); + }) + .enabled(!!this.ca && !!this.cert && !!this.privateKey) + .width(70) + .fontSize(14) + }.width('100%') + .padding(10) + + // 发送消息输入框 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + TextInput({ text: this.sendMsg, placeholder: $r('app.string.Enter_Message') }) + .onChange((value) => { + this.sendMsg = value; + }) + .width(200) + .fontSize(14) + .flexGrow(1) + Button($r('app.string.Send')) + .enabled(this.canSend) + .onClick(() => { + this.sendMsg2Server(); + }) + .width(70) + .fontSize(14) + }.width('100%') + .padding(10) + + // 消息展示区域 + Scroll(this.scroller) { + Text(this.msgHistory) + .textAlign(TextAlign.Start) + .padding(10) + .width('100%') + .backgroundColor(0xeeeeee) + } + .align(Alignment.Top) + .backgroundColor(0xeeeeee) + .height(300) + .flexGrow(1) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.On) + } + .width('100%') + .justifyContent(FlexAlign.Start) + .height('100%') + } + .height('100%') + } + + // 发送消息 + sendMsg2Server() { + if (!this.sendMsg.trim()) { + return; + } + workerPort.postMessage({ type: 'sendMessage', message: this.sendMsg }); + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsTwoWayMessage = e.data; + if (response.type === 'sendMessageSuccess') { + this.msgHistory += `Me: ${response.message} ${getCurrentTimeString()}\r\n`; + } else if (response.type === 'sendMessageFailure') { + this.msgHistory += `Send fail: ${response.message}\r\n`; + } + } + } + + // 选择文件:CA、证书、私钥 + selectCA() { + this.selectFile((file: string) => { + caFileUri = file; + this.canLoad = true; + }) + } + + selectCert() { + this.selectFile((file: string) => { + certFileUri = file; + this.canLoad = true; + }) + } + + selectKey() { + this.selectFile((file: string) => { + keyFileUri = file; + this.canLoad = true; + }) + } + + selectFile(callback: (file: string) => void) { + let pickerInstance = new picker.DocumentViewPicker(); + pickerInstance.select().then((result) => { + if (result.length > 0) { + callback(result[0]); + } + }).catch((e: Error) => { + this.msgHistory += `Select fail: ${e.message}\r\n`; + }); + } + + checkAllFilesLoaded() { + if (caFileUri && certFileUri && keyFileUri) { + this.canSend = true; + } + } + + // 从文件加载内容 + loadCA() { + this.loadFile(caFileUri, (content: string) => { + this.ca = content; + }) + } + + loadCert() { + this.loadFile(certFileUri, (content: string) => { + this.cert = content; + }) + } + + loadKey() { + this.loadFile(keyFileUri, (content: string) => { + this.privateKey = content; + }) + } + + // 加载文件内容并设置状态 + loadFile(fileUri: string, callback: (content: string) => void) { + workerPort.postMessage({ type: 'loadFile', fileUri: fileUri }); + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsTwoWayMessage = e.data; + if (response.type === 'fileLoaded') { + if (response.caContent) { + this.msgHistory += 'CA file loaded successfully.\r\n'; + callback(response.caContent); + } else if (response.certContent) { + this.msgHistory += 'CERT file loaded successfully.\r\n'; + callback(response.certContent); + } else if (response.privateKeyContent) { + this.msgHistory += 'KEY file loaded successfully.\r\n'; + callback(response.privateKeyContent); + } + this.checkAllFilesLoaded() + } else { + this.msgHistory += `Load fail: ${response.message}\r\n`; + } + } + } + + // 连接服务端 + connect2Server() { + workerPort.postMessage({ + type: 'connectServer', + serverIp: this.serverIp, + serverPort: this.serverPort, + ca: this.ca, + cert: this.cert, + privateKey: this.privateKey + }); + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsTwoWayMessage = e.data; + if (response.type === 'connectSuccess') { + this.msgHistory += 'Connect success\r\n'; + this.canSend = true; + } else if (response.type === 'connectFailure') { + this.msgHistory += `Connect fail: ${response.message}\r\n`; + } + } + } +} + +// 获取当前时间的字符串 +function getCurrentTimeString() { + const now = new Date(); + return `[${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}]`; +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/TcpClient.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/TcpClient.ets new file mode 100644 index 0000000000000000000000000000000000000000..b792b9109014840e7e89cb9a28160b8f5bf81b7f --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/TcpClient.ets @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2025 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. + */ + +import { MessageEvents, worker } from '@kit.ArkTS'; +import wifiManager from '@ohos.wifiManager'; +import Logger from '../common/Logger'; + +const workerPort = new worker.ThreadWorker('entry/ets/workers/TcpClientWorker.ets'); + +export interface TcpMessage { + type: string; + message?: string; + clientIp?: string, + clientPort?: number; + serverIp?: string; + serverPort?: number; +} + +interface FONT_SIZE { + SMALL: number; + MEDIUM: number; + LARGE: number; +} + +const FONT: FONT_SIZE = { + SMALL: 16, // 小字体大小 + MEDIUM: 20, // 中等字体大小 + LARGE: 50, // 大字体大小 +}; + +interface MARGIN_SIZE { + SMALL: number; + MEDIUM: number; + LARGE: number; +} + +const MARGIN: MARGIN_SIZE = { + SMALL: 10, // 小间隔距离 + MEDIUM: 20, // 中等间隔距离 + LARGE: 30, // 大间隔距离 +}; + +const BUTTON_HEIGHT: number = 50; // 按钮高度 + +let ipNum = wifiManager.getIpInfo().ipAddress; +let clientIp = (ipNum >>> 24) + '.' + (ipNum >> 16 & 0xFF) + '.' + (ipNum >> 8 & 0xFF) + '.' + (ipNum & 0xFF); + +@Entry +@Component +struct Index { + @State message: string = ''; // 用于展示发送的消息 + @State logMessage: string = ''; // 用于展示接收到的消息 + @State clientIp: string = clientIp; // 客户端IP + @State clientPort: number = 5555; // 客户端端口号 + @State serverIp: string = ''; // 输入的服务端IP + @State serverPort: number = 5555; // 服务端端口号 + scroller: Scroller = new Scroller(); + // 客户端Socket对象 + @State msgHistory: string = ''; + + aboutToDisappear(): void { + workerPort.terminate(); + } + + // 构建界面 + build() { + Column() { + Column() { + Row({ space: 12 }) { // Row布局使输入框水平排列 + // Server IP 输入框 + Column() { + Text($r('app.string.Enter_Server_Ip')) + .fontSize(FONT.MEDIUM) + .fontColor(Color.Gray) + .textAlign(TextAlign.Start) + .width('100%') + TextInput() + .width('100%') + .onChange((value) => { + this.serverIp = value; + }) + } + .width('50%') // 控制占据的宽度,可以调整百分比 + + // Server Port 输入框 + Column() { + Text($r('app.string.Enter_Server_Port')) + .fontSize(FONT.MEDIUM) + .fontColor(Color.Gray) + .textAlign(TextAlign.Start) + .width('100%') + TextInput() + .width('100%') + .onChange((value) => { + this.serverPort = parseInt(value); + }) + } + .width('50%') // 控制占据的宽度,可以调整百分比 + } + .width('80%') + .margin({ + bottom: MARGIN.MEDIUM + }) + .padding({ + left: MARGIN.SMALL, + right: MARGIN.SMALL + }) + } + + // 消息输入框 + Column() { + Text($r('app.string.Send')) + .fontSize(FONT.MEDIUM) + .fontColor(Color.Gray) + .textAlign(TextAlign.Start) + .width('100%') + TextInput() + .width('100%') + .onChange((value) => { + this.message = value; + }) + } + .width('80%') + .margin({ + bottom: MARGIN.MEDIUM + }) + .padding({ + left: MARGIN.SMALL, + right: MARGIN.SMALL + }) + + // 连接、关闭按钮 + Column({ space: 12 }) { + Button($r('app.string.Connect')) + .onClick(() => { + this.connectToServer(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .fontColor(Color.White) + .fontSize(FONT.MEDIUM) + + Button($r('app.string.Close_Connection')) + .onClick(() => { + this.closeConnection(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .fontColor(Color.White) + .fontSize(FONT.MEDIUM) + } + + // 展示历史消息记录 + Scroll(this.scroller) { + Text(this.msgHistory) + .textAlign(TextAlign.Start) + .padding(10) + .width('100%') + .height('100%') + .backgroundColor(0xeeeeee) + } + .width('80%') + .height('60%') + .margin({ top: MARGIN.SMALL }) + } + .width('100%') + .height('100%') + .justifyContent(FlexAlign.Center) + } + + // 连接到服务端 + async connectToServer() { + + if (this.clientIp === '' || !this.clientPort || this.serverIp === '' || !this.serverPort) { + this.msgHistory += 'Some required fields are missing.' + '\r\n'; + Logger.error('Some required fields are missing.'); + return; + } + + let tcpMessage: TcpMessage = { + type: 'sendMessage', + clientIp: this.clientIp, + clientPort: this.clientPort, + serverIp: this.serverIp, + serverPort: this.serverPort, + message: this.message, + }; + + workerPort.postMessage( + tcpMessage + ); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TcpMessage = e.data; + + if (response.type === 'sendMessageSuccess') { + this.msgHistory += 'sendMessageSuccess' + '\r\n'; + this.msgHistory += 'Me: ' + response.message + getCurrentTimeString() + '\r\n'; + } + if (response.type === 'sendMessageFailure') { + this.msgHistory += 'Send failed: ' + response.message + '\r\n'; + } + } + } + + // 关闭连接 + closeConnection() { + let tcpMessage: TcpMessage = { + type: 'disConnect', + }; + workerPort.postMessage( + tcpMessage + ); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TcpMessage = e.data; + if (response.type === 'Connection closed') { + this.msgHistory += 'Connection closed.\n'; + } else if (response.type === 'Close connection failed') { + this.msgHistory += `Close connection failed`; + } + } + } +} + +function getCurrentTimeString() { + const now = new Date(); + // 如果转换后的字符串长度小于2,则在字符串前添加'0',直到字符串长度达到2 + const hours = now.getHours().toString().padStart(2, '0'); + const minutes = now.getMinutes().toString().padStart(2, '0'); + const seconds = now.getSeconds().toString().padStart(2, '0'); + return `[${hours}:${minutes}:${seconds}]`; +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/TcpServer.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/TcpServer.ets new file mode 100644 index 0000000000000000000000000000000000000000..ceade866ff1d9ab02eb8bd8536498306c9cfcd72 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/TcpServer.ets @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2025 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. + */ + +import { TcpMessage } from './TcpClient'; +import { MessageEvents, worker } from '@kit.ArkTS'; +import Logger from '../common/Logger'; + +const workerPort = new worker.ThreadWorker('entry/ets/workers/TcpServerWorker.ets'); + +interface FONT_SIZE { + SMALL: number; + MEDIUM: number; + LARGE: number; +} + +const FONT: FONT_SIZE = { + SMALL: 16, // 小字体大小 + MEDIUM: 20, // 中等字体大小 + LARGE: 50, // 大字体大小 +}; + +interface MARGIN_SIZE { + SMALL: number; + MEDIUM: number; + LARGE: number; +} + +const MARGIN: MARGIN_SIZE = { + SMALL: 10, // 小间隔距离 + MEDIUM: 20, // 中等间隔距离 + LARGE: 30, // 大间隔距离 +}; + +const BUTTON_HEIGHT: number = 50; // 按钮高度 + +@Entry +@Component +struct Index { + @State message: string = ''; // 用于展示发送的消息 + @State logMessage: string = ''; // 用于展示接收到的消息 + @State serverIp: string = ''; // 服务端IP地址,默认值 + @State serverPort: number = 5555; // 服务端端口,默认值 + @State connectionStatus: string = ''; // 显示连接状态 + scroller: Scroller = new Scroller(); + // 消息记录 + @State msgHistory: string = ''; + + aboutToDisappear(): void { + workerPort.terminate(); + } + + // 构建界面 + build() { + Column() { + // 显示服务端IP地址的输入框 + Column() { + Text($r('app.string.Enter_Server_Ip')) + .fontSize(FONT.MEDIUM) + .fontColor(Color.Gray) + .textAlign(TextAlign.Start) + .margin({ bottom: MARGIN.MEDIUM }); + TextInput() + .width('80%') + .onChange((value) => { + this.serverIp = value; + }) + } + .margin({ bottom: MARGIN.MEDIUM }) + .padding({ + left: MARGIN.SMALL, + right: MARGIN.SMALL + }) + + // 显示服务端Port的输入框 + Column() { + Text($r('app.string.Enter_Server_Port')) + .fontSize(FONT.MEDIUM) + .fontColor(Color.Gray) + .textAlign(TextAlign.Start) + .margin({ bottom: MARGIN.MEDIUM }); + TextInput() + .width('80%') + .onChange((value) => { + this.serverPort = parseInt(value); + }) + } + .margin({ bottom: MARGIN.MEDIUM }) + .padding({ + left: MARGIN.SMALL, + right: MARGIN.SMALL + }) + + // 启动服务器按钮 + Button($r('app.string.Start_Server')) + .onClick(() => { + this.startServer(); + }) + .margin({ bottom: MARGIN.SMALL }) + .width('80%') + .height(BUTTON_HEIGHT) + .fontColor(Color.White) + .fontSize(FONT.MEDIUM) + + // 发送消息按钮 + Button($r('app.string.Send')) + .onClick(() => { + this.sendMessage(); + }) + .margin({ bottom: MARGIN.SMALL }) + .width('80%') + .height(BUTTON_HEIGHT) + .fontColor(Color.White) + .fontSize(FONT.MEDIUM) + + // 关闭连接按钮 + Button($r('app.string.Close_Connection')) + .onClick(() => { + this.closeConnection(); + }) + .margin({ bottom: MARGIN.SMALL }) + .width('80%') + .height(BUTTON_HEIGHT) + .fontColor(Color.White) + .fontSize(FONT.MEDIUM) + + // 显示连接状态 + Text(this.connectionStatus) + .fontSize(FONT.SMALL) + .fontColor(Color.Green) + .margin({ top: MARGIN.SMALL }) + + // 输入消息框 + Column() { + Text($r('app.string.Enter_Message')) + .fontSize(FONT.MEDIUM) + .fontColor(Color.Gray) + .margin({ bottom: MARGIN.SMALL }) + TextInput() + .width('80%') + .onChange((value) => { + this.message = value; + }) + } + .margin({ top: MARGIN.MEDIUM }) + + // 消息记录框 + Column() { + Text($r('app.string.Message_History')) + .fontSize(FONT.MEDIUM) + .fontColor(Color.Gray) + .margin({ bottom: MARGIN.SMALL }) + Scroll(this.scroller) { + Text(this.msgHistory) + .textAlign(TextAlign.Start) + .padding(10) + .width('100%') + .height('100%') + .backgroundColor(0xeeeeee) + } + .width('80%') + .height(200) // 消息记录区域 + .margin({ top: MARGIN.SMALL }) + } + } + .width('100%') + .height('100%') + .justifyContent(FlexAlign.Center) + } + + // 启动服务器 + startServer() { + if (!this.serverIp || !this.serverPort) { + Logger.error('Some required fields are missing.'); + this.msgHistory += 'Some required fields are missing.' + '\r\n'; + return; + } + + let tcpMessage: TcpMessage = { + type: 'startServer', + serverIp: this.serverIp, + serverPort: this.serverPort, + message: this.message, + }; + + Logger.info(tcpMessage.serverIp! + tcpMessage.serverPort); + workerPort.postMessage( + tcpMessage + ); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TcpMessage = e.data; + + if (response.type === 'listen success') { + this.msgHistory += 'listen success' + getCurrentTimeString() + '\r\n'; + } + if (response.type === 'client connected') { + this.msgHistory += 'client connected' + getCurrentTimeString() + '\r\n'; + } + if (response.type === 'client on close success') { + this.msgHistory += 'Client disconnected\n'; + } + if (response.type === 'received message--') { + this.msgHistory += `Received message: ${response.message}` + getCurrentTimeString() + '\r\n'; + } else if (response.type === 'listen fail') { + this.msgHistory += 'listen fail' + '\r\n'; + } + } + } + + // 发送消息到服务端 + sendMessage() { + if (!this.message) { + Logger.error('Some required fields are missing.'); + this.msgHistory += 'Some required fields are missing.' + '\r\n'; + return; + } + + let tcpMessage: TcpMessage = { + type: 'sendMessage', + message: this.message, + }; + + workerPort.postMessage( + tcpMessage + ); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TcpMessage = e.data; + + if (response.type === 'send success') { + this.msgHistory += `Sent: ${this.message}\n`; + } + if (response.type === 'send fail') { + this.msgHistory += `Send failed: ` + response.message; + } else if (response.type === 'No client connected') { + this.msgHistory += 'No client connected\n'; + } + } + } + + // 关闭连接 + closeConnection() { + let tcpMessage: TcpMessage = { + type: 'disConnect' + }; + + workerPort.postMessage( + tcpMessage + ); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TcpMessage = e.data; + + if (response.type === 'close success') { + this.msgHistory += `close success`; + } + if (response.type === 'close fail') { + this.msgHistory += 'Close failed:' + response.message; + } else if (response.type === 'No client to close') { + this.msgHistory += 'No client to close\n'; + } + } + } +} + +function getCurrentTimeString() { + const now = new Date(); + // 如果转换后的字符串长度小于2,则在字符串前添加'0',直到字符串长度达到2 + const hours = now.getHours().toString().padStart(2, '0'); + const minutes = now.getMinutes().toString().padStart(2, '0'); + const seconds = now.getSeconds().toString().padStart(2, '0'); + return `[${hours}:${minutes}:${seconds}]`; +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/TlsServer.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/TlsServer.ets new file mode 100644 index 0000000000000000000000000000000000000000..a7c17d872795230cea5152a92fe826df31c41cc8 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/TlsServer.ets @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2025 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. + */ + +import { picker } from '@kit.CoreFileKit'; +import { MessageEvents, util, worker } from '@kit.ArkTS'; +import fs from '@ohos.file.fs'; + +const workerPort = new worker.ThreadWorker('entry/ets/workers/TlsServerWorker.ets'); + +export interface TlsServerMessage { + type: string; + serverIp?: string; // 服务器 IP 地址 + serverPort?: number; // 服务器端口 + serverKey?: string; // CA 证书内容 + serverCert?: string; // 客户端证书内容 + caCert?: string; // 客户端私钥内容 + message?: string; +} + +@Entry +@Component +struct TLS_Server { + @State msgHistory: string = ''; // 通信历史记录 + @State serverIp: string = ''; // 服务器 IP 同一网段下 + @State serverPort: number = 5555; // 服务器端口 + @State serverKey: string = ''; // 服务器私钥内容 + @State serverCert: string = ''; // 服务器证书内容 + @State caCert: string = ''; // CA 证书内容 + @State canStartServer: boolean = false; // 是否可以启动服务器 + @State canSendMessage: boolean = false; // 是否可以发送消息 + @State tlsServerRunning: boolean = false; // TLS 服务器是否正在运行 + scroller: Scroller = new Scroller(); + + aboutToDisappear(): void { + workerPort.terminate(); + } + + build() { + Column() { + Text($r('app.string.TlsServer')) + .fontSize(20) + .fontWeight(FontWeight.Bold) + .margin({ top: 10, bottom: 10 }) + .width('100%') + .textAlign(TextAlign.Center) + .padding(10) + + // 服务器IP与端口输入框 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Server_Address')) + .width(120) + .fontSize(14) + TextInput({ text: this.serverIp }) + .onChange((value) => { + this.serverIp = value; + }) + .width(200) + .fontSize(12) + TextInput({ text: this.serverPort.toString() }) + .type(InputType.Number) + .onChange((value) => { + this.serverPort = parseInt(value); + }) + .width(100) + .fontSize(12) + } + .padding(10) + + // 选择并加载服务器私钥 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Select_Server_Key')) + .width(120) + .fontSize(14) + Button($r('app.string.Select')) + .onClick(() => { + this.selectServerKey(); + }) + .width(100) + .fontSize(14) + Button($r('app.string.Load')) + .onClick(() => { + this.loadServerKey(); + }) + .enabled(this.serverKey !== '') + .width(100) + .fontSize(14) + } + .padding(10) + + // 选择并加载服务器证书 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Select_Server_Cert')) + .width(120) + .fontSize(14) + Button($r('app.string.Select')) + .onClick(() => { + this.selectServerCert(); + }) + .width(100) + .fontSize(14) + Button($r('app.string.Load')) + .onClick(() => { + this.loadServerCert(); + }) + .enabled(this.serverCert !== '') + .width(100) + .fontSize(14) + } + .padding(10) + + // 选择并加载 CA 证书 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.CA_File')) + .width(120) + .fontSize(14) + Button($r('app.string.Select')) + .onClick(() => { + this.selectCACert(); + }) + .width(100) + .fontSize(14) + Button($r('app.string.Load')) + .onClick(() => { + this.loadCACert(); + }) + .enabled(this.caCert !== '') + .width(100) + .fontSize(14) + } + .padding(10) + + // 启动服务器按钮 + Button($r('app.string.Start_Server')) + .onClick(() => { + this.startServer(); + }) + .enabled(this.canStartServer) + .width(150) + .fontSize(14) + + // 消息历史展示区 + TextArea({ text: this.msgHistory }) + .height(150) + .padding(10) + .width('100%') + + // 滚动区域 + Scroll(this.scroller) { + Text(this.msgHistory) + .textAlign(TextAlign.Start) + .padding(10) + .width('100%') + .backgroundColor(0xeeeeee) + } + .height(300) + .scrollable(ScrollDirection.Vertical) + } + .width('100%') + } + + // 选择并加载服务器私钥 + selectServerKey() { + let documentPicker = new picker.DocumentViewPicker(); + documentPicker.select().then((result) => { + if (result.length > 0) { + this.serverKey = result[0]; + this.msgHistory += `Selected server private key file: ${this.serverKey}\n`; + } + }).catch((err: Error) => { + this.msgHistory += `Failed to select server private key file: ${err.message}\n`; + }); + } + + loadServerKey() { + try { + let buf = new ArrayBuffer(4096); // 设置缓冲区大小为4096字节 + let file = fs.openSync(this.serverKey, fs.OpenMode.READ_ONLY); + let readLen = fs.readSync(file.fd, buf, { offset: 0 }); + this.serverKey = this.bufToString(buf.slice(0, readLen)); + this.msgHistory += 'Server private key loaded successfully\n'; + this.checkCanStartServer(); + } catch (err) { + this.msgHistory += `Failed to load server private key: ${err.message}\n`; + } + } + + // 选择并加载服务器证书 + selectServerCert() { + let documentPicker = new picker.DocumentViewPicker(); + documentPicker.select().then((result) => { + if (result.length > 0) { + this.serverCert = result[0]; + this.msgHistory += `Selected server certificate file: ${this.serverCert}\n`; + } + }).catch((err: Error) => { + this.msgHistory += `Failed to select server certificate file: ${err.message}\n`; + }); + } + + loadServerCert() { + try { + let buf = new ArrayBuffer(4096); // 设置缓冲区大小为4096字节 + let file = fs.openSync(this.serverCert, fs.OpenMode.READ_ONLY); + let readLen = fs.readSync(file.fd, buf, { offset: 0 }); + this.serverCert = this.bufToString(buf.slice(0, readLen)); + this.msgHistory += 'Server certificate loaded successfully\n'; + this.checkCanStartServer(); + } catch (err) { + this.msgHistory += `Failed to load server certificate: ${err.message}\n`; + } + } + + // 选择并加载 CA 证书 + selectCACert() { + let documentPicker = new picker.DocumentViewPicker(); + documentPicker.select().then((result) => { + if (result.length > 0) { + this.caCert = result[0]; + this.msgHistory += `Selected CA certificate file: ${this.caCert}\n`; + } + }).catch((err: Error) => { + this.msgHistory += `Failed to select CA certificate file: ${err.message}\n`; + }); + } + + loadCACert() { + try { + let buf = new ArrayBuffer(4096); // 设置缓冲区大小为4096字节 + let file = fs.openSync(this.caCert, fs.OpenMode.READ_ONLY); + let readLen = fs.readSync(file.fd, buf, { offset: 0 }); + this.caCert = this.bufToString(buf.slice(0, readLen)); + this.msgHistory += 'CA certificate loaded successfully\n'; + this.checkCanStartServer(); + } catch (err) { + this.msgHistory += `Failed to load CA certificate: ${err.message}\n`; + } + } + + checkCanStartServer() { + if (this.serverKey && this.serverCert && this.caCert) { + this.canStartServer = true; + } + } + + // 启动服务器 + startServer() { + workerPort.postMessage({ + type: 'startServer', + serverIp: this.serverIp, + serverPort: this.serverPort, + serverKey: this.serverKey, + serverCert: this.serverCert, + caCert: this.caCert + }); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsServerMessage = e.data; + if (response.type === 'serverStarted') { + this.msgHistory += 'TLS server started successfully\n'; + this.tlsServerRunning = true; + } else if (response.type === 'error') { + this.msgHistory += `Error: ${response.message}\n`; + } else if (response.type === 'clientMessage') { + this.msgHistory += `Client message: ${response.message}\n`; + } else if (response.type === 'messageSent') { + this.msgHistory += `Message sent: ${response.message}\n`; + } else if (response.type === 'connectionClosed') { + this.msgHistory += `Connection closed: ${response.message}\n`; + } + }; + } + + bufToString(buf: ArrayBuffer): string { + let msgArray = new Uint8Array(buf); + let textDecoder = util.TextDecoder.create('utf-8'); + return textDecoder.decodeToString(msgArray); + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/TwoWayTls.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/TwoWayTls.ets new file mode 100644 index 0000000000000000000000000000000000000000..aebb87d65390d0bc98027d860afa023e5b54bc7c --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/TwoWayTls.ets @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2025 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. + */ + +import { MessageEvents, worker } from '@kit.ArkTS'; +import wifiManager from '@ohos.wifiManager'; +import picker from '@ohos.file.picker'; + +// 创建 Worker 实例 +const workerPort = new worker.ThreadWorker('entry/ets/workers/TwoWayTlsWorker.ets'); + +interface TlsTwoWayMessage { + type: string; + fileUri?: string; // 用于传递文件路径 + serverIp?: string; // 服务器 IP 地址 + serverPort?: number; // 服务器端口 + ca?: string; // CA 证书内容 + cert?: string; // 客户端证书内容 + privateKey?: string; // 客户端私钥内容 + message?: string; // 要发送的消息 + caContent?: string; // 从文件中读取的 CA 内容 + certContent?: string; // 从文件中读取的证书内容 + privateKeyContent?: string; // 从文件中读取的私钥内容 + content?: string; // 通用的文件加载返回内容(可选) +} + +// 全局状态 +let ipNum = wifiManager.getIpInfo().ipAddress; +let localIp = (ipNum >>> 24) + '.' + (ipNum >> 16 & 0xFF) + '.' + (ipNum >> 8 & 0xFF) + '.' + (ipNum & 0xFF); +let caFileUri = ''; +let certFileUri = ''; +let keyFileUri = ''; + +@Entry +@Component +struct Index { + @State msgHistory: string = ''; + @State sendMsg: string = ''; + @State serverIp: string = ''; // 服务端IP地址 + @State serverPort: number = 5555; // 服务端端口号 + @State canLoad: boolean = false; + @State canConnect: boolean = false; + @State canSend: boolean = false; + @State ca: string = ''; + @State cert: string = ''; + @State privateKey: string = ''; + scroller: Scroller = new Scroller(); + + aboutToDisappear(): void { + workerPort.terminate(); + } + + build() { + Row() { + Column() { + Text($r('app.string.TwoWayTls')) + .fontSize(14) + .fontWeight(FontWeight.Bold) + .width('100%') + .textAlign(TextAlign.Center) + .padding(10) + + // 本地 IP 地址 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Local_IP_Address')) + .width(100) + .fontSize(14) + .flexGrow(0) + Text(localIp) + .width(110) + .fontSize(12) + .flexGrow(1) + }.width('100%') + .padding(10) + + // 服务端 IP 和端口输入框 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Server_Address')) + .fontSize(14) + .width(90) + .flexGrow(1) + TextInput({ text: this.serverIp }) + .onChange((value) => { + this.serverIp = value; + }) + .width(110) + .fontSize(12) + .flexGrow(4) + Text(':') + .width(5) + .flexGrow(0) + TextInput({ text: this.serverPort.toString() }) + .type(InputType.Number) + .onChange((value) => { + this.serverPort = parseInt(value); + }) + .fontSize(12) + .flexGrow(2) + .width(50) + }.width('100%') + .padding(10) + + // 选择和加载 CA 文件 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.CA_File')) + .fontSize(14) + .width(90) + .flexGrow(1) + Button($r('app.string.Select')) + .onClick(() => { + this.selectCA(); + }) + .width(70) + .fontSize(14) + Button($r('app.string.Load')) + .onClick(() => { + this.loadCA(); + }) + .enabled(this.canLoad) + .width(70) + .fontSize(14) + }.width('100%') + .padding(10) + + // 选择和加载客户端证书 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Client_Cert')) + .fontSize(14) + .width(90) + .flexGrow(1) + Button($r('app.string.Select')) + .onClick(() => { + this.selectCert(); + }) + .width(70) + .fontSize(14) + Button($r('app.string.Load')) + .onClick(() => { + this.loadCert(); + }) + .enabled(this.canLoad) + .width(70) + .fontSize(14) + }.width('100%') + .padding(10) + + // 选择和加载客户端私钥 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Text($r('app.string.Client_Key')) + .fontSize(14) + .width(90) + .flexGrow(1) + Button($r('app.string.Select')) + .onClick(() => { + this.selectKey(); + }) + .width(70) + .fontSize(14) + Button($r('app.string.Load')) + .onClick(() => { + this.loadKey(); + }) + .enabled(this.canLoad) + .width(70) + .fontSize(14) + }.width('100%') + .padding(10) + + // 连接和输入发送消息 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + Button($r('app.string.Connect')) + .onClick(() => { + this.connect2Server(); + }) + .enabled(!!this.ca && !!this.cert && !!this.privateKey) + .width(70) + .fontSize(14) + }.width('100%') + .padding(10) + + // 发送消息输入框 + Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { + TextInput({ text: this.sendMsg, placeholder: $r('app.string.Enter_Message') }) + .onChange((value) => { + this.sendMsg = value; + }) + .width(200) + .fontSize(14) + .flexGrow(1) + Button($r('app.string.Send')) + .enabled(this.canSend) + .onClick(() => { + this.sendMsg2Server(); + }) + .width(70) + .fontSize(14) + }.width('100%') + .padding(10) + + // 消息展示区域 + Scroll(this.scroller) { + Text(this.msgHistory) + .textAlign(TextAlign.Start) + .padding(10) + .width('100%') + .backgroundColor(0xeeeeee) + } + .align(Alignment.Top) + .backgroundColor(0xeeeeee) + .height(300) + .flexGrow(1) + .scrollable(ScrollDirection.Vertical) + .scrollBar(BarState.On) + } + .width('100%') + .justifyContent(FlexAlign.Start) + .height('100%') + } + .height('100%') + } + + // 发送消息 + sendMsg2Server() { + if (!this.sendMsg.trim()) { + return; + } + workerPort.postMessage({ type: 'sendMessage', message: this.sendMsg }); + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsTwoWayMessage = e.data; + if (response.type === 'sendMessageSuccess') { + this.msgHistory += `Me: ${response.message} ${getCurrentTimeString()}\r\n`; + } else if (response.type === 'sendMessageFailure') { + this.msgHistory += `Send fail: ${response.message}\r\n`; + } + } + } + + // 选择文件:CA、证书、私钥 + selectCA() { + this.selectFile((file: string) => { + caFileUri = file; + this.canLoad = true; + }) + } + + selectCert() { + this.selectFile((file: string) => { + certFileUri = file; + this.canLoad = true; + }) + } + + selectKey() { + this.selectFile((file: string) => { + keyFileUri = file; + this.canLoad = true; + }) + } + + selectFile(callback: (file: string) => void) { + let pickerInstance = new picker.DocumentViewPicker(); + pickerInstance.select().then((result) => { + if (result.length > 0) { + callback(result[0]); + } + }).catch((e: Error) => { + this.msgHistory += `Select fail: ${e.message}\r\n`; + }); + } + + checkAllFilesLoaded() { + if (caFileUri && certFileUri && keyFileUri) { + this.canSend = true; + } + } + + // 从文件加载内容 + loadCA() { + this.loadFile(caFileUri, (content: string) => { + this.ca = content; + }) + } + + loadCert() { + this.loadFile(certFileUri, (content: string) => { + this.cert = content; + }) + } + + loadKey() { + this.loadFile(keyFileUri, (content: string) => { + this.privateKey = content; + }) + } + + // 加载文件内容并设置状态 + loadFile(fileUri: string, callback: (content: string) => void) { + workerPort.postMessage({ type: 'loadFile', fileUri: fileUri }); + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsTwoWayMessage = e.data; + // 根据返回的字段判断:若有 caContent、certContent 或 privateKeyContent,则调用回调 + if (response.type === 'fileLoaded') { + if (response.caContent) { + this.msgHistory += 'CA file loaded successfully.\r\n'; + callback(response.caContent); + } else if (response.certContent) { + this.msgHistory += 'CERT file loaded successfully.\r\n'; + callback(response.certContent); + } else if (response.privateKeyContent) { + this.msgHistory += 'KEY file loaded successfully.\r\n'; + callback(response.privateKeyContent); + } + this.checkAllFilesLoaded(); + } else { + this.msgHistory += `Load file fail: ${response.message}\r\n`; + } + } + } + + // 连接服务端 + connect2Server() { + workerPort.postMessage({ + type: 'connectServer', + serverIp: this.serverIp, + serverPort: this.serverPort, + ca: this.ca, + cert: this.cert, + privateKey: this.privateKey + }); + workerPort.onmessage = (e: MessageEvents) => { + const response: TlsTwoWayMessage = e.data; + if (response.type === 'connectSuccess') { + this.msgHistory += 'Connect success\r\n'; + this.canSend = true; + } else if (response.type === 'connectFailure') { + this.msgHistory += `Connect fail: ${response.message}\r\n`; + } + } + } +} + +// 获取当前时间的字符串 +function getCurrentTimeString() { + const now = new Date(); + return `[${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}]`; +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/UdpClient_Server.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/UdpClient_Server.ets new file mode 100644 index 0000000000000000000000000000000000000000..8f0909c2210ae02a5065440628ed37c738c3c9b3 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/connect/UdpClient_Server.ets @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2025 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. + */ + +import { MessageEvents, worker } from '@kit.ArkTS'; +import Logger from '../common/Logger'; +import { TcpMessage } from './TcpClient'; + +interface FONT_SIZE { + SMALL: number; + MEDIUM: number; + LARGE: number; +} + +const FONT: FONT_SIZE = { + SMALL: 16, // 小字体大小 + MEDIUM: 20, // 中等字体大小 + LARGE: 50, // 大字体大小 +}; + +interface MARGIN_SIZE { + SMALL: number; + MEDIUM: number; + LARGE: number; +} + +const MARGIN: MARGIN_SIZE = { + SMALL: 10, // 小间隔距离 + MEDIUM: 20, // 中等间隔距离 + LARGE: 30, // 大间隔距离 +}; + +const BUTTON_HEIGHT: number = 50; // 按钮高度 + +const workerPort = new worker.ThreadWorker('entry/ets/workers/UdpWorker.ets'); // 初始化 Worker + +@Entry +@Component +struct Index { + @State message: string = ''; // 用于展示发送的消息 + @State serverIp: string = ''; // 服务端IP地址 + @State clientIp: string = ''; // 客户端IP地址 + @State serverPort: number = 5555; // 服务端端口号 + @State clientPort: number = 5555; // 客户端端口号 + @State msgHistory: string = ''; // 消息记录 + @State connectionStatus: string = ''; // 连接状态 + scroller: Scroller = new Scroller(); + + aboutToDisappear(): void { + workerPort.terminate(); + } + + // 构建界面 + build() { + Column() { + // 服务端IP和端口放在一行 + Row() { + Column() { + Text($r('app.string.Enter_Server_Ip')) + .fontSize(FONT.MEDIUM) + .fontColor(Color.Gray) + .textAlign(TextAlign.Start) + .margin({ right: MARGIN.MEDIUM }) + TextInput() + .width('100%') + .onChange((value) => { + this.serverIp = value; // 更新serverIp的值 + }) + } + .width('50%') + + Column() { + Text($r('app.string.Enter_Server_Port')) + .fontSize(FONT.MEDIUM) + .fontColor(Color.Gray) + .textAlign(TextAlign.Start) + TextInput() + .width('100%') + .onChange((value) => { + this.serverPort = parseInt(value, 10); // 更新serverPort的值 + }) + } + .width('50%') + } + .width('80%') + .margin({ bottom: MARGIN.MEDIUM }) + + Row() { + // 客户端IP和端口放在一行 + Column() { + Text($r('app.string.Enter_Client_Ip')) + .fontSize(FONT.MEDIUM) + .fontColor(Color.Gray) + .textAlign(TextAlign.Start) + .margin({ right: MARGIN.MEDIUM }) + TextInput() + .width('100%') + .onChange((value) => { + this.clientIp = value; // 更新serverIp的值 + }) + } + .width('50%') + + Column() { + Text($r('app.string.Enter_Client_Port')) + .fontSize(FONT.MEDIUM) + .fontColor(Color.Gray) + .textAlign(TextAlign.Start) + TextInput() + .width('100%') + .onChange((value) => { + this.clientPort = parseInt(value, 10); // 更新serverPort的值 + }) + } + .width('50%') + } + .width('80%') + .margin({ bottom: MARGIN.MEDIUM }) + + // 启动服务器按钮 + Button($r('app.string.Start_Server')) + .onClick(() => { + this.startServer(); + }) + .margin({ bottom: MARGIN.SMALL }) + .width('80%') + .height(BUTTON_HEIGHT) + .fontColor(Color.White) + .fontSize(FONT.MEDIUM) + + // 发送消息按钮 + Button($r('app.string.Send')) + .onClick(() => { + this.sendMessage(); + }) + .margin({ bottom: MARGIN.SMALL }) + .width('80%') + .height(BUTTON_HEIGHT) + .fontColor(Color.White) + .fontSize(FONT.MEDIUM) + + // 关闭连接按钮 + Button($r('app.string.Close_Connection')) + .onClick(() => { + this.closeConnection(); + }) + .margin({ bottom: MARGIN.SMALL }) + .width('80%') + .height(BUTTON_HEIGHT) + .fontColor(Color.White) + .fontSize(FONT.MEDIUM) + + // 显示连接状态 + Text(this.connectionStatus) + .fontSize(FONT.SMALL) + .fontColor(Color.Green) + .margin({ top: MARGIN.SMALL }) + + // 输入消息框 + Column() { + Text($r('app.string.Enter_Message')) + .fontSize(FONT.MEDIUM) + .fontColor(Color.Gray) + .margin({ bottom: MARGIN.SMALL }) + TextInput() + .width('80%') + .onChange((value) => { + this.message = value; + }) + } + .margin({ top: MARGIN.MEDIUM }) + + // 消息记录框 + Column() { + Text($r('app.string.Message_History')) + .fontSize(FONT.MEDIUM) + .fontColor(Color.Gray) + .margin({ bottom: MARGIN.SMALL }) + Scroll(this.scroller) { + Text(this.msgHistory) + .textAlign(TextAlign.Start) + .padding(10) + .width('100%') + .height('100%') + .backgroundColor(0xeeeeee) + } + .width('80%') + .height(200) // 消息记录区域高度 + .margin({ top: MARGIN.SMALL }) + } + } + .width('100%') + .height('100%') + .justifyContent(FlexAlign.Center) + } + + // 启动服务器 + startServer() { + if (!this.serverIp || !this.serverPort) { + this.msgHistory += 'Some required fields are missing.\n'; + Logger.error('Some required fields are missing.'); + return; + } + + let tcpMessage: TcpMessage = { + type: 'startServer', + serverIp: this.serverIp, + serverPort: this.serverPort, + }; + + workerPort.postMessage(tcpMessage); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TcpMessage = e.data; + + if (response.type === 'listen success') { + this.msgHistory += 'Server listening success.\n'; + } + if (response.type === 'received message--') { + this.msgHistory += `Received message: ${response.message}\n`; + } else if (response.type === 'listen fail') { + this.msgHistory += 'Listen fail.\n'; + } + } + } + + // 发送消息 + sendMessage() { + if (!this.message || !this.clientIp || !this.clientPort) { + this.msgHistory += 'Some required fields are missing.\n'; + Logger.error('Some required fields are missing.'); + return; + } + + let tcpMessage: TcpMessage = { + type: 'sendMessage', + message: this.message, + clientIp: this.clientIp, // 使用用户输入的clientIp + clientPort: this.clientPort, // 假设客户端端口为默认值 + }; + + workerPort.postMessage(tcpMessage); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TcpMessage = e.data; + if (response.type === 'send success') { + this.msgHistory += `Sent: ${this.message}\n`; + } else if (response.type === 'send fail') { + this.msgHistory += `Send failed: ${response.message}\n`; + } + } + } + + // 关闭连接 + closeConnection() { + let tcpMessage: TcpMessage = { + type: 'disConnect' + }; + + workerPort.postMessage(tcpMessage); + + workerPort.onmessage = (e: MessageEvents) => { + const response: TcpMessage = e.data; + + if (response.type === 'close success') { + this.msgHistory += `Connection closed.\n`; + } else if (response.type === 'close fail') { + this.msgHistory += `Close failed\n`; + } + } + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/entryability/EntryAbility.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..e8fd732cad54feab9bb388e85254abd35adf8565 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025 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. + */ + +import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +} + diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..b1e212947256c5533c7b06285a597c94f840a6e3 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/pages/Index.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..255a06d173aa5e135166dece193c99d2a3c8a907 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2025 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. + */ + +import TitleBar from '../components/TitleBar'; +import router from '@ohos.router'; + +interface Item { + text: string; +} + +const operationUrls: Array = [ + 'connect/TcpClient', + 'connect/TcpServer', + 'connect/UdpClient_Server', + 'connect/Multicast', + 'connect/LocalSocket', + 'connect/OneWayTls', + 'connect/TwoWayTls', + 'connect/Tcp2OneWayTls', + 'connect/Tcp2TwoWayTls', + 'connect/TlsServer', +]; + +@Entry +@Component +struct Index { + ResourceToString(resource: Resource): string { + return getContext(this).resourceManager.getStringSync(resource); + } + + @State ListItem: Item[] = [ + { text: this.ResourceToString($r('app.string.TcpClient')) }, + { text: this.ResourceToString($r('app.string.TcpServer')) }, + { text: this.ResourceToString($r('app.string.Udp')) }, + { text: this.ResourceToString($r('app.string.Multicast')) }, + { text: this.ResourceToString($r('app.string.LocalSocket')) }, + { text: this.ResourceToString($r('app.string.OneWayTls')) }, + { text: this.ResourceToString($r('app.string.TwoWayTls')) }, + { text: this.ResourceToString($r('app.string.Tcp2OneWayTls')) }, + { text: this.ResourceToString($r('app.string.Tcp2TwoWayTls')) }, + { text: this.ResourceToString($r('app.string.TlsServer')) }, + ] + + build() { + Column() { + TitleBar() + List() { + ForEach(this.ListItem, (item: Item, index) => { + ListItem() { + Row() { + Blank().width('4%') + Text(item.text) + .fontSize(16) + .fontColor('black') + .width('90%') + Image($r('app.media.right')) + .height(12) + .width(12) + } + .onClick(() => { + router.pushUrl({ + url: operationUrls[index] + }); + }) + .border({ radius: 20 }) + .width('90%') + .height('8%') + .backgroundColor(Color.White) + .margin({ top: 12, left: 15, right: 8 }) + } + }) + } + .height('90%') + .width('100%') + } + .width('100%') + .height('100%') + .backgroundColor('#F1F3F5') + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/LocalSocketWorker.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/LocalSocketWorker.ets new file mode 100644 index 0000000000000000000000000000000000000000..dcf53564b86d2edd82550cc450377df0f113b6f9 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/LocalSocketWorker.ets @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2025 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. + */ + +import { MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS'; +import { socket } from '@kit.NetworkKit'; +import Logger from '../common/Logger'; + +const workerPort: ThreadWorkerGlobalScope = worker.workerPort; + +interface SocketMessage { + type: string; + address: string; + timeout?: number; + closeTime?: number; +} + +// 处理主线程发送的消息 +workerPort.onmessage = (e: MessageEvents) => { + const message: SocketMessage = e.data; + + if (message.type === 'startLocalSocket') { + startLocalSocket(message.address, message.timeout || 6000, message.closeTime || 30000); // 默认消息超时6s消息关闭时间30s + } + if (message.type === 'startLocalSocketServer') { + startLocalSocketServer(message.address); + } +}; + +// 字符串转 `ArrayBuffer` +function stringToArrayBuffer(str: string): ArrayBuffer { + const buf = new ArrayBuffer(str.length); + const bufView = new Uint8Array(buf); + for (let i = 0; i < str.length; i++) { + bufView[i] = str.charCodeAt(i); + } + return buf; +} + +// [Start application_transmits_data_via_local_socket] +// 启动本地 Socket 客户端 +function startLocalSocket(address: string, timeout: number, closeTime: number): void { + let client: socket.LocalSocket = socket.constructLocalSocketInstance(); + + client.on('message', (value: socket.LocalSocketMessageInfo) => { + const uintArray = new Uint8Array(value.message); + let messageView = ''; + for (let i = 0; i < uintArray.length; i++) { + messageView += String.fromCharCode(uintArray[i]); + } + Logger.info(`Client received: ${messageView}`); + }); + + client.on('connect', () => { + workerPort.postMessage({ type: 'clientStatus', status: 'Connected' }); + Logger.info('Client connected'); + }); + + client.on('close', () => { + workerPort.postMessage({ type: 'clientStatus', status: 'Closed' }); + Logger.info('Client closed'); + }); + + let sendOpt: socket.LocalSendOptions = { + data: 'Hello world!' + }; + + client.connect({ address: { address }, timeout }).then(() => { + Logger.info(`connect success`); + client.send(sendOpt).then(() => { + Logger.info(`send success`); + }).catch((err: Object) => { + Logger.info(`send failed: ` + JSON.stringify(err)); + }); + }).catch((err: Object) => { + Logger.info(`connect fail: ` + JSON.stringify(err)); + }); + + setTimeout(async () => { + await client.close(); + workerPort.postMessage({ type: 'clientStatus', status: 'Closed by timeout' }); + }, closeTime); +} +// [End application_transmits_data_via_local_socket] + +// [Start application_transmits_data_via_local_socket_server] +// 启动本地 Socket 服务器 +function startLocalSocketServer(address: string): void { + let server: socket.LocalSocketServer = socket.constructLocalSocketServerInstance(); + + server.listen({ address }).then(() => { + workerPort.postMessage({ type: 'serverStatus', status: 'Listening' }); + Logger.info(`Server listening on ${address}`); + }).catch((err: object) => { + workerPort.postMessage({ type: 'serverStatus', status: 'Listen Failed' }); + Logger.error(`Server listen error: ${JSON.stringify(err)}`); + }); + + server.on('connect', (connection: socket.LocalSocketConnection) => { + connection.on('message', (value: socket.LocalSocketMessageInfo) => { + const uintArray = new Uint8Array(value.message); + let messageView = ''; + for (let i = 0; i < uintArray.length; i++) { + messageView += String.fromCharCode(uintArray[i]); + } + Logger.info(`Server received: ${messageView}`); + }); + + connection.send({ data: stringToArrayBuffer('Hello from server!') }).then(() => { + Logger.info('Server send success'); + }).catch((err: object) => { + Logger.error(`Server send failed: ${JSON.stringify(err)}`); + }); + setTimeout(() => { + // 取消事件订阅 + connection.close(); + Logger.info('Connection close by server.'); + }, 10000); // 设置一个合理的超时关闭 + }); + + // 在关闭服务器连接时取消事件订阅 + setTimeout(() => { + // 取消事件订阅 + server.off('connect'); + server.off('error'); + Logger.info('Server events unsubscribed.'); + }, 30000); // 设置一个合理的超时关闭(例如30秒后关闭服务器) +} +// [End application_transmits_data_via_local_socket_server] \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/MulticastWorker.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/MulticastWorker.ets new file mode 100644 index 0000000000000000000000000000000000000000..844a8cfe5e71716d48b4014cf1001c31c41df329 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/MulticastWorker.ets @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2025 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. + */ + +import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS'; +import Logger from '../common/Logger'; +import { socket } from '@kit.NetworkKit'; +import { Message } from '../connect/Multicast' +import { BusinessError } from '@kit.BasicServicesKit'; + +const MULTICAST_MESSAGE_SEND_INTERVAL = 30 * 1000; // 多播消息发送间隔 + +// 多播通信数据接口 +interface MulticastData { + address: string; + port: number; + family: number; +} + +interface MulticastMessageEvent { + message: ArrayBuffer; +} + +function postMessage(arg0: Message) { + workerPort.postMessage(arg0); +} + +const workerPort: ThreadWorkerGlobalScope = worker.workerPort; + +// [Start application_transmits_data_via_multicast_socket] +function startMulticast(data: MulticastData) { + let multicast: socket.MulticastSocket = socket.constructMulticastSocketInstance(); + + // 这里构造一个对象用于加入多播组 + const multicastConfig: MulticastData = { + address: data.address, + port: data.port, + family: data.family + }; + + multicast.addMembership(multicastConfig).then(() => { + postMessage({ type: 'multicastStatus', status: 'Joined multicast group' }); + Logger.info(`Multicast: Joined group ${data.address}:${data.port}`); + }).catch((err: BusinessError) => { + postMessage({ type: 'multicastStatus', status: 'Failed to join multicast group' }); + Logger.error(`Multicast: Failed to join group ${data.address}:${data.port} - ${JSON.stringify(err)}`); + }); + + multicast.on('message', (value: MulticastMessageEvent) => { + let str = ''; + let buffer = value.message; + for (let i = 0; i < buffer.byteLength; ++i) { + str += String.fromCharCode(new DataView(buffer).getUint8(i)); + } + postMessage({ type: 'multicastMessage', status: 'Message received', message: str }); + Logger.info(`Multicast: Received message: ${str}`); + }); + + // 发送一条多播消息 + multicast.send({ data: 'Hello multicast group!', address: multicastConfig }).then(() => { + Logger.info('Multicast: Message sent successfully'); + }).catch((err: BusinessError) => { + Logger.error(`Multicast: Failed to send message - ${JSON.stringify(err)}`); + }); + + // 设定一定时间后退出多播组 + setTimeout(() => { + multicast.off('message'); + multicast.dropMembership(multicastConfig).then(() => { + postMessage({ type: 'multicastStatus', status: 'Dropped multicast membership' }); + Logger.info('Multicast: Dropped membership successfully'); + }).catch((err: BusinessError) => { + Logger.error(`Multicast: Failed to drop membership - ${JSON.stringify(err)}`); + }); + }, MULTICAST_MESSAGE_SEND_INTERVAL); +} + +/** + * Defines the event handler to be called when the worker thread receives a message sent by the host thread. + * The event handler is executed in the worker thread. + * + * @param e message data + */ +workerPort.onmessage = (e: MessageEvents) => { + const message: Message = e.data; + + if (message.type === 'startMulticast' && message.message) { + const multicastData = JSON.parse(message.message) as MulticastData; + startMulticast(multicastData); + } +}; +// [End application_transmits_data_via_multicast_socket] + +/** + * Defines the event handler to be called when the worker receives a message that cannot be deserialized. + * The event handler is executed in the worker thread. + * + * @param e message data + */ +workerPort.onmessageerror = (e: MessageEvents) => { +}; + +/** + * Defines the event handler to be called when an exception occurs during worker execution. + * The event handler is executed in the worker thread. + * + * @param e error message + */ +workerPort.onerror = (e: ErrorEvent) => { +}; \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/OneWayTlsWorker.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/OneWayTlsWorker.ets new file mode 100644 index 0000000000000000000000000000000000000000..3f709f8a4db8db82cfac14cb17a8529dcd65ca51 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/OneWayTlsWorker.ets @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2025 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. + */ + +import { MessageEvents, util, worker } from '@kit.ArkTS'; +import socket from '@ohos.net.socket'; +import fs from '@ohos.file.fs'; +import { BusinessError } from '@kit.BasicServicesKit'; + +const workerPort = worker.workerPort; // 获取 workerPort 实例 + +let tlsSocket: socket.TLSSocket | null = null; // 确保全局 TLS 连接实例 + +// 连接服务器 +interface LocalAddress { + address: string; + family: number; +} + +interface ServerAddress { + address: string; + port: number; + family: number; +} + +interface CA { + ca: string[]; +} + +interface TlsOneWayMessage { + type: string; + fileUri?: string; + serverIp?: string; + serverPort?: number; + ca?: string; + message?: string; + caContent?: string; +} + +// 监听主线程消息 +workerPort.onmessage = (e: MessageEvents) => { + const message: TlsOneWayMessage = e.data; + handleMessage(message); +}; + +// 统一处理主线程任务 +function handleMessage(message: TlsOneWayMessage) { + switch (message.type) { + case 'loadCA': + if (message.fileUri) { + loadCA(message.fileUri); + } + break; + + case 'connectServer': + if (message.serverIp && message.serverPort && message.ca) { + connectToServer(message.serverIp, message.serverPort, message.ca); + } + break; + + case 'sendMessage': + if (message.message) { + sendMessageToServer(message.message); + } + break; + } +} + +// [Start one-way_authentication_via_tls_socket] +// 读取 CA 证书 +function loadCA(fileUri: string) { + try { + let buf = new ArrayBuffer(1024 * 4); + let file = fs.openSync(fileUri, fs.OpenMode.READ_ONLY); + let readLen = fs.readSync(file.fd, buf, { offset: 0 }); + let caContent = buf2String(buf.slice(0, readLen)); + fs.closeSync(file); + + workerPort.postMessage({ type: 'caLoaded', caContent: caContent }); + } catch (e) { + workerPort.postMessage({ type: 'error', message: `Failed to load CA file: ${e.message}` }); + } +} + +function connectToServer(serverIp: string, serverPort: number, ca: string) { + if (tlsSocket) { + workerPort.postMessage({ type: 'connectFailure', status: 'Socket already connected' }); + return; + } + + tlsSocket = socket.constructTLSSocketInstance(); + + let localAddress: LocalAddress = { address: '0.0.0.0', family: 1 }; + tlsSocket!.bind(localAddress).then(() => { + let serverAddress: ServerAddress = { address: serverIp, port: serverPort, family: 1 }; + let opt: CA = { ca: [ca] }; + + tlsSocket!.connect({ address: serverAddress, secureOptions: opt }) + .then(() => { + workerPort.postMessage({ type: 'connectSuccess', status: 'Connected successfully' }); + + // 监听服务器消息 + tlsSocket!.on('message', (data) => { + let receivedMsg = buf2String(data.message); + workerPort.postMessage({ type: 'serverMessage', message: receivedMsg }); + }); + + tlsSocket!.on('close', () => { + tlsSocket = null; // 连接关闭后,清除 tlsSocket + workerPort.postMessage({ type: 'connectionClosed', status: 'Connection closed by server' }); + }); + + }) + .catch((e: BusinessError) => { + tlsSocket = null; + workerPort.postMessage({ type: 'connectFailure', status: `Failed to connect: ${e.message}` }); + }); + }).catch((e: BusinessError) => { + tlsSocket = null; + workerPort.postMessage({ type: 'connectFailure', status: `Failed to bind socket: ${e.message}` }); + }); +} + +// 发送消息到服务器 +function sendMessageToServer(message: string) { + if (!tlsSocket) { + workerPort.postMessage({ type: 'sendMessageFailure', message: 'TLS connection is not established.' }); + return; + } + + tlsSocket.send(message + '\r\n') + .then(() => { + workerPort.postMessage({ type: 'sendMessageSuccess', message: message }); + }) + .catch((e: Error) => { + workerPort.postMessage({ type: 'sendMessageFailure', message: e.message }); + }); +} +// [End one-way_authentication_via_tls_socket] + +// ArrayBuffer 转 utf8 字符串 +function buf2String(buf: ArrayBuffer): string { + let msgArray = new Uint8Array(buf); + let textDecoder = util.TextDecoder.create('utf-8'); + return textDecoder.decodeToString(msgArray); +} + diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/Tcp2OneWayTlsWorker.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/Tcp2OneWayTlsWorker.ets new file mode 100644 index 0000000000000000000000000000000000000000..29467953069cd7f75a7ba21807df729a0a839766 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/Tcp2OneWayTlsWorker.ets @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2025 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. + */ + +import { MessageEvents, util, worker } from '@kit.ArkTS'; +import socket from '@ohos.net.socket'; +import fs from '@ohos.file.fs'; +import { BusinessError } from '@kit.BasicServicesKit'; + +const workerPort = worker.workerPort; + +let tcpSocket: socket.TCPSocket | null = null; +let tlsSocket: socket.TLSSocket | null = null; + +interface ServerAddress { + address: string; + port: number; + family: number; +} + +interface TcpConnectOptions { + address: ServerAddress; + timeout: number; +} + +interface TlsOneWayMessage { + type: string; + fileUri?: string; + serverIp?: string; + serverPort?: number; + ca?: string; + message?: string; + caContent?: string; +} + +workerPort.onmessage = (e: MessageEvents) => { + const message: TlsOneWayMessage = e.data; + handleMessage(message); +}; + +function handleMessage(message: TlsOneWayMessage) { + switch (message.type) { + case 'loadCA': + if (message.fileUri) { + loadCA(message.fileUri); + } + break; + case 'connectToServer': + if (message.serverIp && message.serverPort && message.ca) { + connectToServer(message.serverIp, message.serverPort, message.ca); + } + break; + case 'upgradeToTLS': + if (message.ca) { + upgradeToTLSSocket(message.ca); + } + break; + case 'sendMessage': + if (message.message) { + sendMessageToServer(message.message); + } + break; + } +} + +// [Start one-way_authentication_by_upgrading_tcp_socket_to_tls_socket] +function loadCA(fileUri: string) { + try { + let buf = new ArrayBuffer(4096); + let file = fs.openSync(fileUri, fs.OpenMode.READ_ONLY); + let readLen = fs.readSync(file.fd, buf, { offset: 0 }); + let caContent = buf2String(buf.slice(0, readLen)); + fs.closeSync(file); + + workerPort.postMessage({ type: 'caLoaded', caContent: caContent }); + } catch (e) { + workerPort.postMessage({ type: 'error', message: `Failed to load CA file: ${e.message}` }); + } +} + +function connectToServer(serverIp: string, serverPort: number, ca: string) { + if (tcpSocket) { + workerPort.postMessage({ type: 'connectFailure', message: 'TCP already connected' }); + return; + } + + tcpSocket = socket.constructTCPSocketInstance(); + + let serverAddress: ServerAddress = { address: serverIp, port: serverPort, family: 1 }; + let tcpConnectOptions: TcpConnectOptions = { address: serverAddress, timeout: 6000 }; + + tcpSocket.connect(tcpConnectOptions).then(() => { + workerPort.postMessage({ type: 'connectSuccess', message: 'TCP connection successful' }); + }).catch((e: BusinessError) => { + workerPort.postMessage({ type: 'connectFailure', message: `TCP connection failed: ${e.message}` }); + }); +} + +interface CA { + ca: string[]; +} + +function upgradeToTLSSocket(ca: string) { + if (!tcpSocket) { + workerPort.postMessage({ type: 'connectFailure', message: 'No active TCP connection' }); + return; + } + + tlsSocket = socket.constructTLSSocketInstance(tcpSocket); + + let secureOptions: CA = { ca: [ca] }; + + tlsSocket.connect({ + secureOptions, + address: undefined + }).then(() => { + workerPort.postMessage({ type: 'tlsConnectSuccess', message: 'TLS connection successful' }); + + tlsSocket!.on('message', (data) => { + let receivedMsg = buf2String(data.message); + workerPort.postMessage({ type: 'serverMessage', message: receivedMsg }); + }); + + tlsSocket!.on('close', () => { + workerPort.postMessage({ type: 'connectionClosed', message: 'TLS connection closed by server' }); + tlsSocket = null; + }); + }).catch((e: BusinessError) => { + tlsSocket = null; + workerPort.postMessage({ type: 'tlsConnectFailure', message: `TLS connection failed: ${e.message}` }); + }); +} + +function sendMessageToServer(message: string) { + if (!tlsSocket) { + workerPort.postMessage({ type: 'sendMessageFailure', message: 'TLS connection is not established' }); + return; + } + + tlsSocket.send(message + '\r\n').then(() => { + workerPort.postMessage({ type: 'sendMessageSuccess', message: message }); + }).catch((e: Error) => { + workerPort.postMessage({ type: 'sendMessageFailure', message: `Failed to send message: ${e.message}` }); + }); +} +// [End one-way_authentication_by_upgrading_tcp_socket_to_tls_socket] + +function buf2String(buf: ArrayBuffer): string { + let msgArray = new Uint8Array(buf); + let textDecoder = util.TextDecoder.create('utf-8'); + return textDecoder.decodeToString(msgArray); +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/Tcp2TwoWayTlsWorker.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/Tcp2TwoWayTlsWorker.ets new file mode 100644 index 0000000000000000000000000000000000000000..1de35f20eb02094f6dc660e67ba31f8a588fef8b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/Tcp2TwoWayTlsWorker.ets @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2025 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. + */ + +import { MessageEvents, util, worker } from '@kit.ArkTS'; +import socket from '@ohos.net.socket'; +import fs from '@ohos.file.fs'; +import { BusinessError } from '@kit.BasicServicesKit'; + +const workerPort = worker.workerPort; + +interface TlsTwoWayMessage { + type: string; + fileUri?: string; // For passing file path + serverIp?: string; // Server IP address + serverPort?: number; // Server port + ca?: string; // CA certificate content + cert?: string; // Client certificate content + privateKey?: string; // Client private key content + message?: string; // Message to send + caContent?: string; // CA content read from file + certContent?: string; // Certificate content read from file + privateKeyContent?: string; // Private key content read from file + content?: string; // General file load return content (optional) +} + +let tcpSocket: socket.TCPSocket | null = null; +let tlsSocket: socket.TLSSocket | null = null; + +interface ServerAddress { + address: string; + port: number; + family: number; +} + +interface SecureOptions { + ca: string[]; + cert: string; + key: string; +} + +workerPort.onmessage = (e: MessageEvents) => { + const message: TlsTwoWayMessage = e.data; + handleMessage(message); +}; + +function handleMessage(message: TlsTwoWayMessage) { + switch (message.type) { + case 'loadFile': + if (message.fileUri) { + loadFile(message.fileUri); + } + break; + case 'connectServer': + if (message.serverIp && message.serverPort && message.ca && message.cert && message.privateKey) { + connectToServer(message.serverIp, message.serverPort, message.ca, message.cert, message.privateKey); + } + break; + case 'sendMessage': + if (message.message) { + sendMessageToServer(message.message); + } + break; + } +} + +// [Start two-way_authentication_by_upgrading_tcp_socket_to_tls_socket] +// Handle file loading (CA certificate, client certificate, private key) +function loadFile(fileUri: string) { + try { + let buf = new ArrayBuffer(4096); // Set buffer size + let file = fs.openSync(fileUri, fs.OpenMode.READ_ONLY); + let readLen = fs.readSync(file.fd, buf, { offset: 0 }); + let fileContent = buf2String(buf.slice(0, readLen)); + fs.closeSync(file); + + if (fileUri.includes('ca')) { + workerPort.postMessage({ type: 'fileLoaded', caContent: fileContent }); + } else if (fileUri.includes('cert')) { + workerPort.postMessage({ type: 'fileLoaded', certContent: fileContent }); + } else if (fileUri.includes('key')) { + workerPort.postMessage({ type: 'fileLoaded', privateKeyContent: fileContent }); + } + } catch (e) { + workerPort.postMessage({ type: 'error', message: `File loading failed: ${e.message}` }); + } +} + +// Connect to the server and perform TLS upgrade +function connectToServer(serverIp: string, serverPort: number, ca: string, cert: string, privateKey: string) { + if (tcpSocket) { + workerPort.postMessage({ type: 'connectFailure', message: 'TCP already connected' }); + return; + } + + tcpSocket = socket.constructTCPSocketInstance(); + + let serverAddress: ServerAddress = { address: serverIp, port: serverPort, family: 1 }; + let tcpConnectOptions: socket.TCPConnectOptions = { address: serverAddress, timeout: 6000 }; // Set timeout to 6s + + tcpSocket.connect(tcpConnectOptions).then(() => { + workerPort.postMessage({ type: 'connectSuccess', message: 'TCP connection successful' }); + + // Upgrade to TLS + tlsSocket = socket.constructTLSSocketInstance(tcpSocket); + + let secureOptions: SecureOptions = { + ca: [ca], + cert: cert, + key: privateKey, + }; + + tlsSocket.connect({ + secureOptions, + address: { + address: serverIp, + port: serverPort, + family: 1, + }, + }).then(() => { + workerPort.postMessage({ type: 'tlsConnectSuccess', message: 'TLS mutual authentication connection successful' }); + + tlsSocket!.on('message', (data) => { + let receivedMsg = buf2String(data.message); + workerPort.postMessage({ type: 'serverMessage', message: receivedMsg }); + }); + + tlsSocket!.on('close', () => { + workerPort.postMessage({ type: 'connectionClosed', message: 'TLS connection closed' }); + }); + }).catch((e: BusinessError) => { + workerPort.postMessage({ type: 'tlsConnectFailure', message: `TLS connection failed: ${e.message}` }); + }); + }).catch((e: BusinessError) => { + workerPort.postMessage({ type: 'connectFailure', message: `TCP connection failed: ${e.message}` }); + }); +} + +// Send message to the server +function sendMessageToServer(message: string) { + if (!tlsSocket) { + workerPort.postMessage({ type: 'sendMessageFailure', message: 'TLS connection not established' }); + return; + } + + tlsSocket.send(message + '\r\n').then(() => { + workerPort.postMessage({ type: 'sendMessageSuccess', message: message }); + }).catch((e: Error) => { + workerPort.postMessage({ type: 'sendMessageFailure', message: `Send failed: ${e.message}` }); + }); +} +// [End two-way_authentication_by_upgrading_tcp_socket_to_tls_socket] + +function buf2String(buf: ArrayBuffer): string { + let msgArray = new Uint8Array(buf); + let textDecoder = util.TextDecoder.create('utf-8'); + return textDecoder.decodeToString(msgArray); +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/TcpClientWorker.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/TcpClientWorker.ets new file mode 100644 index 0000000000000000000000000000000000000000..0081bcd428a3b9ecef9192fdbe7f901f207b380e --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/TcpClientWorker.ets @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2025 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. + */ + +import { ErrorEvent, MessageEvents, worker } from '@kit.ArkTS'; +import { TcpMessage } from '../connect/TcpClient' +import { socket } from '@kit.NetworkKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import Logger from '../common/Logger'; + +const workerPort = worker.workerPort; // 获取 workerPort 实例 + +let tcpClient: socket.TCPSocket = socket.constructTCPSocketInstance(); + +function handleMessage(message: TcpMessage) { + switch (message.type) { + case 'sendMessage': + if (message.message) { + sendMessage(message); + } + break; + + case 'disConnect': + disConnect(); + break; + } +} + +function sendMessage(tcpMessage: TcpMessage) { + if (!tcpMessage.clientIp || !tcpMessage.clientPort || !tcpMessage.serverIp || !tcpMessage.serverPort) { + Logger.error('Some required fields are missing in worker.'); + return; + } + + if (!tcpClient) { + workerPort.postMessage({ type: 'sendMessageFailure', message: 'Tcp connection is not established.' }); + return; + } + + // [Start client_applies_tcp_protocol_for_communication] + // 绑定本地服务器地址 + class SocketInfo { + public message: ArrayBuffer = new ArrayBuffer(1); + public remoteInfo: socket.SocketRemoteInfo = {} as socket.SocketRemoteInfo; + } + + tcpClient.on('message', (value: SocketInfo) => { + Logger.info('on message'); + let buffer = value.message; + let dataView = new DataView(buffer); + let str = ''; + for (let i = 0; i < dataView.byteLength; ++i) { + str += String.fromCharCode(dataView.getUint8(i)); + } + Logger.info('on connect received:' + str); + }); + + tcpClient.on('connect', () => { + Logger.info('on connect'); + }); + + tcpClient.on('close', () => { + Logger.info('on close'); + }); + + let ipAddress: socket.NetAddress = {} as socket.NetAddress; + + ipAddress.address = tcpMessage.clientIp!; + ipAddress.port = tcpMessage.clientPort; + tcpClient.bind(ipAddress, (err: BusinessError) => { + if (err) { + Logger.info('bind fail'); + return; + } + Logger.info('bind success'); + + let netAddress: socket.NetAddress = { + address: tcpMessage.serverIp!, + port: tcpMessage.serverPort! + }; + let tcpconnectoptions: socket.TCPConnectOptions = { + address: netAddress, + timeout: 6000 // 超时时间设置 + }; + tcpClient.connect(tcpconnectoptions, () => { + Logger.info('connect success'); + let tcpSendOptions: socket.TCPSendOptions = { + data: tcpMessage.message! + }; + tcpClient.send(tcpSendOptions).then(() => { + Logger.info('send success'); + workerPort.postMessage({ type: 'sendMessageSuccess', message: tcpMessage.message }); + }).catch(() => { + Logger.info('send fail'); + workerPort.postMessage({ type: 'sendMessageFailure', message: 'Sending message failed.' }); + }); + }) + }) +} + +function disConnect() { + tcpClient.close().then(() => { + workerPort.postMessage({ type: 'Connection closed' }); + Logger.info('Connection closed'); + }).catch(() => { + workerPort.postMessage({ type: 'Close connection failed' }); + }); +} + // [End client_applies_tcp_protocol_for_communication] +/** + * Defines the event handler to be called when the worker thread receives a message sent by the host thread. + * The event handler is executed in the worker thread. + */ +workerPort.onmessage = (e: MessageEvents) => { + const message: TcpMessage = e.data; + handleMessage(message); +}; + +/** + * Defines the event handler to be called when the worker receives a message that cannot be deserialized. + * The event handler is executed in the worker thread. + */ +workerPort.onmessageerror = (e: MessageEvents) => { +}; + +/** + * Defines the event handler to be called when an exception occurs during worker execution. + * The event handler is executed in the worker thread. + */ +workerPort.onerror = (e: ErrorEvent) => { +}; + diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/TcpServerWorker.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/TcpServerWorker.ets new file mode 100644 index 0000000000000000000000000000000000000000..524bd4ffc022a15c3d810ca53eb65bd93aa0a755 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/TcpServerWorker.ets @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2025 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. + */ + +import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS'; +import { socket } from '@kit.NetworkKit'; +import { TcpMessage } from '../connect/TcpClient'; +import { BusinessError } from '@kit.BasicServicesKit'; +import Logger from '../common/Logger'; + + +let tcpServer: socket.TCPSocketServer = socket.constructTCPSocketServerInstance(); +let client: socket.TCPSocketConnection | null = null; // 保存客户端连接 + +const workerPort: ThreadWorkerGlobalScope = worker.workerPort; + +function handleMessage(message: TcpMessage) { + switch (message.type) { + case 'startServer': + startServer(message); + break; + + case 'sendMessage': + if (message.message) { + sendMessage(message); + } + break; + + case 'disConnect': + disConnect(); + break; + } +} + +// [Start application_transmits_data_via_tcp_socket_server] +function startServer(tcpMessage: TcpMessage) { + let ipAddress: socket.NetAddress = {} as socket.NetAddress; + ipAddress.address = tcpMessage.serverIp!; // 服务端IP + ipAddress.port = tcpMessage.serverPort; // 端口号 + tcpServer.listen(ipAddress).then(() => { + Logger.info('listen success'); + workerPort.postMessage({ type: 'listen success' }); + }).catch(() => { + Logger.info('listen fail'); + workerPort.postMessage({ type: 'listen fail' }); + }); + + class SocketInfo { + public message: ArrayBuffer = new ArrayBuffer(1); + public remoteInfo: socket.SocketRemoteInfo = {} as socket.SocketRemoteInfo; + } + + tcpServer.on('connect', (client_: socket.TCPSocketConnection) => { + Logger.info('client connected'); + workerPort.postMessage({ type: 'client connected' }); + // 保存客户端连接 + client = client_; + + // 订阅客户端连接的事件 + client.on('close', () => { + Logger.info('client on close success'); + workerPort.postMessage({ type: 'client on close success' }); + }); + + client.on('message', (value: SocketInfo) => { + let buffer = value.message; + let dataView = new DataView(buffer); + let str = ''; + for (let i = 0; i < dataView.byteLength; ++i) { + str += String.fromCharCode(dataView.getUint8(i)); + } + Logger.info('received message--:' + str); + workerPort.postMessage({ type: 'received message--', message: str }); + }); + }); + + // 设置连接超时(例如 30 秒后取消连接) + setTimeout(() => { + tcpServer.off('connect'); + }, 30 * 1000); +} + +function sendMessage(tcpMessage: TcpMessage) { + if (client) { + let tcpSendOptions: socket.TCPSendOptions = {} as socket.TCPSendOptions; + tcpSendOptions.data = tcpMessage.message!; + + client.send(tcpSendOptions).then(() => { + Logger.info('send success'); + workerPort.postMessage({ type: 'send success', message: tcpMessage.message }); + + }).catch((err: Object) => { + Logger.error('send fail: ' + JSON.stringify(err)); + workerPort.postMessage({ type: 'send fail', message: JSON.stringify(err) }); + }); + } else { + Logger.info('No client connected'); + workerPort.postMessage({ type: 'No client connected' }); + + } +} + +function disConnect() { + if (client) { + client.close().then(() => { + Logger.info('close success'); + workerPort.postMessage({ type: 'close success' }); + }).catch((err: BusinessError) => { + Logger.info('close fail'); + workerPort.postMessage({ type: 'close fail', message: JSON.stringify(err) }); + }); + + // 取消事件订阅,设置关闭连接超时(例如 10 秒后取消关闭连接) + setTimeout(() => { + client?.off('message'); + client?.off('close'); + }, 10 * 1000); + } else { + Logger.info('No client to close'); + workerPort.postMessage({ type: 'No client to close' }); + + } +} +// [End application_transmits_data_via_tcp_socket_server] + +/** + * Defines the event handler to be called when the worker thread receives a message sent by the host thread. + * The event handler is executed in the worker thread. + * + * @param e message data + */ +workerPort.onmessage = (e: MessageEvents) => { + const message: TcpMessage = e.data; + handleMessage(message); +}; + +/** + * Defines the event handler to be called when the worker receives a message that cannot be deserialized. + * The event handler is executed in the worker thread. + * + * @param e message data + */ +workerPort.onmessageerror = (e: MessageEvents) => { +} + +/** + * Defines the event handler to be called when an exception occurs during worker execution. + * The event handler is executed in the worker thread. + * + * @param e error message + */ +workerPort.onerror = (e: ErrorEvent) => { +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/TlsServerWorker.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/TlsServerWorker.ets new file mode 100644 index 0000000000000000000000000000000000000000..7906b840c59c76e5309821fdd9c182b88242e54a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/TlsServerWorker.ets @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2025 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. + */ + +import socket from '@ohos.net.socket'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { MessageEvents, util, worker } from '@kit.ArkTS'; +import { TlsServerMessage } from '../connect/TlsServer'; + + +let tlsServer: socket.TLSSocketServer = socket.constructTLSSocketServerInstance(); + +const workerPort = worker.workerPort; + +workerPort.onmessage = (e: MessageEvents) => { + const message:TlsServerMessage = e.data; + handleMessage(message); +}; + +function handleMessage(message: TlsServerMessage) { + switch (message.type) { + case 'startServer': + startTLSserver(message); + break; + default: + break; + } +} + +// [Start application_transmits_encrypted_data_via_tls_socket_server] +function startTLSserver(message: TlsServerMessage) { + try { + const tlsSecureOptions: socket.TLSSecureOptions = { + key: message.serverKey, + cert: message.serverCert, + ca: message.caCert, + protocols: socket.Protocol.TLSv12, + useRemoteCipherPrefer: true, + signatureAlgorithms: 'rsa_pss_rsae_sha256:ECDSA+SHA256', + cipherSuite: 'AES256-SHA256' + }; + + const tlsConnectOptions: socket.TLSConnectOptions = { + address: { address: message.serverIp!, port: message.serverPort }, + secureOptions: tlsSecureOptions, + ALPNProtocols: ['spdy/1', 'http/1.1'] + }; + + tlsServer.listen(tlsConnectOptions).then(() => { + workerPort.postMessage({ type: 'serverStarted', status: 'TLS server started successfully' }); + + tlsServer.on('connect', (client: socket.TLSSocketConnection) => { + handleClientConnection(client); + }); + }).catch((err: BusinessError) => { + workerPort.postMessage({ type: 'error', message: `Failed to start TLS server: ${err.message}` }); + }); + } catch (err) { + workerPort.postMessage({ type: 'error', message: `Error starting server: ${err.message}` }); + } +} + +function handleClientConnection(client: socket.TLSSocketConnection) { + client.on('message', (value) => { + let messageView = bufToString(value.message); + workerPort.postMessage({ type: 'clientMessage', message: messageView }); + }); + + client.send('Hello, client!').then(() => { + workerPort.postMessage({ type: 'messageSent', message: 'Message sent successfully' }); + }).catch((err: Error) => { + workerPort.postMessage({ type: 'error', message: `Failed to send message: ${err.message}` }); + }); + + client.close().then(() => { + workerPort.postMessage({ type: 'connectionClosed', message: 'Connection closed successfully' }); + }).catch((err: Error) => { + workerPort.postMessage({ type: 'error', message: `Failed to close connection: ${err.message}` }); + }); +} +// [End application_transmits_encrypted_data_via_tls_socket_server] + +function bufToString(buf: ArrayBuffer): string { + let msgArray = new Uint8Array(buf); + let textDecoder = util.TextDecoder.create('utf-8'); + return textDecoder.decodeToString(msgArray); +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/TwoWayTlsWorker.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/TwoWayTlsWorker.ets new file mode 100644 index 0000000000000000000000000000000000000000..c9484a1cda94266b56792c92bfc2ba488f4357f0 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/TwoWayTlsWorker.ets @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2025 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. + */ + +import { MessageEvents, util, worker } from '@kit.ArkTS'; +import socket from '@ohos.net.socket'; +import fs from '@ohos.file.fs'; +import { BusinessError } from '@kit.BasicServicesKit'; + +// 获取 workerPort 实例 +const workerPort = worker.workerPort; + +// 连接到服务器 +interface LocalAddress { + address: string; + family: number; +} + +interface ServerAddress { + address: string; + port: number; + family: number; +} + +interface TlsTwoWayMessage { + type: string; + fileUri?: string; // 用于传递文件路径 + serverIp?: string; // 服务器 IP 地址 + serverPort?: number; // 服务器端口 + ca?: string; // CA 证书内容 + cert?: string; // 客户端证书内容 + privateKey?: string; // 客户端私钥内容 + message?: string; // 要发送的消息 + caContent?: string; // 从文件中读取的 CA 内容 + certContent?: string; // 从文件中读取的证书内容 + privateKeyContent?: string; // 从文件中读取的私钥内容 +} + +// 共享变量 +let tlsSocket: socket.TLSSocket | null = null; + +// 监听主线程消息 +workerPort.onmessage = (e: MessageEvents) => { + const message: TlsTwoWayMessage = e.data; + handleMessage(message); +}; + +// 处理不同类型的任务 +function handleMessage(message: TlsTwoWayMessage) { + switch (message.type) { + case 'loadFile': + if (message.fileUri) { + loadFile(message.fileUri); + } + break; + + case 'connectServer': + if (message.serverIp && message.serverPort && message.ca && message.cert && message.privateKey) { + connectToServer(message.serverIp, message.serverPort, message.ca, message.cert, message.privateKey); + } + break; + + case 'sendMessage': + if (message.message) { + sendMessageToServer(message.message); + } + break; + } +} + +// [Start two-way_authentication_via_tls_socket] +// 读取文件并返回内容 +function loadFile(fileUri: string) { + try { + let file = fs.openSync(fileUri, fs.OpenMode.READ_ONLY); + let buf = new ArrayBuffer(1024 * 4); + let readLen = fs.readSync(file.fd, buf, { offset: 0 }); + let content = buf2String(buf.slice(0, readLen)); + fs.closeSync(file); + + // 根据文件类型返回不同的内容 + let response: TlsTwoWayMessage = { type: 'fileLoaded' }; + if (fileUri.includes('ca')) { + response.caContent = content; + } else if (fileUri.includes('cert')) { + response.certContent = content; + } else if (fileUri.includes('key')) { + response.privateKeyContent = content; + } + + workerPort.postMessage(response); + } catch (e) { + workerPort.postMessage({ type: 'error', message: `Failed to load file: ${e.message}` }); + } +} + +function connectToServer(serverIp: string, serverPort: number, ca: string, cert: string, privateKey: string) { + if (tlsSocket) { + workerPort.postMessage({ type: 'connectFailure', message: 'Socket already connected' }); + return; + } + + tlsSocket = socket.constructTLSSocketInstance(); + + let localAddress: LocalAddress = { address: '0.0.0.0', family: 1 }; + tlsSocket.bind(localAddress).then(() => { + let serverAddress: ServerAddress = { address: serverIp, port: serverPort, family: 1 }; + let opt: socket.TLSSecureOptions = { + ca: [ca], + cert: cert, + key: privateKey + }; + + tlsSocket!.connect({ address: serverAddress, secureOptions: opt }).then(() => { + workerPort.postMessage({ type: 'connectSuccess', status: 'Connected successfully' }); + + // 监听消息 + tlsSocket!.on('message', (data) => { + let receivedMsg = buf2String(data.message); + workerPort.postMessage({ type: 'serverMessage', message: receivedMsg }); + }); + + // 监听连接关闭 + tlsSocket!.on('close', () => { + tlsSocket = null; // 清理连接 + workerPort.postMessage({ type: 'connectionClosed', status: 'Connection closed by server' }); + }); + }).catch((e: BusinessError) => { + tlsSocket = null; + workerPort.postMessage({ type: 'connectFailure', status: `Failed to connect: ${e.message}` }); + }); + }).catch((e: BusinessError) => { + tlsSocket = null; + workerPort.postMessage({ type: 'connectFailure', status: `Failed to bind socket: ${e.message}` }); + }); +} + +// 发送消息到服务器 +function sendMessageToServer(message: string) { + if (!tlsSocket) { + workerPort.postMessage({ type: 'sendMessageFailure', message: 'TLS connection is not established.' }); + return; + } + + tlsSocket.send(message + '\r\n').then(() => { + workerPort.postMessage({ type: 'sendMessageSuccess', message: message }); + }).catch((e: Error) => { + workerPort.postMessage({ type: 'sendMessageFailure', message: e.message }); + }); +} +// [End two-way_authentication_via_tls_socket] + +// ArrayBuffer 转 utf8 字符串 +function buf2String(buf: ArrayBuffer): string { + let msgArray = new Uint8Array(buf); + let textDecoder = util.TextDecoder.create('utf-8'); + return textDecoder.decodeToString(msgArray); +} + + diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/UdpWorker.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/UdpWorker.ets new file mode 100644 index 0000000000000000000000000000000000000000..d8a45253b2d6b5d14c3637be6a5eb4c4e963bc2c --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/ets/workers/UdpWorker.ets @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025 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. + */ + +import { MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS'; +import { socket } from '@kit.NetworkKit'; +import { TcpMessage } from '../connect/TcpClient'; +import { BusinessError } from '@kit.BasicServicesKit'; + +let udpServer: socket.UDPSocket = socket.constructUDPSocketInstance(); + +const workerPort: ThreadWorkerGlobalScope = worker.workerPort; + +function handleMessage(message: TcpMessage) { + switch (message.type) { + case 'startServer': + startServer(message); + break; + + case 'sendMessage': + if (message.message) { + sendMessage(message); + } + break; + + case 'disConnect': + disConnect(); + break; + } +} + +// [Start apply_udp_protocol_for_communication] +function startServer(tcpMessage: TcpMessage) { + let ipAddress: socket.NetAddress = {} as socket.NetAddress; + ipAddress.address = tcpMessage.serverIp!; // 服务端IP + ipAddress.port = tcpMessage.serverPort; // 端口号 + + udpServer.bind(ipAddress, (err: BusinessError) => { + if (err) { + workerPort.postMessage({ type: 'listen fail' }); + return; + } + + workerPort.postMessage({ type: 'listen success' }); + + udpServer.on('message', (value: socket.SocketMessageInfo) => { + let messageView = ''; + let uint8Array = new Uint8Array(value.message); + for (let i = 0; i < uint8Array.length; i++) { + messageView += String.fromCharCode(uint8Array[i]); + } + + workerPort.postMessage({ type: 'received message--', message: messageView }); + }); + }); +} + +function sendMessage(tcpMessage: TcpMessage) { + let netAddress: socket.NetAddress = { address: tcpMessage.clientIp!, port: tcpMessage.clientPort }; + + let sendOptions: socket.UDPSendOptions = { + data: tcpMessage.message!, + address: netAddress + }; + + udpServer.send(sendOptions, (err: BusinessError) => { + if (err) { + workerPort.postMessage({ type: 'send fail', message: JSON.stringify(err) }); + } else { + workerPort.postMessage({ type: 'send success', message: tcpMessage.message }); + } + }); +} + +function disConnect() { + udpServer.close((err: BusinessError) => { + if (err) { + workerPort.postMessage({ type: 'close fail', message: JSON.stringify(err) }); + } else { + workerPort.postMessage({ type: 'close success' }); + } + }); +} +// [End apply_udp_protocol_for_communication] + +workerPort.onmessage = (e: MessageEvents) => { + const message: TcpMessage = e.data; + handleMessage(message); +}; diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/module.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..0cfdde1ecc15c50887fc70a721bae4167899885e --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/module.json5 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:app_name", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.INTERNET", + "reason": "$string:grant_internet", + "usedScene": { + "abilities": [ + "MainAbility" + ], + "when": "inuse" + } + }, + { + "name": "ohos.permission.GET_WIFI_INFO", + "reason": "$string:grant_get_wifi_info", + "usedScene": { + "abilities": [ + "MainAbility" + ], + "when": "inuse" + } + } + ] + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/element/color.json b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..5ae3a13538a6a90f51952b9965be58adb9f7fd9f --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/element/string.json @@ -0,0 +1,312 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "Socket", + "value": "Socket连接" + }, + { + "name": "TcpClient", + "value": "Tcp客户端" + }, + { + "name": "TcpServer", + "value": "Tcp服务端" + }, + { + "name": "Udp", + "value": "Udp客户端&服务端" + }, + { + "name": "Multicast", + "value": "多播通信" + }, + { + "name": "LocalSocket", + "value": "LocalSocket 数据传输" + }, + { + "name": "OneWayTls", + "value": "单向TLS Socket 加密数据传输" + }, + { + "name": "TwoWayTls", + "value": "双向TLS Socket 加密数据传输" + }, + { + "name": "Tcp2OneWayTls", + "value": "TCP Socket 升级为单向TLS Socket 加密数据传输" + }, + { + "name": "Tcp2TwoWayTls", + "value": "TCP Socket 升级为双向TLS Socket加密数据传输" + }, + { + "name": "TlsServer", + "value": "TLS Socket Server 加密数据传输" + }, + { + "name": "Parameter_error", + "value": "参数错误" + }, + { + "name": "sendMessageSuccess", + "value": "发送消息成功" + }, + { + "name": "sendMessageFailure", + "value": "发送消息失败" + }, + { + "name": "Close_connection_failed", + "value": "连接关闭失败" + }, + { + "name": "TcpClient_does_not_exist", + "value": "Tcp客户端不存在" + }, + { + "name": "No_Client_Connected", + "value": "无客户端连接" + }, + { + "name": "No_client_to_close", + "value": "无可关闭客户端" + }, + { + "name": "Socket_Communication_Example", + "value": "LocalSocket通信示例" + }, + { + "name": "Server_Status", + "value": "服务端状态: " + }, + { + "name": "Client_Status", + "value": "客户端状态: " + }, + { + "name": "Start_Local_Socket_Communication", + "value": "开始Local_Socket通信" + }, + { + "name": "Start_Local_Socket_Server", + "value": "启动LocalSocket服务器" + }, + { + "name": "Start_multicast_communication", + "value": "开始多播通信: " + }, + { + "name": "TLS_Communication_Example", + "value": "TLS通讯示例" + }, + { + "name": "Local_IP_Address", + "value": "本地IP地址" + }, + { + "name": "Server_Address", + "value": "服务端地址" + }, + { + "name": "CA", + "value": "CA(输入或者加载)" + }, + { + "name": "Select", + "value": "选择" + }, + { + "name": "Connect", + "value": "连接" + }, + { + "name": "Enter_The_Message_To_Send", + "value": "输入消息" + }, + { + "name": "Send", + "value": "发送" + }, + { + "name": "Load", + "value": "加载" + }, + { + "name": "UDP_Socket_Communication_Example", + "value": "UDP连接示例" + }, + { + "name": "UDP_Client_Communication", + "value": "启动UDP连接" + }, + { + "name": "ConnectExternalUDPServer", + "value": "连接UDP服务器" + }, + { + "name": "SendUDPMessage", + "value": "发送消息" + }, + { + "name": "CloseUDPConnection", + "value": "关闭UDP连接" + }, + { + "name": "Enter_Server_Ip", + "value": "输入服务端IP" + }, + { + "name": "Enter_Server_Port", + "value": "输入服务端Port" + }, + { + "name": "localIp", + "value": "本地IP地址:" + }, + { + "name": "serverIp", + "value": "服务端地址:" + }, + { + "name": "chooseCA", + "value": "选择 CA 证书:" + }, + { + "name": "select", + "value": "选择" + }, + { + "name": "connect", + "value": "连接" + }, + { + "name": "Enter_Message", + "value": "输入消息" + }, + { + "name": "caSelected", + "value": "已选择 CA 文件: " + }, + { + "name": "caSelectFailed", + "value": "选择 CA 文件失败: " + }, + { + "name": "caLoadedSuccess", + "value": "CA 文件加载成功" + }, + { + "name": "caContentEmpty", + "value": "CA 文件内容为空" + }, + { + "name": "caLoadFailed", + "value": "加载 CA 文件失败: " + }, + { + "name": "tcpConnectSuccess", + "value": "TCP 连接成功" + }, + { + "name": "tcpConnectFailed", + "value": "TCP 连接失败: " + }, + { + "name": "tlsConnectSuccess", + "value": "TLS 连接成功" + }, + { + "name": "tlsConnectFailed", + "value": "TLS 连接失败: " + }, + { + "name": "messageSent", + "value": "我: " + }, + { + "name": "sendMessageFailed", + "value": "消息发送失败: " + }, + { + "name": "tlsConnectionClosed", + "value": "TLS 连接已关闭" + }, + { + "name": "serverPort", + "value": "服务端端口:" + }, + { + "name": "send", + "value": "发送" + }, + { + "name": "status", + "value": "状态:" + }, + { + "name": "StartMulticast", + "value": "启动多播通信" + }, + { + "name": "Start_Server", + "value": "启动服务器" + }, + { + "name": "Message_History", + "value": "消息记录" + }, + { + "name": "Enter_Client_Ip", + "value": "输入客户端IP" + }, + { + "name": "Enter_Client_Port", + "value": "输入客户端Port" + }, + { + "name": "Close_Connection", + "value": "关闭连接" + }, + { + "name": "CA_File", + "value": "CA证书文件" + }, + { + "name": "Client_Cert", + "value": "客户端证书" + }, + { + "name": "Client_Key", + "value": "客户端私钥" + }, + { + "name": "Select_Server_Key", + "value": "选择服务器私钥" + }, + { + "name": "Select_Server_Cert", + "value": "选择服务器证书" + }, + { + "name": "grant_internet", + "value": "网络使用权限" + }, + { + "name": "grant_get_wifi_info", + "value": "获取wifi信息权限" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/back.svg b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/back.svg new file mode 100644 index 0000000000000000000000000000000000000000..0614389ee6f66cd188d47ba045aa83b4babee356 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/back.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/background.png b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/background.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/foreground.png b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/foreground.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/layered_image.json b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/right.svg b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/right.svg new file mode 100644 index 0000000000000000000000000000000000000000..f743acf7d50d9b4818e54d14596d2f71243ba7f2 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/startIcon.png b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/media/startIcon.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/profile/backup_config.json b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/profile/main_pages.json b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..5bc6466836740e5e4a3ca6d47846e13355380574 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,15 @@ +{ + "src": [ + "pages/Index", + "connect/TcpClient", + "connect/TcpServer", + "connect/UdpClient_Server", + "connect/Multicast", + "connect/LocalSocket", + "connect/OneWayTls", + "connect/TwoWayTls", + "connect/Tcp2OneWayTls", + "connect/Tcp2TwoWayTls", + "connect/TlsServer" + ] +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/dark/element/color.json b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/dark/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..79b11c2747aec33e710fd3a7b2b3c94dd9965499 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/main/resources/dark/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#000000" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/ohosTest/ets/test/Ability.test.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..e086fd84551689001a34e4908605065f38542216 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2025 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. + */ + +import { describe, it, expect } from '@ohos/hypium'; +import { abilityDelegatorRegistry, Driver, MatchPattern, ON } from '@kit.TestKit'; +import Logger from '../../../main/ets/common/Logger'; +import { UIAbility, Want} from '@kit.AbilityKit'; + +const delegator = abilityDelegatorRegistry.getAbilityDelegator(); + +const BUNDLE = 'Socket_'; +const BUNDLE_NAME = 'com.samples.Socket'; +const ABILITY_NAME: string = 'EntryAbility'; +let driver: Driver = Driver.create(); +const DELAY_MS = 3000; // 超时设置3s + +async function goBack(driver: Driver) { + try { + await driver.assertComponentExist(ON.id('btnBack')); + let backButton = await driver.findComponent(ON.id('btnBack')); + await backButton.click(); + await driver.delayMs(DELAY_MS); + } catch (e) { + Logger.error(`Failed to go back: ${e.message}`); + } +} + +async function checkAndClickByText(text: string) { + try { + await driver.assertComponentExist(ON.text(text, MatchPattern.CONTAINS)); + let component = await driver.findComponent(ON.text(text)); + await component.click(); + await driver.delayMs(DELAY_MS); + } catch (e) { + Logger.error(`Failed to click on text: ${text}, Error: ${e.message}`); + } +} + +function ResourceToString(resource: Resource): string { + return getContext().resourceManager.getStringSync(resource); +} + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + + /** + * @tc.number StartAbility_001 + * @tc.name StartAbility_001 + * @tc.desc 启动Ability + */ + it(BUNDLE + 'StartAbility_001', 0, async (done: Function) => { + Logger.info(`${BUNDLE} StartAbility_001 begin`); + const want: Want = { + bundleName: BUNDLE_NAME, + abilityName: ABILITY_NAME + }; + try { + await delegator.startAbility(want); + await driver.delayMs(DELAY_MS); + const ability: UIAbility = await delegator.getCurrentTopAbility(); + expect(ability.context.abilityInfo.name).assertEqual(ABILITY_NAME); + } catch (error) { + Logger.error(`${BUNDLE} StartAbility_001 failed: ${error.message}`); + } + done(); + Logger.info(`${BUNDLE} StartAbility_001 end`); + }); + + /** + * @tc.number Multicast_001 + * @tc.name Multicast_001 + * @tc.desc 测试多播通信 + */ + it(BUNDLE + 'Multicast_001', 0, async (done: Function) => { + Logger.info(`${BUNDLE} Multicast_001 begin`); + await checkAndClickByText(ResourceToString($r('app.string.Multicast'))); + await checkAndClickByText(ResourceToString($r('app.string.StartMulticast'))); + try { + let resultText = await driver.findComponent(ON.id('multicast_communication')); + let result = await resultText.getText(); + expect(result).assertContain('Joined'); + } catch (error) { + Logger.error(`Multicast communication failed: ${error.message}`); + } + await goBack(driver); + done(); + Logger.info(`${BUNDLE} Multicast_001 end`); + }); + + /** + * @tc.number LocalSocket_001 + * @tc.name LocalSocket_001 + * @tc.desc 启动本地服务器 + */ + it(BUNDLE + 'LocalSocket_001', 0, async (done: Function) => { + Logger.info(`${BUNDLE} LocalSocket_001 begin`); + await checkAndClickByText(ResourceToString($r('app.string.LocalSocket'))); + await checkAndClickByText(ResourceToString($r('app.string.Start_Local_Socket_Server'))); + try { + let resultText = await driver.findComponent(ON.id('serverStatus')); + let result = await resultText.getText(); + expect(result).assertContain('Listening'); + } catch (error) { + Logger.error(`LocalSocket server failed: ${error.message}`); + } + await goBack(driver); + done(); + Logger.info(`${BUNDLE} LocalSocket_001 end`); + }); + + /** + * @tc.number LocalSocket_002 + * @tc.name LocalSocket_002 + * @tc.desc 启动本地客户端 + */ + it(BUNDLE + 'LocalSocket_002', 0, async (done: Function) => { + Logger.info(`${BUNDLE} LocalSocket_002 begin`); + await checkAndClickByText(ResourceToString($r('app.string.LocalSocket'))); + await checkAndClickByText(ResourceToString($r('app.string.Start_Local_Socket_Communication'))); + try { + let resultText = await driver.findComponent(ON.id('clientStatus')); + let result = await resultText.getText(); + expect(result).assertContain('Connected'); + } catch (error) { + Logger.error(`LocalSocket client failed: ${error.message}`); + } + await goBack(driver); + done(); + Logger.info(`${BUNDLE} LocalSocket_002 end`); + }); + }); +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/ohosTest/ets/test/List.test.ets b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..1eac52fcebe8958e19a7b8fed2e8f39c520a3e42 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ + +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/ohosTest/module.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c3fd9dda3040d888d9d8b0b62bcb5d3b6fbeb614 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/hvigor/hvigor-config.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1e473e424320d4e68b16737b289f5c851bb19d36 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/hvigor/hvigor-config.json5 @@ -0,0 +1,22 @@ +{ + "modelVersion": "5.0.1", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/hvigorfile.ts b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/oh-package.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e41bae026aab3b50d0abb42fece08ba43b4a772b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.1", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.19", + "@ohos/hamock": "1.0.0" + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/ohosTest.md b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..336b5d8ee28b1897f13540f162ec88dbb89f8175 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/ohosTest.md @@ -0,0 +1,10 @@ +# 测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +| -------------- | ------------ | -------------------------------- | --------------------------------------- | -------- | -------- | +| 多播通信测试 | 设备正常运行 | 点击"StartMulticast"按钮 | 多播通信成功,显示"Joined"信息 | 是 | pass | +| 启动本地服务器 | 设备正常运行 | 点击"启动Local_Socket服务器"按钮 | 本地服务器启动成功,显示"Listening"信息 | 是 | pass | +| 启动本地客户端 | 设备正常运行 | 点击"开启LocalSocket通信"按钮 | 本地客户端连接成功,显示"Connected"信息 | 是 | pass | + diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/screenshots/MainPage.jpeg b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/screenshots/MainPage.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..c391ca19eaabbf8a3eca3daaf877f567e1bf63e7 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/screenshots/MainPage.jpeg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/screenshots/TcpClient.jpeg b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/screenshots/TcpClient.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..bac7fa2242c96e9c813d85640f48f22acf561ee1 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/screenshots/TcpClient.jpeg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/screenshots/TcpServer.jpeg b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/screenshots/TcpServer.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..923f7ef46bd24fd343b8b95aed83249efef4ee9f Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/screenshots/TcpServer.jpeg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/Socket/screenshots/UDP.jpeg b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/screenshots/UDP.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..b3508bec7bced5b0778c108e5096b86f0e95efc6 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/Socket/screenshots/UDP.jpeg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/.gitignore b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/AppScope/app.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c6ec69426cf116801b3aab4250c4eb806fc52b14 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "bundleName": "com.samples.websocket_c", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/AppScope/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..6fe6871627ebf3d99b1dd0e23ae322172d13c84d --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "WebSocket_C" + } + ] +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/AppScope/resources/base/media/app_icon.png b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/AppScope/resources/base/media/app_icon.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/README.md b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ab5d3f909d36aa7bc62062aaa840924486002e75 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/README.md @@ -0,0 +1,79 @@ +# WebSocket连接(C/C++) + +### 介绍 + +本示例依照指南 开发->系统->网络->Network Kit(网络服务->Network Kit数据传输能力->[WebSocket连接(C/C++)](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/network/native-websocket-guidelines.md)进行编写。 +本示例主要展示了WebSocket模块可以建立服务器与客户端的双向连接的相关基础功能。通过在源文件中将相关接口封装,再在ArkTS层对封装的接口进行调用,以实现建立与WebSocket服务器的连接、发送消息给WebSocket服务器、关闭WebSocket连接。 + +### 效果预览 + +| 程序主页 | 连接成功 | 发送消息 | 断开连接 | +| ------------------------------------------- | --------------------------------------------- | ------------------------------------------ | ----------------------------------------- | +| ![image](./screenshots/WebSocket_Index.jpg) | ![image](./screenshots/WebSocket_Connect.jpg) | ![image](./screenshots/WebSocket_Send.jpg) | ![image](screenshots/WebSocket_Close.jpg) | + +使用说明: + +1. 在URL输入框中,输入`ws://`或`wss://`开头的WebSocket URL,点击连接按钮进行连接。 +2. 在发送内容输入框里输入要发送给服务器的内容,点击发送按钮发送。 +3. 点击关闭按钮,WebSocket连接断开,可以重新输入新的WebSocket URL。 + +### 工程目录 + +``` +entry/src/main/ +│ +│---cpp +│ │ CMakeLists.txt +│ │ napi_init.cpp // 链接层 +│ │ +│ └─types +│ └─libentry +│ Index.d.ts +│ oh-package.json5 +| +|---entryability +| │---EntryAbility.ets +|---entrybackupability +│ |---EntryBackupAbility.ets +|---pages +│ |---Index.ets // 主页 +``` + +### 具体实现 + +1. 配置`CMakeLists.txt`,本模块需要用到的共享库是`libnet_websocket.so`。 +2. 编写调用该API的代码,接受ArkTS传递过来的url字符串参数,创建WebSocket对象指针后,检查连接到服务器是否成功。 +3. WebSocket连接通过 `testWebsocket.Connect(this.wsUrl)` 方法发起,默认连接到 `ws://echo.websocket.org` +4. 消息发送使用`testWebsocket.Send(this.content)`。 +5. 断开连接使用`testWebsocket.Close()`关闭 WebSocket 连接。 + +### 相关权限 + +[ohos.permission.INTERNET](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-all.md#ohospermissioninternet) + +### 依赖 + +不涉及。 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:华为手机。 + +2. HarmonyOS系统:HarmonyOS 5.0.2 Release及以上。 +3. HarmonyOS SDK版本:HarmonyOS 5.0.2 Release及以上。 +4. 本示例需要使用DevEco Studio Release(5.0.5.306)及以上版本才可编译运行。 + +5. 本示例需要设备联网使用。 + + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C > .git/info/sparse-checkout +git remote add origin https://gitee.com/harmonyos_samples/guide-snippets.git +git pull origin master +``` diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/build-profile.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ed831f1db622c015dcc8fd8326adc7b9ff1ce4fc --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/build-profile.json5 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.2(14)", + "targetSdkVersion": "5.0.2(14)", + "runtimeOS": "HarmonyOS" + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/.gitignore b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/build-profile.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..7b41a885b3a105d0109b3ce50bec7fb14e965d8d --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/build-profile.json5 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "apiType": "stageMode", + "buildOption": { + "externalNativeOptions": { + "path": "./src/main/cpp/CMakeLists.txt", + "arguments": "", + "cppFlags": "", + "abiFilters": ["arm64-v8a", "x86_64"] + }, + + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + }, + "nativeLib": { + "debugSymbol": { + "strip": true, + "exclude": [] + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/hvigorfile.ts b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4f43d54667f8327c367c8096bd08bb8c75aff54 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/obfuscation-rules.txt b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/oh-package.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..118bdd4fe7699368a010e04c24f5bfc887cf1298 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/oh-package.json5 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "libentry.so": "file:./src/main/cpp/types/libentry" + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/cpp/CMakeLists.txt b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc72b1df1bf23c0197063b1c7328c1cfd6d3558c --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/cpp/CMakeLists.txt @@ -0,0 +1,15 @@ +# the minimum version of CMake. +cmake_minimum_required(VERSION 3.5.0) +project(WebSocket_C) + +set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +if(DEFINED PACKAGE_FIND_FILE) + include(${PACKAGE_FIND_FILE}) +endif() + +include_directories(${NATIVERENDER_ROOT_PATH} + ${NATIVERENDER_ROOT_PATH}/include) + +add_library(entry SHARED napi_init.cpp) +target_link_libraries(entry PUBLIC libace_napi.z.so libnet_websocket.so PUBLIC libhilog_ndk.z.so) diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/cpp/napi_init.cpp b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/cpp/napi_init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6842b775298695bd3ee1beaace76db2c3df65c93 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/cpp/napi_init.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2025 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 "network/netstack/net_websocket.h" +#include "network/netstack/net_websocket_type.h" +#include "hilog/log.h" + +#include + +#undef LOG_DOMAIN +#undef LOG_TAG +#define LOG_DOMAIN 0x3200 // 全局domain宏,标识业务领域 +#define LOG_TAG "WSDEMO" // 全局tag宏,标识模块日志tag + +// [Start websocket_build_project] +// WebSocket客户端全局变量 +static struct WebSocket *g_client = nullptr; + +static void onOpen(struct WebSocket *wsClient, WebSocket_OpenResult openResult) +{ + (void)wsClient; + OH_LOG_INFO(LOG_APP, "onOpen: code: %{public}u, reason: %{public}s", openResult.code, openResult.reason); +} + +static void onMessage(struct WebSocket *wsClient, char *data, uint32_t length) +{ + (void)wsClient; + char *tmp = new char[length + 1]; + for (uint32_t i = 0; i < length; i++) { + tmp[i] = data[i]; + } + tmp[length] = '\0'; + OH_LOG_INFO(LOG_APP, "onMessage: len: %{public}u, data: %{public}s", length, tmp); +} + +static void onError(struct WebSocket *wsClient, WebSocket_ErrorResult errorResult) +{ + (void)wsClient; + OH_LOG_INFO(LOG_APP, "onError: code: %{public}u, message: %{public}s", errorResult.errorCode, + errorResult.errorMessage); +} + +static void onClose(struct WebSocket *wsClient, WebSocket_CloseResult closeResult) +{ + (void)wsClient; + OH_LOG_INFO(LOG_APP, "onClose: code: %{public}u, reason: %{public}s", closeResult.code, closeResult.reason); +} + +static napi_value ConnectWebsocket(napi_env env, napi_callback_info info) +{ + size_t argc = 2; + napi_value args[2] = {nullptr}; + napi_value result; + + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + size_t length = 0; + napi_status status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &length); + if (status != napi_ok) { + napi_get_boolean(env, false, &result); + return result; + } + + if (g_client != nullptr) { + OH_LOG_INFO(LOG_APP, "there is already one websocket client running."); + napi_get_boolean(env, false, &result); + return result; + } + char *buf = new char[length + 1]; + std::memset(buf, 0, length + 1); + napi_get_value_string_utf8(env, args[0], buf, length + 1, &length); + // 创建WebSocket Client对象指针 + g_client = OH_WebSocketClient_Constructor(onOpen, onMessage, onError, onClose); + if (g_client == nullptr) { + delete[] buf; + napi_get_boolean(env, false, &result); + return result; + } + // 连接buf存放的URL对应的WebSocket服务器 + int connectRet = OH_WebSocketClient_Connect(g_client, buf, {}); + + delete[] buf; + napi_get_boolean(env, connectRet == 0, &result); + return result; +} +// [End websocket_build_project] + +static napi_value SendMessage(napi_env env, napi_callback_info info) +{ + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_value result; + + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + size_t length = 0; + napi_status status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &length); + if (status != napi_ok) { + napi_create_int32(env, -1, &result); + return result; + } + + if (g_client == nullptr) { + OH_LOG_INFO(LOG_APP, "websocket client not connected."); + napi_create_int32(env, WebSocket_ErrCode::WEBSOCKET_CLIENT_NULL, &result); + return result; + } + char *buf = new char[length + 1]; + std::memset(buf, 0, length + 1); + napi_get_value_string_utf8(env, args[0], buf, length + 1, &length); + // 发送buf中的消息给服务器 + int ret = OH_WebSocketClient_Send(g_client, buf, length); + + delete[] buf; + napi_create_int32(env, ret, &result); + return result; +} + +static napi_value CloseWebsocket(napi_env env, napi_callback_info info) +{ + napi_value result; + if (g_client == nullptr) { + OH_LOG_INFO(LOG_APP, "websocket client not connected."); + napi_create_int32(env, -1, &result); + return result; + } + // 关闭WebSocket连接 + int ret = OH_WebSocketClient_Close(g_client, { + .code = 0, + .reason = "Actively Close", + }); + // 释放WebSocket资源并置空 + OH_WebSocketClient_Destroy(g_client); + g_client = nullptr; + napi_create_int32(env, ret, &result); + return result; +} + +EXTERN_C_START +static napi_value Init(napi_env env, napi_value exports) +{ + napi_property_descriptor desc[] = { + {"Connect", nullptr, ConnectWebsocket, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"Send", nullptr, SendMessage, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"Close", nullptr, CloseWebsocket, nullptr, nullptr, nullptr, napi_default, nullptr}, + }; + napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); + return exports; +} +EXTERN_C_END + +static napi_module demoModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "entry", + .nm_priv = ((void *)0), + .reserved = {0}, +}; + +extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); } \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/cpp/types/libentry/Index.d.ts b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/cpp/types/libentry/Index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..c3dc2c986bc75d41418a0cf5d628a9da352c4102 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/cpp/types/libentry/Index.d.ts @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025 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. + */ + +export const Connect: (url: string) => boolean; +export const Send: (data: string) => number; +export const Close: () => number; \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/cpp/types/libentry/oh-package.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/cpp/types/libentry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..6abf3b7c20f22c62aaac6a995a25cae672f73f35 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/cpp/types/libentry/oh-package.json5 @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "libentry.so", + "types": "./Index.d.ts", + "version": "1.0.0", + "description": "Please describe the basic information." +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/ets/entryability/EntryAbility.ets b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..965554bb3a331e2418180b86da1993baf5e8d536 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 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. + */ + +import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +}; \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..b1e212947256c5533c7b06285a597c94f840a6e3 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/ets/pages/Index.ets b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..85f2d02c1d79eeb6cc697e251a3919094bf8f714 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2025 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. + */ + +import testWebsocket from 'libentry.so'; +import { promptAction } from '@kit.ArkUI'; + +@Entry +@Component +struct Index { + @State wsUrl: string = ''; + @State content: string = ''; + @State connecting: boolean = false; + + build() { + Navigation() { + Column() { + Column() { + Text($r('app.string.WebSocket_address')) + .fontColor(Color.Gray) + .textAlign(TextAlign.Start) + .width('100%') + TextInput() + .width('100%') + .id('textInput_address') + .onChange((value) => { + this.wsUrl = value; + }) + } + .margin({ + bottom: 16 // 与底间隔 + }) + .padding({ + left: 16, // 与左间隔 + right: 16 // 与右间隔 + }) + + Column() { + Text($r('app.string.Content')) + .fontColor(Color.Gray) + .textAlign(TextAlign.Start) + .width('100%') + TextInput() + .width('100%') + .id('textInput_content') + .enabled(this.connecting) + .onChange((value) => { + this.content = value; + }) + } + .margin({ + bottom: 16 // 与底间隔 + }) + .padding({ + left: 16, // 与左间隔 + right: 16 // 与右间隔 + }) + + Blank() + Column({ + space: 12 // 占位空间 + }) { + Button($r('app.string.Connect')) + .id('Connect') + .enabled(!this.connecting) + .onClick(() => { + let connRet = testWebsocket.Connect(this.wsUrl); + if (connRet) { + this.connecting = true; + promptAction.showToast({ + message: $r('app.string.Connect_successfully'), + duration: 4000, // 持续时间 + bottom: 300 // 与底间隔 + }); + } else { + this.connecting = false; + promptAction.showToast({ + message: $r('app.string.Connect_failed'), + duration: 4000, // 持续时间 + bottom: 300 // 与底间隔 + }); + } + }) + Button($r('app.string.Send')) + .id('Send') + .enabled(this.connecting) + .onClick(() => { + testWebsocket.Send(this.content); + promptAction.showToast({ + message: $r('app.string.Send_message_successfully'), + duration: 4000, // 持续时间 + bottom: 300 // 与底间隔 + }); + }) + Button($r('app.string.Close')) + .id('Close') + .enabled(this.connecting) + .onClick(() => { + let closeResult = testWebsocket.Close(); + if (closeResult != -1) { + promptAction.showToast({ + message: $r('app.string.Close_connection'), + duration: 4000, // 持续时间 + bottom: 300 // 与底间隔 + }); + this.connecting = false; + this.wsUrl = ''; + this.content = ''; + } else { + promptAction.showToast({ + message: $r('app.string.Close_connection_failed'), + duration: 4000, // 持续时间 + bottom: 300 // 与底间隔 + }); + } + }) + } + } + } + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/module.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..5c4e4c84e507c998c510711176a2a513f64caa0b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/module.json5 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:app_name", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "requestPermissions":[ + { + "name" : "ohos.permission.INTERNET", + "reason": "$string:grant_internet", + "usedScene": { + "abilities": [ + "FormAbility" + ], + "when":"inuse" + } + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/element/color.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..c0ff9aa5f35b94ef4bf76323a802a49d86ad9c60 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/element/string.json @@ -0,0 +1,60 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "WebSocket_address", + "value": "WebSocket地址:" + }, + { + "name": "Content", + "value": "发送内容:" + }, + { + "name": "Connect", + "value": "连接" + }, + { + "name": "Send", + "value": "发送" + }, + { + "name": "Close", + "value": "断开连接" + }, + { + "name": "Connect_successfully", + "value": "连接成功!" + }, + { + "name": "Connect_failed", + "value": "连接失败!" + }, + { + "name": "Send_message_successfully", + "value": "消息发送成功!" + }, + { + "name": "Close_connection", + "value": "关闭连接!" + }, + { + "name": "Close_connection_failed", + "value": "关闭连接失败!" + }, + { + "name": "grant_internet", + "value": "网络使用权限" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/media/background.png b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/media/background.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/media/foreground.png b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/media/foreground.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/media/layered_image.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/media/startIcon.png b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/media/startIcon.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/profile/backup_config.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/profile/main_pages.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/dark/element/color.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/dark/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..79b11c2747aec33e710fd3a7b2b3c94dd9965499 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/main/resources/dark/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#000000" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/ohosTest/ets/test/Ability.test.ets b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..048f66f73f2e82bd33d9bf3356750c172cef1850 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { describe, it, expect } from '@ohos/hypium'; +import { abilityDelegatorRegistry, Driver, ON } from '@kit.TestKit'; + +const BUNDLE = 'WebSocket_C_'; +const TAG: string = '[Sample_WebSocketConnection]'; +const DOMAIN = 0xF811; // 域名设置为0xF811 +const DELAY_TIME = 500; // 延迟时间 +const delegator = abilityDelegatorRegistry.getAbilityDelegator(); +const bundleName = abilityDelegatorRegistry.getArguments().bundleName; +let driver: Driver = Driver.create(); + +function resourceToString(resource: Resource): string { + return getContext().resourceManager.getStringSync(resource); +} + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + /** + * @tc.number LaunchIndexPage_001 + * @tc.name LaunchIndexPage_001 + * @tc.desc 启动应用 + */ + it(BUNDLE + 'LaunchIndexPage_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'LaunchIndexPage_001, begin'); + try { + await delegator.startAbility({ + bundleName: bundleName, + abilityName: 'EntryAbility' + }); + done(); + } catch (err) { + hilog.info(DOMAIN, TAG, BUNDLE + 'LaunchIndexPage_001, error'); + expect(0).assertEqual(err.code) + done(); + } + hilog.info(DOMAIN, TAG, BUNDLE + 'LaunchIndexPage_001, end'); + }) + + /** + * @tc.number WebSocketConnectionFunction_002 + * @tc.name WebSocketConnectionFunction_002 + * @tc.desc WebSocket连接 + */ + it(BUNDLE + 'WebSocketConnectionFunction_002', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'WebSocketConnectionFunction_002, begin'); + await driver.delayMs(DELAY_TIME); + // 点击TextInput输入'wss://echo.websocket.org'文本 + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.WebSocket_address')))); + await driver.assertComponentExist(ON.id('textInput_address')); + let textInput_address = await driver.findComponent(ON.id('textInput_address')); + await textInput_address.inputText('wss://echo.websocket.org'); + await driver.assertComponentExist(ON.id('Connect')); + let buttonConnect = await driver.findComponent(ON.id('Connect')); + await buttonConnect.click(); + await driver.delayMs(DELAY_TIME); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'WebSocketConnectionFunction_002, end'); + }) + + /** + * @tc.number WebSocketSendMessageFunction_003 + * @tc.name WebSocketSendMessageFunction_003 + * @tc.desc WebSocket发送消息 + */ + it(BUNDLE + 'WebSocketSendMessageFunction_003', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'WebSocketSendMessageFunction_003, begin'); + await driver.delayMs(DELAY_TIME); + // 点击TextInput输入'Hello World!'文本 + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.Content')))); + await driver.assertComponentExist(ON.id('textInput_content')); + let textInput_content = await driver.findComponent(ON.id('textInput_content')); + await textInput_content.inputText('Hello World!'); + await driver.assertComponentExist(ON.id('Send')); + let buttonSend = await driver.findComponent(ON.id('Send')); + await buttonSend.click(); + await driver.delayMs(DELAY_TIME); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'WebSocketSendMessageFunction_003, end'); + }) + + /** + * @tc.number WebSocketCloseFunction_004 + * @tc.name WebSocketCloseFunction_004 + * @tc.desc WebSocket连接释放 + */ + it(BUNDLE + 'WebSocketCloseFunction_004', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'WebSocketCloseFunction_004, begin'); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id('Close')); + let buttonClose = await driver.findComponent(ON.id('Close')); + await buttonClose.click(); + await driver.delayMs(DELAY_TIME); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'WebSocketCloseFunction_004, end'); + }) + }) +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/ohosTest/ets/test/List.test.ets b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..1eac52fcebe8958e19a7b8fed2e8f39c520a3e42 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ + +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/ohosTest/module.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c3fd9dda3040d888d9d8b0b62bcb5d3b6fbeb614 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/hvigor/hvigor-config.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d584c19c247db9a7caee4b606bb931aa9279c637 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/hvigor/hvigor-config.json5 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.1", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/hvigorfile.ts b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/oh-package.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e41bae026aab3b50d0abb42fece08ba43b4a772b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.1", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.19", + "@ohos/hamock": "1.0.0" + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/ohosTest.md b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..620335377248bdad239d39334388345fd55ff62c --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/ohosTest.md @@ -0,0 +1,9 @@ +# 测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +| ----------------- | ------------ | ------------------------------------------------------------ | --------------------------------------------------- | -------- | -------- | +| WebSocket连接 | 设备正常运行 | 输入`wss://echo.websocket.org`作为WebSocket地址,点击连接按钮 | 弹出Toast,成功连接WebSocket,并进入连接状态 | 是 | pass | +| WebSocket发送消息 | 设备正常运行 | 输入`Hello World!`作为消息,点击发送按钮 | 弹出Toast,发送的消息成功发送并被接收(无错误信息) | 是 | pass | +| WebSocket连接释放 | 设备正常运行 | 点击“Close”按钮,断开连接 | 弹出Toast,成功关闭WebSocket连接并释放资源 | 是 | pass | diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/screenshots/WebSocket_Close.jpg b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/screenshots/WebSocket_Close.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ac79e6674c25a38d040263a0a1556f289fb08bd2 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/screenshots/WebSocket_Close.jpg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/screenshots/WebSocket_Connect.jpg b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/screenshots/WebSocket_Connect.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f55a407e57bc050932126abaa366f96da0ecd85c Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/screenshots/WebSocket_Connect.jpg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/screenshots/WebSocket_Index.jpg b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/screenshots/WebSocket_Index.jpg new file mode 100644 index 0000000000000000000000000000000000000000..04c09cdb365161e3214b95d8df4304088af8c2af Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/screenshots/WebSocket_Index.jpg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/screenshots/WebSocket_Send.jpg b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/screenshots/WebSocket_Send.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b90014143246ee0d83f8de7e61b977e1cda23d35 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_C/screenshots/WebSocket_Send.jpg differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/.gitignore b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/AppScope/app.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e96fc9d6466769a000b301e1ac8070a98bf6859f --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "bundleName": "com.samples.websocket", + "vendor": "samples", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/AppScope/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..ffe9c22e5726be017aaed5314b5345e638bb9d09 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "webSocket" + } + ] +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/AppScope/resources/base/media/app_icon.png b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/AppScope/resources/base/media/app_icon.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/README_zh.md b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..ce5705049894c06fb397731b3dce55630f14fee7 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/README_zh.md @@ -0,0 +1,89 @@ +# WebSocket连接 + +### 介绍 + +本示例依照指南 开发->系统->网络->Network Kit(网络服务->Network Kit数据传输能力->[WebSocket连接(C/C++)](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/network/native-websocket-guidelines.md)进行编写。使用WebSocket建立服务器与客户端的双向连接,需要先通过createWebSocket()方法创建WebSocket对象,然后通过connect()方法连接到服务器。当连接成功后,客户端会收到open事件的回调,之后客户端就可以通过send()方法与服务器进行通信。当服务器发信息给客户端时,客户端会收到message事件的回调。当客户端不要此连接时,可以通过调用close()方法主动断开连接,之后客户端会收到close事件的回调。本项目展示了一个WebSocket连接的示例应用,它实现了通过按钮创建一个WebSocket连接、发送消息、断开连接的功能,使用了[@ohos.net.webSocket](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/reference/apis-network-kit/js-apis-net-connection.md)接口。 + +### 效果预览 + +| 程序启动 | 连接成功 | 发送消息 | 断开连接 | +| ------------------------------------------------------------ | ---------------------------------------------------------- | ------------------------------------------------------- | --------------------------------------------------------- | +| | | | | + +使用说明 + +1. 点击“连接 WebSocket”按钮时,应用程序尝试连接到 WebSocket 服务器(默认为 `ws://echo.websocket.org`)。 + + 若连接成功,显示 `Connected`,并记录日志。 + + 若连接失败,显示 `Connection failed`,并记录错误日志。 + +2. 点击“发送消息”按钮时,应用程序通过 WebSocket 连接发送一条消息(默认为 `Hello, WebSocket!`)。 + + 发送结果会显示在 UI 上,若发送成功,显示 `Message sent successfully`,若发送失败,显示 `Message sending failed`。 + +3. 点击“断开连接”按钮时,应用程序会关闭与 WebSocket 服务器的连接。 + + 若关闭连接成功,显示 `Connection closed successfully`,否则显示 `Connection closing failed`。 + +### 工程目录 + +``` +entry/src/main/ets/ +|---common +| |---Logger.ts // 日志工具 +|---entryability +| │---EntryAbility.ets +|---entrybackupability +│ |---EntryBackupAbility.ets +|---pages +│ |---Index.ets // 主页 +``` + +### 具体实现 + +1.WebSocket 连接 + +- 使用 `@kit.NetworkKit` 提供的 `webSocket` 对象创建 WebSocket 连接。 +- 使用 `ws.on('open')` 监听连接成功事件,`ws.on('close')` 监听连接关闭事件,`ws.on('error')` 监听连接错误事件。 +- 连接通过 `ws.connect()` 方法发起,默认连接到 `ws://echo.websocket.org`。 + +2. 消息发送 + +- 使用 `ws.send()` 发送消息。 +- 发送成功后,通过 `sendMessageResult` 状态更新 UI,显示“Message sent successfully”。 +- 发送失败后,更新 UI 显示“Message sending failed”。 + +3. 断开连接 + +- 使用 `ws.close()` 关闭 WebSocket 连接。 +- 关闭成功后,更新 UI 显示“Connection closed successfully”,并重置连接状态为“Not connected”。 +- 关闭失败时,显示“Connection closing failed”。 + +### 相关权限 + +[ohos.permission.INTERNET](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-all.md#ohospermissioninternet) + +### 依赖 + +不涉及。 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:华为手机。 +2. HarmonyOS系统:HarmonyOS 5.0.2 Release及以上。 +3. HarmonyOS SDK版本:HarmonyOS 5.0.2 Release及以上。 +4. 本示例需要使用DevEco Studio Release(5.0.5.306)及以上版本才可编译运行。 +5. 本示例需要设备联网使用。 + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/ > .git/info/sparse-checkout +git remote add origin https://gitee.com/harmonyos_samples/guide-snippets.git +git pull origin master +``` \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/build-profile.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ed831f1db622c015dcc8fd8326adc7b9ff1ce4fc --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/build-profile.json5 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.2(14)", + "targetSdkVersion": "5.0.2(14)", + "runtimeOS": "HarmonyOS" + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/.gitignore b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/build-profile.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..eed6635f664fa08b992d84590bf62be7e0bb5381 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/build-profile.json5 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/hvigorfile.ts b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4f43d54667f8327c367c8096bd08bb8c75aff54 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/obfuscation-rules.txt b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..69c4d6a8a5531548e4886fa766090c5c157a87d9 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/oh-package.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c9cb6c8174858277c9b0d465a51547dcab16d5ff --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/ets/common/Logger.ets b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/ets/common/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..79324c310e488c2afceaf20a2dc3eb90b657bcf1 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/ets/common/Logger.ets @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; + +class Logger { + + private domain: number; + private prefix: string; + private format: string = '%{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0x0001; // 此处应填写应用的domain值 + } + + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } + + fatal(...args: string[]) { + hilog.fatal(this.domain, this.prefix, this.format, args); + } + + isLoggable(level: number) { + hilog.isLoggable(this.domain, this.prefix, level); + } +} + +export default new Logger('[Sample_WebSocket]'); diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/ets/entryability/EntryAbility.ets b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..0f2f8b94aa24b0a50e272270e4e18b6df93ac5fd --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 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. + */ + +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..b1e212947256c5533c7b06285a597c94f840a6e3 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/ets/pages/Index.ets b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..82660a443c2a76fde63a9ceff079c531fc3a1d18 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2025 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. + */ + +import { webSocket } from '@kit.NetworkKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import Logger from '../common/Logger'; + +const LOG_TAG = '[Sample_WebSocketDemo]'; +const DEFAULT_IP_ADDRESS = 'wss://echo.websocket.org'; // WebSocket地址 +const BUTTON_HEIGHT = 50; // 按钮高度 +const BUTTON_RADIUS = 5; // 按钮边缘角度 +const BUTTON_FONT_SIZE = 20; // 按钮字体大小 +const TEXT_FONT_SIZE = 18; // 内容字体大小 +const TITLE_FONT_SIZE = 20; // 标题字体大小 +const MARGIN_TOP = 20; // 与顶间隔 +const MARGIN_BOTTOM = 10; // 与底间隔 + +export function resourceToString(resource: Resource): string { + return getContext().resourceManager.getStringSync(resource); +} + +@Entry +@Component +struct WebSocketDemo { + @State connectionStatus: string = 'Not connected'; + @State sendMessageResult: string = ''; + @State closeConnectionResult: string = ''; + @State isConnect: Boolean = false; + ws: webSocket.WebSocket | null = null; + + build() { + Column() { + Text($r('app.string.WebSocket_Example')).fontSize(TITLE_FONT_SIZE).margin({ bottom: MARGIN_BOTTOM }) + + Text(resourceToString($r('app.string.ConnectStatus')) + ':' + this.connectionStatus) + .fontSize(TEXT_FONT_SIZE) + .fontColor(Color.Black) + .margin({ top: MARGIN_TOP }) + .id('connectionStatus') + + Text(resourceToString($r('app.string.SendMessageResult')) + ':' + this.sendMessageResult) + .fontSize(TEXT_FONT_SIZE) + .fontColor(Color.Black) + .margin({ top: MARGIN_TOP }) + .id('sendMessageResult') + + Text(resourceToString($r('app.string.CloseConnectionResult')) + '' + this.closeConnectionResult) + .fontSize(TEXT_FONT_SIZE) + .fontColor(Color.Black) + .margin({ top: MARGIN_TOP }) + .id('closeConnection') + + Button($r('app.string.Connect_WebSocket')) + .onClick(() => { + this.connectWebSocket(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN_TOP }) + .backgroundColor(Color.Blue) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_RADIUS) + .id('Connect_WebSocket') + + Button($r('app.string.SendMessage')) + .onClick(() => { + this.sendMessage(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN_TOP }) + .backgroundColor(Color.Green) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_RADIUS) + .id('SendMessage') + + Button($r('app.string.DisConnect')) + .onClick(() => { + this.closeWebSocket(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN_TOP }) + .backgroundColor(Color.Red) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_RADIUS) + .id('DisConnect') + }.width('100%').height('100%').justifyContent(FlexAlign.Center) + } + +// [Start websocket_development_steps] + // 连接WebSocket + connectWebSocket() { + this.connectionStatus = 'Connecting...'; + this.sendMessageResult = ''; + this.closeConnectionResult = ''; + if (this.isConnect) { + this.connectionStatus = 'WebSocket is already connected'; + return; + } + + this.ws = webSocket.createWebSocket(); + + this.ws.on('open', (err: BusinessError, value: Object) => { + if (!err) { + this.connectionStatus = 'Connected'; + this.isConnect = true; + Logger.info(`${LOG_TAG} WebSocket connected: ` + JSON.stringify(value)); + } else { + this.connectionStatus = 'Connection failed'; + Logger.error(`${LOG_TAG} WebSocket connection failed: ` + JSON.stringify(err)); + } + }); + + this.ws.on('message', (err: BusinessError, value: string | ArrayBuffer) => { + if (err) { + Logger.error(`${LOG_TAG} Error receiving message: ` + JSON.stringify(err)); + return; + } + Logger.info('Received message: ' + value); + + if (value === 'bye') { + this.ws!.close((err: BusinessError) => { + if (!err) { + this.closeConnectionResult = 'Connection closed successfully'; + this.connectionStatus = 'Not connected'; + Logger.info(`${LOG_TAG} WebSocket closed successfully`); + } else { + this.closeConnectionResult = 'Connection closing failed'; + Logger.error(`${LOG_TAG} WebSocket closing failed: ` + JSON.stringify(err)); + } + }); + } + }) + + this.ws.on('close', (err: BusinessError, value: webSocket.CloseResult) => { + if (!err) { + this.connectionStatus = 'Disconnected'; + Logger.info('Connection closed: code = ' + value.code + ', reason =' + value.reason); + this.ws = null; + } else { + Logger.error('Error while closing connection: ' + JSON.stringify(err)); + } + }); + + this.ws.on('error', (err: BusinessError) => { + this.connectionStatus = 'Error'; + Logger.error('WebSocket error: ' + JSON.stringify(err)); + }); + + this.ws.connect(DEFAULT_IP_ADDRESS, (err: BusinessError) => { + if (!err) { + } else { + this.connectionStatus = 'Connection failed'; + Logger.error(`${LOG_TAG} WebSocket connection failed: ` + JSON.stringify(err)); + } + }); + } + + // 发送消息 + sendMessage() { + if (!this.ws) { + this.sendMessageResult = 'WebSocket not connected'; + Logger.error(`${LOG_TAG} WebSocket not connected`); + return; + } + + const message = 'Hello, WebSocket!'; + this.ws.send(message, (err: BusinessError) => { + if (!err) { + this.sendMessageResult = 'Message sent successfully'; + Logger.info(`${LOG_TAG} Message sent successfully: ${message}`); + } else { + this.sendMessageResult = 'Message sending failed'; + Logger.error(`${LOG_TAG} Message sending failed: ` + JSON.stringify(err)); + } + }); + } + + // 关闭WebSocket连接 + closeWebSocket() { + if (!this.ws) { + this.closeConnectionResult = 'WebSocket not connected'; + Logger.error(`${LOG_TAG} WebSocket not connected`); + return; + } + + this.ws.close((err: BusinessError) => { + if (!err) { + this.closeConnectionResult = 'Connection closed successfully'; + this.connectionStatus = 'Not connected'; + Logger.info(`${LOG_TAG} WebSocket closed successfully`); + } else { + this.closeConnectionResult = 'Connection closing failed'; + Logger.error(`${LOG_TAG} WebSocket closing failed: ` + JSON.stringify(err)); + } + }); + this.ws.off('open'); + this.ws.off('close'); + this.ws.off('error'); + this.ws = null; + } + // [End websocket_development_steps] +} + diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/module.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..689f6cd041d97249edce64107ebdba2f5d1f55b5 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/module.json5 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:app_name", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.INTERNET", + "reason": "$string:grant_internet", + "usedScene": { + "abilities": [ + "MainAbility" + ], + "when": "inuse" + } + } + ] + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/element/color.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..23fed3e4f3ab8f6525c5a57a9841c246a7b095b6 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/element/string.json @@ -0,0 +1,44 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "WebSocket_case" + }, + { + "name": "WebSocket_Example", + "value": "WebSocket示例" + }, + { + "name": "Connect_WebSocket", + "value": "连接WebSocket" + }, + { + "name": "SendMessage", + "value": "发送消息" + }, + { + "name": "DisConnect", + "value": "断开连接" + }, + { + "name": "ConnectStatus", + "value": "连接状态" + }, + { + "name": "SendMessageResult", + "value": "发送消息结果" + }, + { + "name": "CloseConnectionResult", + "value": "关闭连接结果" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/media/background.png b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/media/background.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/media/foreground.png b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/media/foreground.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/media/layered_image.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/media/startIcon.png b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/media/startIcon.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/profile/backup_config.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/profile/main_pages.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/en_US/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..c4122a478b875e0c52a7fadbe3556cf322661ca0 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,48 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "WebSocket_case" + }, + { + "name": "WebSocket_Example", + "value": "WebSocket_Example" + }, + { + "name": "Connect_WebSocket", + "value": "Connect_WebSocket" + }, + { + "name": "SendMessage", + "value": "SendMessage" + }, + { + "name": "DisConnect", + "value": "DisConnect" + }, + { + "name": "ConnectStatus", + "value": "ConnectStatus" + }, + { + "name": "SendMessageResult", + "value": "SendMessageResult" + }, + { + "name": "CloseConnectionResult", + "value": "CloseConnectionResult" + }, + { + "name": "grant_internet", + "value": "grant_internet" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/zh_CN/element/string.json b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..033dc89b4252e56eed7731d90e08dacb0c3188dd --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,48 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "WebSocket_case" + }, + { + "name": "WebSocket_Example", + "value": "WebSocket示例" + }, + { + "name": "Connect_WebSocket", + "value": "连接WebSocket" + }, + { + "name": "SendMessage", + "value": "发送消息" + }, + { + "name": "DisConnect", + "value": "断开连接" + }, + { + "name": "ConnectStatus", + "value": "连接状态" + }, + { + "name": "SendMessageResult", + "value": "发送消息结果" + }, + { + "name": "CloseConnectionResult", + "value": "关闭连接结果" + }, + { + "name": "grant_internet", + "value": "网络使用权限" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/ohosTest/ets/test/Ability.test.ets b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..029761067f93af03fb3eb40741121508029da86a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { describe, expect, it } from '@ohos/hypium'; +import { abilityDelegatorRegistry, Driver, ON } from '@kit.TestKit'; +import { UIAbility, Want } from '@kit.AbilityKit'; + + +const DELAY_SHORT = 1000; // 短时间延迟 +const DELAY_MEDIUM = 3000; // 正常时间延迟 +const DELAY_LONG = 5000; // 长时间延迟 + +const delegator = abilityDelegatorRegistry.getAbilityDelegator(); +const DOMAIN = 0xF811; // 域值 +const TAG = 'WebSocketCaseTest'; // 日志标签 +const BUNDLE = 'WebSocketCASE_'; +const BUNDLENAME = 'com.samples.websocket'; +let driver: Driver = Driver.create(); + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + /** + * @tc.number StartAbility_001 + * @tc.name StartAbility_001 + * @tc.desc 启动Ability + */ + + it(BUNDLE + 'StartAbility_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'StartAbility_001 begin'); + const want: Want = { + bundleName: BUNDLENAME, + abilityName: 'EntryAbility' + } + await delegator.startAbility(want); + await driver.delayMs(DELAY_MEDIUM); + const ability: UIAbility = await delegator.getCurrentTopAbility(); + expect(ability.context.abilityInfo.name).assertEqual('EntryAbility'); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'StartAbility_001 end'); + }) + + /** + * @tc.number WebSocket_case_001 + * @tc.name WebSocket_case_001 + * @tc.desc 测试 连接 WebSocket 按钮 + */ + it(BUNDLE + 'WebSocket_case_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLENAME + 'WebSocket_case_001 begin'); + await driver.delayMs(DELAY_SHORT); + await driver.assertComponentExist(ON.id('Connect_WebSocket')); + let stack = await driver.findComponent(ON.id('Connect_WebSocket')); + await stack.click(); + await driver.delayMs(DELAY_LONG * 2); + // 验证界面上是否显示了"通过"或"失败" + let resultText = await driver.findComponent(ON.id('connectionStatus')); // 或者 ON.text('失败') + let result = await resultText.getText(); + expect(result).assertContain('Connected'); + hilog.info(DOMAIN, TAG, BUNDLENAME + 'WebSocket_case_001 end'); + done(); + }); + + /** + * @tc.number WebSocket_case_002 + * @tc.name WebSocket_case_002 + * @tc.desc 测试 发送消息 按钮 + */ + it(BUNDLE + 'WebSocket_case_002', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLENAME + 'WebSocket_case_002 begin'); + await driver.delayMs(DELAY_MEDIUM); + await driver.assertComponentExist(ON.id('SendMessage')); + let stack = await driver.findComponent(ON.id('SendMessage')); + await stack.click(); + await driver.delayMs(DELAY_LONG); + // 验证界面上是否显示了 消息发送成功 + let resultText = await driver.findComponent(ON.id('sendMessageResult')); // 或者 ON.text('失败') + let result = await resultText.getText(); + expect(result).assertContain('Message sent successfully'); + hilog.info(DOMAIN, TAG, BUNDLENAME + 'WebSocket_case_002 end'); + done(); + }) + + /** + * @tc.number WebSocket_case_003 + * @tc.name WebSocket_case_003 + * @tc.desc 测试 断开连接 按钮 + */ + it(BUNDLE + 'WebSocket_case_003', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLENAME + 'WebSocket_case_0023begin'); + await driver.delayMs(DELAY_MEDIUM); + await driver.assertComponentExist(ON.id('DisConnect')); + let stack = await driver.findComponent(ON.id('DisConnect')); + await stack.click(); + await driver.delayMs(DELAY_MEDIUM); + // 验证界面上是否显示了 消息发送成功 + let resultText = await driver.findComponent(ON.id('closeConnection')); // 或者 ON.text('失败') + let result = await resultText.getText(); + expect(result).assertContain('Connection closed successfully'); + hilog.info(DOMAIN, TAG, BUNDLENAME + 'WebSocket_case_003 end'); + done(); + }) + }) +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/ohosTest/ets/test/List.test.ets b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..1eac52fcebe8958e19a7b8fed2e8f39c520a3e42 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ + +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/ohosTest/module.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c3fd9dda3040d888d9d8b0b62bcb5d3b6fbeb614 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/hvigor/hvigor-config.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..43beb743cbd25c3507b1cf8a744bf8197b3bf2fb --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/hvigor/hvigor-config.json5 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/hvigorfile.ts b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/oh-package.json5 b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..952ab183532e81c9c50e5de1e64393704e1e48fe --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.18", + "@ohos/hamock": "1.0.0" + } +} diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/ohosTest.md b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..b23b2db86b2a8169344c6ae50a792abe74e88e51 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/ohosTest.md @@ -0,0 +1,9 @@ +# 测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +| ------------- | ------------ | ---- | ------------------------------------------------------------ | -------- | -------- | +| 连接WebSocket | 设备正常运行 | | 显示“Connected”状态,表示WebSocket成功连接 | 是 | pass | +| 发送消息 | 设备正常运行 | | 显示“Message sent successfully”状态,表示消息发送成功 | 是 | pass | +| 断开连接 | 设备正常运行 | | 显示“Connection closed successfully”状态,表示WebSocket连接断开成功 | 是 | pass | diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/screenshots/ApplicationStart.png b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/screenshots/ApplicationStart.png new file mode 100644 index 0000000000000000000000000000000000000000..44e3219a599e360a2a7003525c8907abb1417f0e Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/screenshots/ApplicationStart.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/screenshots/CloseConnection.png b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/screenshots/CloseConnection.png new file mode 100644 index 0000000000000000000000000000000000000000..0a2c69bab3d75e6e15e8529c99a86e0d46ea2309 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/screenshots/CloseConnection.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/screenshots/ConnectSuccess.png b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/screenshots/ConnectSuccess.png new file mode 100644 index 0000000000000000000000000000000000000000..823fac98929f7b24bb7a84dcf9a75c1b2fd43339 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/screenshots/ConnectSuccess.png differ diff --git a/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/screenshots/SendMessage.png b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/screenshots/SendMessage.png new file mode 100644 index 0000000000000000000000000000000000000000..701fc5af7af4716223de1707dad5022b39ee8e9d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_Datatransmission/WebSocket_case/screenshots/SendMessage.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/.gitignore b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/AppScope/app.json5 b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..4b4fd5c4655f75d759bf071331acce8ac2f0dce1 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "bundleName": "com.samples.flowmanagement", + "vendor": "samples", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/AppScope/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..ad7b0883716fc4d3f6015d873ba06cce23006244 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "flowManagement" + } + ] +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/AppScope/resources/base/media/app_icon.png b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/AppScope/resources/base/media/app_icon.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/README_zh.md b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..19c5c4909b96c3a9a5202177f8054d6b5b09b6c0 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/README_zh.md @@ -0,0 +1,103 @@ +# 流量管理 + +### 介绍 + +本示例依照指南 开发->系统->网络->Network Kit(网络服务->Network Kit数据传输能力->[流量管理](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/network/net-statistics.md)进行编写,在此调用了示例中的所有非系统接口部分。 + +流量管理提供了基于物理网络的数据流量统计能力,支持基于网卡/UID 的流量统计。 + +流量管理主要实现功能有: + +- 支持基于网卡/UID 的实时流量统计。 +- 支持基于网卡/UID 的历史流量统计。 +- 支持基于网卡/UID 的流量变化订阅。 + +**注意:**1.获取蜂窝流量接口(getCellularRxBytes、getCellularTxBytes)需要设备插入sim卡使用。 + +​ 2.指定应用相关接口(getUidRxBytes、getUidTxBytes)需要指定有流量开销的应用。 + +​ 3.指定Socket相关接口(getSockfdRxBytes、getSockfdTxBytes)需要传入一个有效socket,代码中的实例仅供参考。 + +### 效果预览 + +| 获取指定网卡下行流量 | 获取指定网卡上行流量 | 获取所有网卡下行流量 | 获取所有网卡上行流量 | +| ---------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------ | +| ![image](screenshots/Aquire_traffic_downStream.jpeg) | ![image](screenshots/Aquire_traffic_upStream.jpeg) | ![image](screenshots/Aquire_all_traffic_downStream.jpeg) | ![image](screenshots/Aquire_all_traffic_upStream.jpeg) | + +使用说明 + +1. 点击按钮调用指定接口。 + +2. 接口调用结果在程序上方显示。 + + +### 工程目录 + +``` +entry/src/main/ets/ +|---common +| |---Logger.ts // 日志工具 +|---entryability +| │---EntryAbility.ets +|---entrybackupability +│ |---EntryBackupAbility.ets +|---filemanager +| |---GetRealtimeStats.ets // 实时流量统计接口 +|---pages +│ |---Index.ets // 主页 +``` + +### 具体实现 + +- **获取指定网卡实时下行流量数据** + 使用 `statistics.getIfaceRxBytes(IFACE)` 获取指定网络接口(如 `wlan0`)的实时下行流量数据。 +- **获取指定网卡实时上行流量数据** + 使用 `statistics.getIfaceTxBytes(IFACE)` 获取指定网络接口的实时上行流量数据。 +- **获取蜂窝网络实时下行流量数据** + 使用 `statistics.getCellularRxBytes()` 获取蜂窝网络的实时下行流量数据。 +- **获取蜂窝网络实时上行流量数据** + 使用 `statistics.getCellularTxBytes()` 获取蜂窝网络的实时上行流量数据。 +- **获取所有网卡实时下行流量数据** + 使用 `statistics.getAllRxBytes()` 获取所有网卡的实时下行流量数据。 +- **获取所有网卡实时上行流量数据** + 使用 `statistics.getAllTxBytes()` 获取所有网卡的实时上行流量数据。 + +- **获取指定应用实时下行流量数据** + 使用 `statistics.getUidRxBytes(UID)` 获取指定应用(通过 `UID` 获取)的实时下行流量数据。 +- **获取指定应用实时上行流量数据** + 使用 `statistics.getUidTxBytes(UID)` 获取指定应用的实时上行流量数据。 + +- **获取指定 Socket 实时下行流量数据** + 使用 `statistics.getSockfdRxBytes(sockfd)` 获取指定 socket 文件描述符的实时下行流量数据。 +- **获取指定 Socket 实时上行流量数据** + 使用 `statistics.getSockfdTxBytes(sockfd)` 获取指定 socket 文件描述符的实时上行流量数据。 + +### 相关权限 + +[ohos.permission.INTERNET](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-all.md#ohospermissioninternet) + +[ohos.permission.GET_NETWORK_INFO](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-all.md#ohospermissionget_network_info) + +### 依赖 + +不涉及。 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:华为手机。 +2. HarmonyOS系统:HarmonyOS 5.1.1 Release及以上。 +3. DevEco Studio版本:DevEco Studio 5.1.1 Release及以上。 +4. HarmonyOS SDK版本:HarmonyOS 5.1.1 Release及以上。 +5. 本示例需要设备联网使用。 + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/ > .git/info/sparse-checkout +git remote add origin https://gitee.com/harmonyos_samples/guide-snippets.git +git pull origin master +``` \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/build-profile.json5 b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..bcdaa331e902c0894a5fa0ea1902ef6ddd7ac037 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/build-profile.json5 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "targetSdkVersion": "5.1.1(19)", + "compatibleSdkVersion": "5.1.1(19)", + "runtimeOS": "HarmonyOS" + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/.gitignore b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/build-profile.json5 b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..eed6635f664fa08b992d84590bf62be7e0bb5381 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/build-profile.json5 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/hvigorfile.ts b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4f43d54667f8327c367c8096bd08bb8c75aff54 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/obfuscation-rules.txt b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..69c4d6a8a5531548e4886fa766090c5c157a87d9 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/oh-package.json5 b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c9cb6c8174858277c9b0d465a51547dcab16d5ff --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/ets/common/Logger.ets b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/ets/common/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..539313586f60e426bf57684d1abb7d6ef216a282 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/ets/common/Logger.ets @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; + +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0x0001; + } + + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } + + fatal(...args: string[]) { + hilog.fatal(this.domain, this.prefix, this.format, args); + } + + isLoggable(level: number) { + hilog.isLoggable(this.domain, this.prefix, level); + } +} + +export default new Logger('[Sample_FlowManagement]'); diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/ets/entryability/EntryAbility.ets b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..0f2f8b94aa24b0a50e272270e4e18b6df93ac5fd --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 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. + */ + +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..b1e212947256c5533c7b06285a597c94f840a6e3 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/ets/pages/Index.ets b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..0349587d572ce24769e9f75a68bada7337c40ea3 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2025 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. + */ + +import { socket, statistics } from '@kit.NetworkKit'; +import { BusinessError } from '@kit.BasicServicesKit'; + +const LOG_FONT_SIZE = 15; // 日志文字大小 +const LOG_MARGIN = 20; // 日志上边距 +const BUTTON_HEIGHT = 50; // 按钮高度 +const BUTTON_MARGIN_TOP = 8; // 按钮的上边距 +const BUTTON_FONT_SIZE = 24; // 修改按钮文字大小 +const TEXT_FONT_SIZE = 32; // 修改标题文字大小 +const BUTTON_BORDER_RADIUS = 5; // 圆角大小 +const IFACE = 'wlan0'; // 网络接口名 +let UID = getContext(this).applicationInfo.uid; // 示例 UID + +@Entry +@Component +struct NetworkStatisticsDemo { + @State logMessage: string = ''; // 用于显示日志的变量 + + build() { + Column() { + Text($r('app.string.TitleName')) + .fontSize(TEXT_FONT_SIZE) + .fontColor(Color.Black) + .id('title') + + // 显示日志的文本 + Text(this.logMessage) + .fontSize(LOG_FONT_SIZE) + .fontColor(Color.Black) + .margin({ bottom: LOG_MARGIN }) + .textAlign(TextAlign.Center) + .id('logger-output') + + Button($r('app.string.Get_Designated_Network_Card_Downstream_Traffic')) + .onClick(() => { + this.getIfaceRxBytes(); + }) + .id('Button_GetIfaceRxBytes') + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: BUTTON_MARGIN_TOP }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_BORDER_RADIUS) + + Button($r('app.string.Get_Designated_Network_Card_Upstream_Traffic')) + .onClick(() => { + this.getIfaceTxBytes(); + }) + .id('Button_GetIfaceTxBytes') + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: BUTTON_MARGIN_TOP }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_BORDER_RADIUS) + + Button($r('app.string.Get_Cellular_Downstream_Traffic')) + .onClick(() => { + this.getCellularRxBytes(); + }) + .id('Button_GetCellularRxBytes') + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: BUTTON_MARGIN_TOP }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_BORDER_RADIUS) + + Button($r('app.string.Get_Cellular_Upstream_Traffic')) + .onClick(() => { + this.getCellularTxBytes(); + }) + .id('Button_GetCellularTxBytes') + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: BUTTON_MARGIN_TOP }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_BORDER_RADIUS) + + Button($r('app.string.Get_All_Network_Card_Downstream_Traffic')) + .onClick(() => { + this.getAllRxBytes(); + }) + .id('Button_GetAllRxBytes') + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: BUTTON_MARGIN_TOP }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_BORDER_RADIUS) + + Button($r('app.string.Get_All_Network_Card_Upstream_Traffic')) + .onClick(() => { + this.getAllTxBytes(); + }) + .id('Button_GetAllTxBytes') + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: BUTTON_MARGIN_TOP }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_BORDER_RADIUS) + + Button($r('app.string.Get_Designated_App_Downstream_Traffic')) + .onClick(() => { + this.getUidRxBytes(); + }) + .id('Button_GetUidRxBytes') + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: BUTTON_MARGIN_TOP }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_BORDER_RADIUS) + + Button($r('app.string.Get_Designated_App_Upstream_Traffic')) + .onClick(() => { + this.getUidTxBytes(); + }) + .id('Button_GetUidTxBytes') + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: BUTTON_MARGIN_TOP }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_BORDER_RADIUS) + + Button($r('app.string.Get_Designated_Socket_Downstream_Traffic')) + .onClick(() => { + this.getSockfdRxBytes(); + }) + .id('Button_GetSockfdRxBytes') + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: BUTTON_MARGIN_TOP }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_BORDER_RADIUS) + + Button($r('app.string.Get_Designated_Socket_Upstream_Traffic')) + .onClick(() => { + this.getSockfdTxBytes(); + }) + .id('Button_GetSockfdTxBytes') + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: BUTTON_MARGIN_TOP }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_BORDER_RADIUS) + } + .width('100%') + .height('100%') + .justifyContent(FlexAlign.Center); + } + +// [Start obtain_realtime_traffic_statistics_of_network_interface_cards/uid] + // 获取各种实体的流量数据方法 + getIfaceRxBytes() { + statistics.getIfaceRxBytes(IFACE).then((stats: number) => { + this.logMessage = `Interface ${IFACE} downstream traffic: ${JSON.stringify(stats)}`; + }).catch((err: BusinessError) => { + this.logMessage = `Failed to get downstream traffic for interface ${IFACE}: ${JSON.stringify(err)}`; + }); + } + + getIfaceTxBytes() { + statistics.getIfaceTxBytes(IFACE).then((stats: number) => { + this.logMessage = `Interface ${IFACE} upstream traffic: ${JSON.stringify(stats)}`; + }).catch((err: BusinessError) => { + this.logMessage = `Failed to get upstream traffic for interface ${IFACE}: ${JSON.stringify(err)}`; + }); + } + + getCellularRxBytes() { + statistics.getCellularRxBytes().then((stats: number) => { + this.logMessage = `Cellular downstream traffic: ${JSON.stringify(stats)}`; + }).catch((err: BusinessError) => { + this.logMessage = `Failed to get cellular downstream traffic: ${JSON.stringify(err)}`; + }); + } + + getCellularTxBytes() { + statistics.getCellularTxBytes().then((stats: number) => { + this.logMessage = `Cellular upstream traffic: ${JSON.stringify(stats)}`; + }).catch((err: BusinessError) => { + this.logMessage = `Failed to get cellular upstream traffic: ${JSON.stringify(err)}`; + }); + } + + getAllRxBytes() { + statistics.getAllRxBytes().then((stats: number) => { + this.logMessage = `All interfaces downstream traffic: ${JSON.stringify(stats)}`; + }).catch((err: BusinessError) => { + this.logMessage = `Failed to get all interfaces downstream traffic: ${JSON.stringify(err)}`; + }); + } + + getAllTxBytes() { + statistics.getAllTxBytes().then((stats: number) => { + this.logMessage = `All interfaces upstream traffic: ${JSON.stringify(stats)}`; + }).catch((err: BusinessError) => { + this.logMessage = `Failed to get all interfaces upstream traffic: ${JSON.stringify(err)}`; + }); + } + + getUidRxBytes() { + UID = getContext(this).applicationInfo.uid + statistics.getUidRxBytes(UID).then((stats: number) => { + this.logMessage = `Application ${UID} downstream traffic: ${JSON.stringify(stats)}`; + }).catch((err: BusinessError) => { + this.logMessage = `Failed to get downstream traffic for app ${UID}: ${JSON.stringify(err)}`; + }); + } + + getUidTxBytes() { + statistics.getUidTxBytes(UID).then((stats: number) => { + this.logMessage = `Application ${UID} upstream traffic: ${JSON.stringify(stats)}`; + }).catch((err: BusinessError) => { + this.logMessage = `Failed to get upstream traffic for app ${UID}: ${JSON.stringify(err)}`; + }); + } + + getSockfdRxBytes() { + let tcp: socket.TCPSocket = socket.constructTCPSocketInstance(); + tcp.getSocketFd().then((sockfd: number) => { + statistics.getSockfdRxBytes(sockfd).then((stats: number) => { + this.logMessage = `Socket ${sockfd} downstream traffic: ${JSON.stringify(stats)}`; + }).catch((err: BusinessError) => { + this.logMessage = `Failed to get socket downstream traffic: ${JSON.stringify(err)}`; + }); + }).catch((err: BusinessError) => { + this.logMessage = `Failed to get socket file descriptor: ${JSON.stringify(err)}`; + }); + } + + getSockfdTxBytes() { + let tcp: socket.TCPSocket = socket.constructTCPSocketInstance(); + tcp.getSocketFd().then((sockfd: number) => { + statistics.getSockfdTxBytes(sockfd).then((stats: number) => { + this.logMessage = `Socket ${sockfd} upstream traffic: ${JSON.stringify(stats)}`; + }).catch((err: BusinessError) => { + this.logMessage = `Failed to get socket upstream traffic: ${JSON.stringify(err)}`; + }); + }).catch((err: BusinessError) => { + this.logMessage = `Failed to get socket file descriptor: ${JSON.stringify(err)}`; + }); + } +// [End obtain_realtime_traffic_statistics_of_network_interface_cards/uid] +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/module.json5 b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..0b95c44301857394101e73f37cab7080e0767b36 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/module.json5 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:app_name", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.INTERNET", + "reason": "$string:grant_internet", + "usedScene": { + "abilities": [ + "MainAbility" + ], + "when": "inuse" + } + }, + { + "name": "ohos.permission.GET_NETWORK_INFO", + "reason": "$string:grant_get_network_info", + "usedScene": { + "abilities": [ + "MainAbility" + ], + "when": "inuse" + } + } + ] + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/element/color.json b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..cf44d6d14dd74a8702dc96d7dcf3c79a719f8939 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/element/string.json @@ -0,0 +1,60 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "Get_Designated_Network_Card_Downstream_Traffic", + "value": "获取指定网卡下行流量" + }, + { + "name": "Get_Designated_Network_Card_Upstream_Traffic", + "value": "获取指定网卡上行流量" + }, + { + "name": "Get_Cellular_Downstream_Traffic", + "value": "获取蜂窝下行流量" + }, + { + "name": "Get_Cellular_Upstream_Traffic", + "value": "获取蜂窝上行流量" + }, + { + "name": "Get_All_Network_Card_Downstream_Traffic", + "value": "获取所有网卡下行流量" + }, + { + "name": "Get_All_Network_Card_Upstream_Traffic", + "value": "获取所有网卡上行流量" + }, + { + "name": "Get_Designated_App_Downstream_Traffic", + "value": "获取指定应用下行流量" + }, + { + "name": "Get_Designated_App_Upstream_Traffic", + "value": "获取指定应用上行流量" + }, + { + "name": "Get_Designated_Socket_Downstream_Traffic", + "value": "获取指定Socket下行流量" + }, + { + "name": "Get_Designated_Socket_Upstream_Traffic", + "value": "获取指定Socket上行流量" + }, + { + "name": "TitleName", + "value": "流量管理" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/back.svg b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/back.svg new file mode 100644 index 0000000000000000000000000000000000000000..0614389ee6f66cd188d47ba045aa83b4babee356 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/back.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/background.png b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/background.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/foreground.png b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/foreground.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/layered_image.json b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/startIcon.png b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/media/startIcon.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/profile/backup_config.json b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/profile/main_pages.json b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/en_US/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..5ffc94b91a42c0c49a7da0359b1fd1a9100d7bc4 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,68 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "Get_Designated_Network_Card_Downstream_Traffic", + "value": "Get_Designated_Network_Card_Downstream_Traffic" + }, + { + "name": "Get_Designated_Network_Card_Upstream_Traffic", + "value": "Get_Designated_Network_Card_Upstream_Traffic" + }, + { + "name": "Get_Cellular_Downstream_Traffic", + "value": "Get_Cellular_Downstream_Traffic" + }, + { + "name": "Get_Cellular_Upstream_Traffic", + "value": "Get_Cellular_Upstream_Traffic" + }, + { + "name": "Get_All_Network_Card_Downstream_Traffic", + "value": "Get_All_Network_Card_Downstream_Traffic" + }, + { + "name": "Get_All_Network_Card_Upstream_Traffic", + "value": "Get_All_Network_Card_Upstream_Traffic" + }, + { + "name": "Get_Designated_App_Downstream_Traffic", + "value": "Get_Designated_App_Downstream_Traffic" + }, + { + "name": "Get_Designated_App_Upstream_Traffic", + "value": "Get_Designated_App_Upstream_Traffic" + }, + { + "name": "Get_Designated_Socket_Downstream_Traffic", + "value": "Get_Designated_Socket_Downstream_Traffic" + }, + { + "name": "Get_Designated_Socket_Upstream_Traffic", + "value": "Get_Designated_Socket_Upstream_Traffic" + }, + { + "name": "TitleName", + "value": "Flow_Manage_Name" + }, + { + "name": "grant_internet", + "value": "grant_internet" + }, + { + "name": "grant_get_network_info", + "value": "grant_get_network_info" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/zh_CN/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..0858a4c0aecdfd2907c79d71f38a3af09ada32c2 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,68 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "Get_Designated_Network_Card_Downstream_Traffic", + "value": "获取指定网卡下行流量" + }, + { + "name": "Get_Designated_Network_Card_Upstream_Traffic", + "value": "获取指定网卡上行流量" + }, + { + "name": "Get_Cellular_Downstream_Traffic", + "value": "获取蜂窝下行流量" + }, + { + "name": "Get_Cellular_Upstream_Traffic", + "value": "获取蜂窝上行流量" + }, + { + "name": "Get_All_Network_Card_Downstream_Traffic", + "value": "获取所有网卡下行流量" + }, + { + "name": "Get_All_Network_Card_Upstream_Traffic", + "value": "获取所有网卡上行流量" + }, + { + "name": "Get_Designated_App_Downstream_Traffic", + "value": "获取指定应用下行流量" + }, + { + "name": "Get_Designated_App_Upstream_Traffic", + "value": "获取指定应用上行流量" + }, + { + "name": "Get_Designated_Socket_Downstream_Traffic", + "value": "获取指定Socket下行流量" + }, + { + "name": "Get_Designated_Socket_Upstream_Traffic", + "value": "获取指定Socket上行流量" + }, + { + "name": "TitleName", + "value": "流量管理" + }, + { + "name": "grant_internet", + "value": "网络使用权限" + }, + { + "name": "grant_get_network_info", + "value": "获取网络信息权限" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/ohosTest/ets/test/Ability.test.ets b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..f8fb67dadc8ab03fb08b08b5d6b2c706a00e6a28 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { describe, expect, it } from '@ohos/hypium'; +import { abilityDelegatorRegistry, Driver, ON } from '@kit.TestKit'; +import { UIAbility, Want } from '@kit.AbilityKit'; + +const delegator = abilityDelegatorRegistry.getAbilityDelegator(); + +const DOMAIN = 0xF812; // 域值 +const TAG = 'NetworkStatisticsTest'; // 日志标签 +const BUNDLE = 'NETWORKSTATISTICS_'; +const BUNDLENAME = 'com.samples.flowmanagement'; +const DELAY_TIME = 3000; // 延迟时间 +let driver: Driver = Driver.create(); + +export default function AbilityTest() { + describe('NetworkStatisticsAbilityTest', () => { + + /** + * @tc.number StartAbility_001 + * @tc.name StartAbility_001 + * @tc.desc 启动NetworkStatisticsDemo Ability + */ + it(BUNDLE + 'StartAbility_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'StartAbility_001, begin'); + const want: Want = { + bundleName: BUNDLENAME, + abilityName: 'EntryAbility', + }; + await delegator.startAbility(want); + await driver.delayMs(DELAY_TIME); + const ability: UIAbility = await delegator.getCurrentTopAbility(); + expect(ability.context.abilityInfo.name).assertEqual('EntryAbility'); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id('title')); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'StartAbility_001, end'); + }); + + // 测试获取指定网卡实时下行流量数据 + it(BUNDLE + 'GetIfaceRxBytes_Test_002', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'GetIfaceRxBytes_Test_002, begin'); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id('Button_GetIfaceRxBytes')); + let button = await driver.findComponent(ON.id('Button_GetIfaceRxBytes')); + await button.click(); + await driver.delayMs(DELAY_TIME); + // 显示日志 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('Interface wlan0 downstream traffic'); + hilog.info(DOMAIN, TAG, BUNDLE + 'GetIfaceRxBytes_Test_002, end'); + done(); + }); + + // 测试获取指定网卡实时上行流量数据 + it(BUNDLE + 'GetIfaceTxBytes_Test_003', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'GetIfaceTxBytes_Test_003, begin'); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id('Button_GetIfaceTxBytes')); + let button = await driver.findComponent(ON.id('Button_GetIfaceTxBytes')); + await button.click(); + await driver.delayMs(DELAY_TIME); + // 显示日志 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('Interface wlan0 upstream traffic'); + hilog.info(DOMAIN, TAG, BUNDLE + 'GetIfaceTxBytes_Test_003, end'); + done(); + }); + + // 测试获取蜂窝实时下行流量数据 + it(BUNDLE + 'GetCellularRxBytes_Test_004', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'GetCellularRxBytes_Test_004, begin'); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id('Button_GetCellularRxBytes')); + let button = await driver.findComponent(ON.id('Button_GetCellularRxBytes')); + await button.click(); + await driver.delayMs(DELAY_TIME); + hilog.info(DOMAIN, TAG, BUNDLE + 'GetCellularRxBytes_Test_004, end'); + done(); + }); + + // 测试获取蜂窝实时上行流量数据 + it(BUNDLE + 'GetCellularTxBytes_Test_005', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'GetCellularTxBytes_Test_005, begin'); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id('Button_GetCellularTxBytes')); + let button = await driver.findComponent(ON.id('Button_GetCellularTxBytes')); + await button.click(); + await driver.delayMs(DELAY_TIME); + hilog.info(DOMAIN, TAG, BUNDLE + 'GetCellularTxBytes_Test_005, end'); + done(); + }); + + // 测试获取所有网卡实时下行流量数据 + it(BUNDLE + 'GetAllRxBytes_Test_006', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'GetAllRxBytes_Test_006, begin'); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id('Button_GetAllRxBytes')); + let button = await driver.findComponent(ON.id('Button_GetAllRxBytes')); + await button.click(); + await driver.delayMs(DELAY_TIME); + // 显示日志 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('All interfaces downstream traffic'); + hilog.info(DOMAIN, TAG, BUNDLE + 'GetAllRxBytes_Test_006, end'); + done(); + }); + + // 测试获取所有网卡实时上行流量数据 + it(BUNDLE + 'GetAllTxBytes_Test_007', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'GetAllTxBytes_Test_007, begin'); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id('Button_GetAllTxBytes')); + let button = await driver.findComponent(ON.id('Button_GetAllTxBytes')); + await button.click(); + await driver.delayMs(DELAY_TIME); + // 显示日志 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('All interfaces upstream traffic'); + hilog.info(DOMAIN, TAG, BUNDLE + 'GetAllTxBytes_Test_007, end'); + done(); + }); + + // 测试获取指定应用实时下行流量数据 + it(BUNDLE + 'GetUidRxBytes_Test_008', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'GetUidRxBytes_Test_008, begin'); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id('Button_GetUidRxBytes')); + let button = await driver.findComponent(ON.id('Button_GetUidRxBytes')); + await button.click(); + await driver.delayMs(DELAY_TIME); + hilog.info(DOMAIN, TAG, BUNDLE + 'GetUidRxBytes_Test_008, end'); + done(); + }); + + // 测试获取指定应用实时上行流量数据 + it(BUNDLE + 'GetUidTxBytes_Test_009', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'GetUidTxBytes_Test_009, begin'); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id('Button_GetUidTxBytes')); + let button = await driver.findComponent(ON.id('Button_GetUidTxBytes')); + await button.click(); + await driver.delayMs(DELAY_TIME); + hilog.info(DOMAIN, TAG, BUNDLE + 'GetUidTxBytes_Test_009, end'); + done(); + }); + + // 测试获取指定Socket实时下行流量数据 + it(BUNDLE + 'GetSockfdRxBytes_Test_010', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'GetSockfdRxBytes_Test_010, begin'); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id('Button_GetSockfdRxBytes')); + let button = await driver.findComponent(ON.id('Button_GetSockfdRxBytes')); + await button.click(); + await driver.delayMs(DELAY_TIME); + hilog.info(DOMAIN, TAG, BUNDLE + 'GetSockfdRxBytes_Test_010, end'); + done(); + }); + + // 测试获取指定Socket实时上行流量数据 + it(BUNDLE + 'GetSockfdTxBytes_Test_011', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'GetSockfdTxBytes_Test_011, begin'); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id('Button_GetSockfdTxBytes')); + let button = await driver.findComponent(ON.id('Button_GetSockfdTxBytes')); + await button.click(); + await driver.delayMs(DELAY_TIME); + hilog.info(DOMAIN, TAG, BUNDLE + 'GetSockfdTxBytes_Test_011, end'); + done(); + }); + }); +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/ohosTest/ets/test/List.test.ets b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..1eac52fcebe8958e19a7b8fed2e8f39c520a3e42 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ + +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/ohosTest/module.json5 b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c3fd9dda3040d888d9d8b0b62bcb5d3b6fbeb614 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/hvigor/hvigor-config.json5 b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..43beb743cbd25c3507b1cf8a744bf8197b3bf2fb --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/hvigor/hvigor-config.json5 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/hvigorfile.ts b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/oh-package.json5 b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..952ab183532e81c9c50e5de1e64393704e1e48fe --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.18", + "@ohos/hamock": "1.0.0" + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/ohosTest.md b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..3e0b3dd3f4234e99da605a473067e1f8647098e6 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/ohosTest.md @@ -0,0 +1,17 @@ +# 测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +| ------------------------------ | ----------------------- | ---- | ------------- | -------- | -------- | +| 获取指定网卡实时下行流量数据 | 设备正常运行 | | Toast正常显示 | 是 | pass | +| 获取指定网卡实时上行流量数据 | 设备正常运行 | | Toast正常显示 | 是 | pass | +| 获取蜂窝实时下行流量数据 | 设备插入sim卡 | | Toast正常显示 | 是 | pass | +| 获取蜂窝实时上行流量数据 | 设备插入sim卡 | | Toast正常显示 | 是 | pass | +| 获取所有网卡实时下行流量数据 | 设备正常运行 | | Toast正常显示 | 是 | pass | +| 获取所有网卡实时上行流量数据 | 设备正常运行 | | Toast正常显示 | 是 | pass | +| 获取指定应用实时下行流量数据 | UID指定有流量开销的应用 | | Toast正常显示 | 是 | pass | +| 获取指定应用实时上行流量数据 | UID指定有流量开销的应用 | | Toast正常显示 | 是 | pass | +| 获取指定Socket实时下行流量数据 | 传入一个有效socket | | Toast正常显示 | 是 | pass | +| 获取指定Socket实时上行流量数据 | 传入一个有效socket | | Toast正常显示 | 是 | pass | + diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/screenshots/Aquire_all_traffic_downStream.jpeg b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/screenshots/Aquire_all_traffic_downStream.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..d429c17e32deb672d2640550d8a00d3476f73f86 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/screenshots/Aquire_all_traffic_downStream.jpeg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/screenshots/Aquire_all_traffic_upStream.jpeg b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/screenshots/Aquire_all_traffic_upStream.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..8e17d7b1d4e7ab157ea0ed927914ab50afb72acf Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/screenshots/Aquire_all_traffic_upStream.jpeg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/screenshots/Aquire_traffic_downStream.jpeg b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/screenshots/Aquire_traffic_downStream.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..42fea1fefe43e4526bd20dcdbf0e1e7a117ee89a Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/screenshots/Aquire_traffic_downStream.jpeg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/screenshots/Aquire_traffic_upStream.jpeg b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/screenshots/Aquire_traffic_upStream.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..48227a9fb7249003679850f75ad8fcdc03c51ec1 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/FlowManagement_case/screenshots/Aquire_traffic_upStream.jpeg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/.gitignore b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/AppScope/app.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..2268306023f4bf2e43f2d0eb2389fbdd75a1ed48 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "bundleName": "com.samples.netconnection", + "vendor": "samples", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/AppScope/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..83676a47e1fcda88de77c89f6d995cecd7ad3e7d --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "NetConnection" + } + ] +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/AppScope/resources/base/media/app_icon.png b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/AppScope/resources/base/media/app_icon.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/README_zh.md b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..c275f442f548e3dff93b61ae19b9d49c049bf9d6 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/README_zh.md @@ -0,0 +1,80 @@ +# NetConnection开发指导 + +### 介绍 + +本示例参考OpenHarmony官方文档中的网络服务部分(网络服务->Network Kit数据传输能力->[NetConnection开发指导](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/network/native-websocket-guidelines.md))进行编写。项目中的前两个按钮所调用的接口对应于《NetConnection开发指导》中的内容,而后续按钮则调用了[NetConnection](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/reference/apis-network-kit/_net_connection.md)中的其他网络接口。 + +本示例旨在展示`NetConnection`模块中常用的网络信息查询功能。通过创建一个Native C++工程,我们首先在源代码中封装相关接口,随后在ArkTS层调用这些封装接口,从而实现对`NetConnection`中部分接口的调用。 + +**注意**:由于部分接口需要提供有效的NetID进行调用,可能会出现调用失败的情况。该示例的主要目的是指导用户如何进行接口调用,某些接口的正常调用条件较为复杂,涉及到的对象和环境配置未完全构建,因此部分接口可能无法正常执行。 + +### 效果预览 + +| 获取默认网络ID | 获取响应状态码 | 查询是否有激活的的数据网络 | 查询默认网络是否记流量 | +| --------------------------------------- | ------------------------------------ | ------------------------------------------ | ------------------------------------------------ | +| ![image](screenshots/GetDefaultNet.jpg) | ![image](screenshots/CodeNumber.jpg) | ![image](screenshots/GetHasDefaultNet.jpg) | ![image](screenshots/GetIsDefaultNetMetered.jpg) | + +使用说明 + +1. 点击相应的按钮调取相应接口。 +2. 相关接口的调用结果会在屏幕上方显示。 + +### 工程目录 + +``` +entry/src/main/ +│ +│---cpp +│ │---CMakeLists.txt +│ │---napi_init.cpp // 链接层 +│ └---types +│ |---libentry +│ |---Index.d.ts +│ |---oh-package.json5 +| +|---entryability +| │---EntryAbility.ets +|---entrybackupability +│ |---EntryBackupAbility.ets +|---pages +│ |---Index.ets // 主页 +``` + +### 具体实现 + +1. 配置`CMakeLists.txt`,本模块需要用到的共享库是`libnet_connection.so`。 +2. 在 C++ 层封装对 `NetConnection` API 的调用。 +3. 在 ArkTS 层通过调用封装的接口获取相应的网络信息。 +4. 提供多种网络相关功能:获取默认网络、检查默认网络状态、获取网络编号等。 + +### 相关权限 + +[ohos.permission.INTERNET](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-all.md#ohospermissioninternet) + +[ohos.permission.GET_NETWORK_INFO](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-all.md#ohospermissionget_network_info) + +### 依赖 + +不涉及。 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:华为手机。 + +2. HarmonyOS系统:HarmonyOS 5.1.1 Release及以上。 +3. DevEco Studio版本:DevEco Studio 5.1.1 Release及以上。 +4. HarmonyOS SDK版本:HarmonyOS 5.1.1 Release及以上。 +5. 本示例需要设备联网使用。 + + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case > .git/info/sparse-checkout +git remote add origin https://gitee.com/harmonyos_samples/guide-snippets.git +git pull origin master +``` diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/build-profile.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ce9e509426647aaceb1c01874154885e87d0d5f7 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/build-profile.json5 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "targetSdkVersion": "5.1.1(19)", + "compatibleSdkVersion": "5.1.1(19)", + "runtimeOS": "HarmonyOS", + "buildOption": { + "externalNativeOptions": { + "abiFilters": [ + "arm64-v8a", + "x86_64", + ] + } + } + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ], + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/.gitignore b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/build-profile.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f2fe95eef7515b8fa905f3e67c4cc5a4cbb1a496 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/build-profile.json5 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "apiType": "stageMode", + "buildOption": { + "externalNativeOptions": { + "path": "./src/main/cpp/CMakeLists.txt", + "arguments": "", + "cppFlags": "", + "abiFilters": [ + "arm64-v8a", + "x86_64", + ] + } + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + }, + "nativeLib": { + "debugSymbol": { + "strip": true, + "exclude": [] + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/hvigorfile.ts b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4f43d54667f8327c367c8096bd08bb8c75aff54 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/obfuscation-rules.txt b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..fdbb5b9852d7dd5f39bddaeb21ab5ee1f3346749 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/obfuscation-rules.txt @@ -0,0 +1,22 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/oh-package.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..118bdd4fe7699368a010e04c24f5bfc887cf1298 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/oh-package.json5 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "libentry.so": "file:./src/main/cpp/types/libentry" + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/cpp/CMakeLists.txt b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..012f71d87bebe29ea9ac6c09f7efc8caea7a93f8 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/cpp/CMakeLists.txt @@ -0,0 +1,17 @@ +# the minimum version of CMake. +cmake_minimum_required(VERSION 3.5.0) +project(NetConnection2) + +set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +if(DEFINED PACKAGE_FIND_FILE) + include(${PACKAGE_FIND_FILE}) +endif() + +include_directories(${NATIVERENDER_ROOT_PATH} + ${NATIVERENDER_ROOT_PATH}/include) + +add_library(entry SHARED napi_init.cpp) +target_link_libraries(entry PUBLIC libace_napi.z.so + libnet_connection.so ) + diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/cpp/napi_init.cpp b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/cpp/napi_init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b07f4a2f1ace8e4a73c68542f13f8e6ee6e0b975 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/cpp/napi_init.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2025 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. + */ + +// [Start header_file] +#include "napi/native_api.h" +#include "network/netmanager/net_connection.h" +#include "network/netmanager/net_connection_type.h" +// [End header_file] +#include + +const size_t MIN_ARG_COUNT = 3; // 最小参数个数,用于函数参数检查 +const int PARAMETER_ERROR_CODE = 401; // 错误代码,表示参数错误 +const size_t MAX_HOST_LENGTH = 256; // 用于存储 host 的最大长度 +const size_t MAX_SERV_LENGTH = 256; // 用于存储 serv 的最大长度 +const size_t NET_ID_ARG_INDEX = 2; // 代表 netId 的索引位置,表示接收到的第三个参数 +const size_t HOST_ARG_INDEX = 0; // args 数组中的索引,表示 host 参数的位置 +const size_t SERV_ARG_INDEX = 1; // args 数组中的索引,表示 serv 参数的位置 +const int SUCCESS_CODE = 0; // 表示成功的状态码 + +// 封装错误结果生成函数 +napi_value CreateErrorResult(napi_env env, int32_t errorCode) +{ + napi_value result; + napi_create_int32(env, errorCode, &result); + return result; +} + +// [Start build_project1] +// 获取默认网络的函数 +static napi_value GetDefaultNet(napi_env env, napi_callback_info info) +{ + size_t argc = 1; // 期望接收一个函数 + napi_value args[1] = {nullptr}; // 存储接收到的参数 + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + // [StartExclude build_project1] + if (argc < 1) { // 如果参数小于1 + return CreateErrorResult(env, PARAMETER_ERROR_CODE); + } + + int32_t param; + napi_get_value_int32(env, args[0], ¶m); // 从 args[0] 获取整数值并存储到 param 中 + // [EndExclude build_project1] + NetConn_NetHandle netHandle; + if (param == 0) { // 如果参数是0 + param = OH_NetConn_GetDefaultNet(NULL); + } else { + param = OH_NetConn_GetDefaultNet(&netHandle); + } + + napi_value result; + napi_create_int32(env, param, &result); + return result; +} + +// 获取默认网络ID的函数 +static napi_value NetId(napi_env env, napi_callback_info info) +{ + int32_t defaultNetId; + NetConn_NetHandle netHandle; + OH_NetConn_GetDefaultNet(&netHandle); + defaultNetId = netHandle.netId; // 获取默认的 netId + napi_value result; + napi_create_int32(env, defaultNetId, &result); + return result; +} +// [End build_project1] + +// 检查默认网络是否激活的函数 +static napi_value HasDefaultNet(napi_env env, napi_callback_info info) +{ + int32_t hasDefaultNet = 0; + int32_t result = OH_NetConn_HasDefaultNet(&hasDefaultNet); // 使用引用传递 hasDefaultNet + napi_value napiResult; + if (result == 0) { + napi_create_int32(env, hasDefaultNet, &napiResult); // 激活为 1,未激活为 0 + } else { + napi_create_int32(env, result, &napiResult); // 返回错误码 + } + + return napiResult; +} + +// 检查默认网络的流量是否受限 +static napi_value IsDefaultNetMetered(napi_env env, napi_callback_info info) +{ + int32_t isMetered = 0; + int32_t result = OH_NetConn_IsDefaultNetMetered(&isMetered); + napi_value napiResult; + if (result == 0) { + napi_create_int32(env, isMetered, &napiResult); // 如果受限返回 1,未受限返回 0 + } else { + napi_create_int32(env, result, &napiResult); // 返回错误码 + } + + return napiResult; +} + +// 获取指定网络连接属性的函数 +static napi_value GetConnectionProperties(napi_env env, napi_callback_info info) +{ + napi_value result; + napi_status status; + size_t argc = 1; + napi_value args[1] = {nullptr}; + status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok || argc < 1) { + return CreateErrorResult(env, PARAMETER_ERROR_CODE); // 参数错误 + } + + NetConn_NetHandle netHandle; + napi_get_value_int32(env, args[0], &netHandle.netId); + NetConn_ConnectionProperties connProps; + int32_t statusCode = OH_NetConn_GetConnectionProperties(&netHandle, &connProps); + if (statusCode == SUCCESS_CODE) { + napi_create_int32(env, 0, &result); + } else { + napi_create_int32(env, statusCode, &result); // 返回错误码 + } + + return result; +} + +// 获取网络能力的函数 +static napi_value GetNetCapabilities(napi_env env, napi_callback_info info) +{ + napi_value result; + napi_status status; + size_t argc = 1; + napi_value args[1] = {nullptr}; + status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok || argc < 1) { + return CreateErrorResult(env, PARAMETER_ERROR_CODE); // 参数错误 + } + + NetConn_NetHandle netHandle; + napi_get_value_int32(env, args[0], &netHandle.netId); + NetConn_NetCapabilities netCapabilities; + int32_t statusCode = OH_NetConn_GetNetCapabilities(&netHandle, &netCapabilities); + if (statusCode == SUCCESS_CODE) { + napi_create_int32(env, 0, &result); // 成功 + } else { + napi_create_int32(env, statusCode, &result); // 返回错误码 + } + + return result; +} + +// 获取默认 HTTP 代理配置的函数 +static napi_value GetDefaultHttpProxy(napi_env env, napi_callback_info info) +{ + napi_value result; + napi_status status; + NetConn_HttpProxy httpProxy = {}; + int32_t statusCode = OH_NetConn_GetDefaultHttpProxy(&httpProxy); + if (statusCode == SUCCESS_CODE) { + napi_create_int32(env, 0, &result); // 成功 + } else { + napi_create_int32(env, statusCode, &result); // 返回错误码 + } + + return result; +} + +// 获取 DNS 信息的函数 +static napi_value GetAddrInfo(napi_env env, napi_callback_info info) +{ + napi_value result; + napi_status status; + size_t argc = MIN_ARG_COUNT; // 期望接收 3 个参数 + napi_value args[MIN_ARG_COUNT] = {nullptr}; // 存储接收到的参数 + status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + if (status != napi_ok || argc < MIN_ARG_COUNT) { + return CreateErrorResult(env, PARAMETER_ERROR_CODE); // 参数错误 + } + + char host[MAX_HOST_LENGTH]; + char serv[MAX_SERV_LENGTH]; + int32_t netId; + struct addrinfo hint; + struct addrinfo *res = nullptr; + napi_get_value_string_utf8(env, args[HOST_ARG_INDEX], host, sizeof(host), nullptr); + napi_get_value_string_utf8(env, args[SERV_ARG_INDEX], serv, sizeof(serv), nullptr); + napi_get_value_int32(env, args[NET_ID_ARG_INDEX], &netId); + memset(&hint, 0, sizeof(hint)); + int32_t statusCode = OH_NetConn_GetAddrInfo(host, serv, &hint, &res, netId); + if (statusCode == SUCCESS_CODE) { + napi_create_int32(env, SUCCESS_CODE, &result); // 成功 + struct addrinfo *cur = res; + while (cur != nullptr) { + cur = cur->ai_next; + } + OH_NetConn_FreeDnsResult(res); // 释放内存 + } else { + return CreateErrorResult(env, statusCode); // 错误:返回错误码 + } + + return result; +} + +// 获取所有活动的网络句柄的函数 +static napi_value GetAllNets(napi_env env, napi_callback_info info) +{ + napi_value result; + napi_status status; + NetConn_NetHandleList netHandleList; + memset(&netHandleList, 0, sizeof(netHandleList)); + int32_t statusCode = OH_NetConn_GetAllNets(&netHandleList); + if (statusCode == SUCCESS_CODE) { + napi_create_int32(env, 0, &result); // 成功 + } else { + napi_create_int32(env, statusCode, &result); // 返回错误码 + } + + return result; +} + + +// 自定义 DNS 解析器的函数 +int MyCustomDnsResolver(const char *host, const char *serv, const struct addrinfo *hint, struct addrinfo **res) +{ + struct addrinfo hints; + struct addrinfo *result; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + int err = getaddrinfo(host, serv, &hints, &result); + if (err != 0) { + return -1; + } + + *res = result; + return 0; +} + +// 注册自定义 DNS 解析器的函数 +static napi_value RegisterDnsResolver(napi_env env, napi_callback_info info) +{ + napi_value result; + napi_status status; + int statusCode = OH_NetConn_RegisterDnsResolver(MyCustomDnsResolver); + if (statusCode == SUCCESS_CODE) { + napi_create_int32(env, 0, &result); + } else { + napi_create_int32(env, statusCode, &result); + } + + return result; +} + +// 注销自定义 DNS 解析器的函数 +static napi_value UnregisterDnsResolver(napi_env env, napi_callback_info info) +{ + napi_value result; + napi_status status; + int32_t statusCode = OH_NetConn_UnregisterDnsResolver(); + if (statusCode == SUCCESS_CODE) { + napi_create_int32(env, 0, &result); + } else { + napi_create_int32(env, statusCode, &result); + } + + return result; +} + +// [Start build_project2] +EXTERN_C_START +static napi_value Init(napi_env env, napi_value exports) +{ + napi_property_descriptor desc[] = { + {"GetDefaultNet", nullptr, GetDefaultNet, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"NetId", nullptr, NetId, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"HasDefaultNet", nullptr, HasDefaultNet, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"IsDefaultNetMetered", nullptr, IsDefaultNetMetered, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"GetConnectionProperties", nullptr, GetConnectionProperties, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"GetNetCapabilities", nullptr, GetNetCapabilities, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"GetDefaultHttpProxy", nullptr, GetDefaultHttpProxy, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"GetAddrInfo", nullptr, GetAddrInfo, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"GetAllNets", nullptr, GetAllNets, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"RegisterDnsResolver", nullptr, RegisterDnsResolver, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"UnregisterDnsResolver", nullptr, UnregisterDnsResolver, nullptr, nullptr, nullptr, napi_default, nullptr}}; + + constexpr size_t desc_size = 11; // 定义描述符数组的大小,表示有 11 个属性描述符 + napi_define_properties(env, exports, desc_size, desc); + return exports; +} +EXTERN_C_END +// [End build_project2] + +// [Start build_project3] +static napi_module demoModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "entry", + .nm_priv = ((void *)0), + .reserved = {0}, +}; + +extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); } +// [End build_project3] \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/cpp/types/libentry/Index.d.ts b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/cpp/types/libentry/Index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..8be6c77492390ab88b059625b87384dea0cba88b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/cpp/types/libentry/Index.d.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ + +export const GetDefaultNet: (code: number) => number; +export const NetId: () => number; +export const HasDefaultNet: () => number; +export const IsDefaultNetMetered: () => number; +export const GetConnectionProperties: ( netId: number ) => number; +export const GetNetCapabilities: (netId: number ) => number; +export const GetDefaultHttpProxy: () => number; +export const GetAddrInfo: (host: string, serv: string, netId: number) => number; +export const GetAllNets: () => number; +export const RegisterDnsResolver: () => number; +export const UnregisterDnsResolver: () => number; diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/cpp/types/libentry/oh-package.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/cpp/types/libentry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..6abf3b7c20f22c62aaac6a995a25cae672f73f35 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/cpp/types/libentry/oh-package.json5 @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "libentry.so", + "types": "./Index.d.ts", + "version": "1.0.0", + "description": "Please describe the basic information." +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/ets/common/Logger.ets b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/ets/common/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..0df32f9f0fe6763084baf84dcca4bf5c45ce5d31 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/ets/common/Logger.ets @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; + +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0x0001; // 设置域名 + } + + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } + + fatal(...args: string[]) { + hilog.fatal(this.domain, this.prefix, this.format, args); + } + + isLoggable(level: number) { + hilog.isLoggable(this.domain, this.prefix, level); + } +} + +export default new Logger('[Sample_NetConnection_Exploitation]'); diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/ets/entryability/EntryAbility.ets b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..c5539bddd7a772710f55da0ddd7b5676305469de --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 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. + */ + +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +}; diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..b1e212947256c5533c7b06285a597c94f840a6e3 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/ets/pages/Index.ets b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..9e0ee8c2987197d832ca6602b48a04245aad9387 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2025 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. + */ + +// [Start build_project5] +import testNetManager from 'libentry.so'; +// [StartExclude build_project5] +import Logger from '../common/Logger'; + +enum ReturnCode { + SUCCESS = 0, // 操作成功 + MISSING_PERMISSION = 201, // 缺少权限 + PARAMETER_ERROR = 401, // 参数错误 +} + +// 常量定义 +interface SizeOptions { + SMALL: number; + MEDIUM: number; + LARGE?: number; +} + +const BORDER_RADIUS: number = 5; // 按钮圆角大小 +const BUTTON_HEIGHT: number = 50; // 按钮高度 + +// 字体大小定义 +const FONT_SIZE: SizeOptions = { + SMALL: 16, // 小号字体 + MEDIUM: 20, // 中号字体 + LARGE: 50, // 大号字体 +}; + +// 边距定义 +const MARGIN: SizeOptions = { + SMALL: 10, // 小边距 + MEDIUM: 20, // 中边距 + LARGE: 30, // 大边距 +}; + +interface NetHandle { + netId: number; +} + +@Entry +@Component +struct Index { + @State message: string = ''; // 用于展示日志消息 + @State logMessage: string = ''; // 用于展示 Logger 输出的日志 + + build() { + Column() { // 显示 Logger 输出的日志 + Text(this.logMessage) + .fontSize(FONT_SIZE.SMALL) + .fontColor(Color.Black) + .margin({ bottom: MARGIN.SMALL }) + .id('logger-output') // 为日志输出设置 ID,便于测试获取内容 + + // 显示测试消息 + Text(this.message) + .fontSize(FONT_SIZE.SMALL) + .fontColor(Color.Black) + .margin({ bottom: MARGIN.SMALL }) + .id('test-message') // 为测试消息设置 ID,便于测试获取内容 + // [EndExclude build_project5] + Button($r('app.string.GetDefaultNet')) + .onClick(() => { + this.GetDefaultNet(); + }) + // [StartExclude build_project5] + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN.SMALL }) + .backgroundColor(Color.Blue) + .fontColor(Color.White) + .fontSize(FONT_SIZE.SMALL) + .borderRadius(BORDER_RADIUS) + .id('get-default-net-btn') // 为按钮设置 ID,便于测试点击 + // [EndExclude build_project5] + + Button($r('app.string.CodeNumber')) + .onClick(() => { + this.CodeNumber(); + }) + // [StartExclude build_project5] + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN.SMALL }) + .backgroundColor(Color.Green) + .fontColor(Color.White) + .fontSize(FONT_SIZE.SMALL) + .borderRadius(BORDER_RADIUS) + .id('get-code-number-btn') + + Button($r('app.string.HasDefaultNet')) + .onClick(() => { + this.GetHasDefaultNet(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN.SMALL }) + .backgroundColor(Color.Orange) + .fontColor(Color.White) + .fontSize(FONT_SIZE.SMALL) + .borderRadius(BORDER_RADIUS) + .id('check-default-net-btn') + + Button($r('app.string.IsDefaultNetMetered')) + .onClick(() => { + this.GetIsDefaultNetMetered(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN.SMALL }) + .backgroundColor(Color.Grey) + .fontColor(Color.White) + .fontSize(FONT_SIZE.SMALL) + .borderRadius(BORDER_RADIUS) + .id('check-default-net-metered-btn') + + Button($r('app.string.GetConnectionProperties')) + .onClick(() => { + this.GetConnectionProperties(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN.SMALL }) + .backgroundColor(Color.Brown) + .fontColor(Color.White) + .fontSize(FONT_SIZE.SMALL) + .borderRadius(BORDER_RADIUS) + .id('get-connection-properties-btn') + + Button($r('app.string.GetNetCapabilities')) + .onClick(() => { + this.GetNetCapabilities(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN.SMALL }) + .backgroundColor(Color.Blue) + .fontColor(Color.White) + .fontSize(FONT_SIZE.SMALL) + .borderRadius(BORDER_RADIUS) + .id('get-net-capabilities-btn') + + Button($r('app.string.GetDefaultHttpProxy')) + .onClick(() => { + this.GetDefaultHttpProxy(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN.SMALL }) + .backgroundColor(Color.Orange) + .fontColor(Color.White) + .fontSize(FONT_SIZE.SMALL) + .borderRadius(BORDER_RADIUS) + .id('get-default-http-proxy-btn') + + Button($r('app.string.GetAddrInfo')) + .onClick(() => { + this.GetAddrInfo(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN.SMALL }) + .backgroundColor(Color.Black) + .fontColor(Color.White) + .fontSize(FONT_SIZE.SMALL) + .borderRadius(BORDER_RADIUS) + .id('get-addr-info-btn') + + Button($r('app.string.GetAllNets')) + .onClick(() => { + this.GetAllNets(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN.SMALL }) + .backgroundColor(Color.Red) + .fontColor(Color.White) + .fontSize(FONT_SIZE.SMALL) + .borderRadius(BORDER_RADIUS) + .id('get-all-nets-btn') + + Button($r('app.string.RegisterDnsResolver')) + .onClick(() => { + this.RegisterDnsResolver(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN.SMALL }) + .backgroundColor(Color.Blue) + .fontColor(Color.White) + .fontSize(FONT_SIZE.SMALL) + .borderRadius(BORDER_RADIUS) + .id('register-dns-resolver-btn') + + Button($r('app.string.UnregisterDnsResolver')) + .onClick(() => { + this.UnregisterDnsResolver(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: MARGIN.SMALL }) + .backgroundColor(Color.Blue) + .fontColor(Color.White) + .fontSize(FONT_SIZE.SMALL) + .borderRadius(BORDER_RADIUS) + .id('unregister-dns-resolver-btn') + }.width('100%').height('100%').justifyContent(FlexAlign.Center); + } + // [EndExclude build_project5] + + GetDefaultNet() { + try { + let netId = testNetManager.NetId(); + this.logMessage = `The defaultNetId is [${netId}]`; + Logger.info(this.logMessage); + } catch (error) { + this.logMessage = `Error fetching defaultNetId: ${error}`; + Logger.error(this.logMessage); + } + } + + CodeNumber() { + let testParam = 1; + try { + let codeNumber = testNetManager.GetDefaultNet(testParam); + switch (codeNumber) { + case ReturnCode.SUCCESS: + this.logMessage = `Test success. [${codeNumber}]`; + Logger.info(this.logMessage); + break; + case ReturnCode.MISSING_PERMISSION: + this.logMessage = `Missing permissions. [${codeNumber}]`; + Logger.warn(this.logMessage); + break; + case ReturnCode.PARAMETER_ERROR: + this.logMessage = `Parameter error. [${codeNumber}]`; + Logger.warn(this.logMessage); + break; + default: + this.logMessage = `Unexpected result: [${codeNumber}]`; + Logger.warn(this.logMessage); + break; + } + } catch (error) { + this.logMessage = `Error fetching CodeNumber: ${error}`; + Logger.error(this.logMessage); + } + } + // [End build_project5] + + GetHasDefaultNet() { + let hasDefaultNet = testNetManager.HasDefaultNet(); // 调用 C++ 部分的接口 + if (hasDefaultNet === 1) { + this.logMessage = 'The default network is activated.'; + Logger.info(this.logMessage); + } else if (hasDefaultNet === 0) { + this.logMessage = 'No default network activated.'; + Logger.info(this.logMessage); + } else { // 处理错误码 + this.logMessage = `Error: [${hasDefaultNet}]`; + Logger.error(this.logMessage); + } + } + + GetIsDefaultNetMetered() { + let isMetered = testNetManager.IsDefaultNetMetered(); // 调用 C++ 部分的接口 + if (isMetered === 1) { + this.logMessage = 'The default network data usage is metered.'; + Logger.info(this.logMessage); + } else if (isMetered === 0) { + this.logMessage = 'The default network data usage is not metered.'; + Logger.info(this.logMessage); + } else { // 处理错误码 + this.logMessage = `Error: [${isMetered}]`; + Logger.error(this.logMessage); + } + } + + GetConnectionProperties() { + let netId = testNetManager.NetId(); + let result = testNetManager.GetConnectionProperties(netId); // 调用 C++ 端的接口 + if (result === 0) { + this.logMessage = `Successfully fetched connection properties for network ID: ${netId}`; + Logger.info(this.logMessage); + } else { + this.logMessage = `Error fetching connection properties. Error code: ${result}`; + Logger.error(this.logMessage); + } + } + + GetNetCapabilities() { + let netId = testNetManager.NetId(); + let result = testNetManager.GetNetCapabilities(netId); // 调用 C++ 端的接口 + if (result === 0) { + this.logMessage = `Successfully fetched network capabilities for network ID: ${netId}`; + Logger.info(this.logMessage); + } else { + this.logMessage = `Error fetching network capabilities. Error code: ${result}`; + Logger.error(this.logMessage); + } + } + + GetDefaultHttpProxy() { + let result = testNetManager.GetDefaultHttpProxy(); // 调用 C++ 部分的接口 + if (result === 0) { + this.logMessage = `Successfully fetched the default HTTP proxy configuration.`; + Logger.info(this.logMessage); + } else { + this.logMessage = `Error fetching default HTTP proxy. Error code: ${result}`; + Logger.error(this.logMessage); + } + } + + GetAddrInfo() { + let host = 'example.com'; + let serv = 'http'; // Or any service you want to query (like "80", "443", etc.) + let netId = 0; // Use 0 for the default network + + let result = testNetManager.GetAddrInfo(host, serv, netId); // 调用 C++ 部分的接口 + if (result === 0) { + this.logMessage = `Successfully fetched DNS information for host: ${host}, service: ${serv}`; + Logger.info(this.logMessage); + } else { + this.logMessage = `Error fetching DNS info. Error code: ${result}`; + Logger.error(this.logMessage); + } + } + + GetAllNets() { + let result = testNetManager.GetAllNets(); // 调用 C++ 部分的接口 + if (result === 0) { + this.logMessage = 'Successfully fetched all active network handles.'; + Logger.info(this.logMessage); + } else { + this.logMessage = `Error fetching active network handles. Error code: ${result}`; + Logger.error(this.logMessage); + } + } + + RegisterDnsResolver() { + let result = testNetManager.RegisterDnsResolver(); // 调用 C++ 端的接口 + if (result === 0) { + this.logMessage = 'Successfully registered custom DNS resolver.'; + Logger.info(this.logMessage); + } else { + this.logMessage = `Error registering DNS resolver. Error code: ${result}`; + Logger.error(this.logMessage); + } + } + + UnregisterDnsResolver() { + let result = testNetManager.UnregisterDnsResolver(); // 调用 C++ 端的接口 + if (result === 0) { + this.logMessage = 'Successfully unregistered custom DNS resolver.'; + Logger.info(this.logMessage); + } else { + this.logMessage = `Error unregistering DNS resolver. Error code: ${result}`; + Logger.error(this.logMessage); + } + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/module.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..0b95c44301857394101e73f37cab7080e0767b36 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/module.json5 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:app_name", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.INTERNET", + "reason": "$string:grant_internet", + "usedScene": { + "abilities": [ + "MainAbility" + ], + "when": "inuse" + } + }, + { + "name": "ohos.permission.GET_NETWORK_INFO", + "reason": "$string:grant_get_network_info", + "usedScene": { + "abilities": [ + "MainAbility" + ], + "when": "inuse" + } + } + ] + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/element/color.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..78631d0183a9d61842072bd0b54b145dbb36a248 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/element/string.json @@ -0,0 +1,60 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "GetDefaultNet", + "value": "GetDefaultNet" + }, + { + "name": "CodeNumber", + "value": "CodeNumber" + }, + { + "name": "HasDefaultNet", + "value": "HasDefaultNet" + }, + { + "name": "IsDefaultNetMetered", + "value": "IsDefaultNetMetered" + }, + { + "name": "GetConnectionProperties", + "value": "GetConnectionProperties" + }, + { + "name": "GetNetCapabilities", + "value": "GetNetCapabilities" + }, + { + "name": "GetDefaultHttpProxy", + "value": "GetDefaultHttpProxy" + }, + { + "name": "GetAddrInfo", + "value": "GetAddrInfo" + }, + { + "name": "GetAllNets", + "value": "GetAllNets" + }, + { + "name": "RegisterDnsResolver", + "value": "RegisterDnsResolver" + }, + { + "name": "UnregisterDnsResolver", + "value": "UnregisterDnsResolver" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/media/background.png b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/media/background.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/media/foreground.png b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/media/foreground.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/media/layered_image.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/media/startIcon.png b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/media/startIcon.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/profile/backup_config.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/profile/main_pages.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/en_US/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..edf3d00bc5e1dc4f1e991a739089e802773db7af --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,68 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "GetDefaultNet", + "value": "GetDefaultNet" + }, + { + "name": "CodeNumber", + "value": "CodeNumber" + }, + { + "name": "HasDefaultNet", + "value": "HasDefaultNet" + }, + { + "name": "IsDefaultNetMetered", + "value": "IsDefaultNetMetered" + }, + { + "name": "GetConnectionProperties", + "value": "GetConnectionProperties" + }, + { + "name": "GetNetCapabilities", + "value": "GetNetCapabilities" + }, + { + "name": "GetDefaultHttpProxy", + "value": "GetDefaultHttpProxy" + }, + { + "name": "GetAddrInfo", + "value": "GetAddrInfo" + }, + { + "name": "GetAllNets", + "value": "GetAllNets" + }, + { + "name": "RegisterDnsResolver", + "value": "RegisterDnsResolver" + }, + { + "name": "UnregisterDnsResolver", + "value": "UnregisterDnsResolver" + }, + { + "name": "grant_internet", + "value": "grant_internet" + }, + { + "name": "grant_get_network_info", + "value": "grant_get_network_info" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/zh_CN/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..46a0c1dfbc363d729974a55260160884d90e0384 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,68 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "GetDefaultNet", + "value": "获取默认网络ID" + }, + { + "name": "CodeNumber", + "value": "获取响应状态码" + }, + { + "name": "GetAddrInfo", + "value": "通过netId获取DNS结果" + }, + { + "name": "GetAllNets", + "value": "查询所有激活的数据网络" + }, + { + "name": "GetConnectionProperties", + "value": "查询某个数据网络的链路信息" + }, + { + "name": "GetDefaultHttpProxy", + "value": "查询默认的网络代理" + }, + { + "name": "GetNetCapabilities", + "value": "查询某个网络的能力集" + }, + { + "name": "HasDefaultNet", + "value": "查询是否有默认激活的数据网络" + }, + { + "name": "IsDefaultNetMetered", + "value": "查询默认数据网络是否记流量" + }, + { + "name": "RegisterDnsResolver", + "value": "注册自定义 DNS 解析器" + }, + { + "name": "UnregisterDnsResolver", + "value": "取消注册自定义 DNS 解析器" + }, + { + "name": "grant_internet", + "value": "网络使用权限" + }, + { + "name": "grant_get_network_info", + "value": "获取网络信息权限" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/ohosTest/ets/test/Ability.test.ets b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..67cca9e94d8051941f367b43294bcd441b564b5a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { describe, expect, it } from '@ohos/hypium'; +import { abilityDelegatorRegistry, Driver, ON } from '@kit.TestKit'; +import { UIAbility, Want } from '@kit.AbilityKit'; + +// 常量定义 +const DOMAIN: number = 0xF812; // 日志域值 +const TAG: string = 'LoggerTest'; // 日志标签 +const BUNDLE: string = 'LOGGERSAMPLE_'; // 测试用例前缀 +const BUNDLENAME: string = 'com.samples.netconnection'; // 应用包名 +let driver: Driver = Driver.create(); + +const DELAY_MS: number = 3000; // 延迟时间 +const delegator = abilityDelegatorRegistry.getAbilityDelegator(); + +export default function LoggerTest() { + describe('LoggerTest', () => { + /** + * @tc.number StartAbility_001 + * @tc.name StartAbility_001 + * @tc.desc 启动Ability + */ + it(BUNDLE + 'StartAbility_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'StartAbility_001 begin'); + // start tested ability + const want: Want = { + bundleName: BUNDLENAME, + abilityName: 'EntryAbility', + }; + await delegator.startAbility(want); + await driver.delayMs(DELAY_MS); + const ability: UIAbility = await delegator.getCurrentTopAbility(); + expect(ability.context.abilityInfo.name).assertEqual('EntryAbility'); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'StartAbility_001 end'); + }); + + /** + * @tc.number Logger_case_001 + * @tc.name Logger_case_001 + * @tc.desc 测试 "GetDefaultNet" 按钮点击效果 + */ + it(BUNDLE + 'Logger_case_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_001 begin'); + await driver.delayMs(DELAY_MS); + // 验证按钮存在 + await driver.assertComponentExist(ON.id('get-default-net-btn')); + let button = await driver.findComponent(ON.id('get-default-net-btn')); + await button.click(); // 点击按钮 + await driver.delayMs(DELAY_MS); + // 验证日志输出 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('The defaultNetId is'); + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_001 end'); + done(); + }); + + /** + * @tc.number Logger_case_002 + * @tc.name Logger_case_002 + * @tc.desc 测试 "CodeNumber" 按钮点击效果 + */ + it(BUNDLE + 'Logger_case_002', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_002 begin'); + await driver.delayMs(DELAY_MS); + // 验证按钮存在 + await driver.assertComponentExist(ON.id('get-code-number-btn')); + let button = await driver.findComponent(ON.id('get-code-number-btn')); + await button.click(); // 点击按钮 + await driver.delayMs(DELAY_MS); + // 验证日志输出 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + + // 断言日志内容是否为预期的参数错误 + expect(result).assertContain('success'); + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_002 end'); + done(); + }); + + /** + * @tc.number Logger_case_003 + * @tc.name Logger_case_003 + * @tc.desc 测试 "HasDefaultNet" 按钮点击效果 + */ + it(BUNDLE + 'Logger_case_003', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_003 begin'); + await driver.delayMs(DELAY_MS); + // 验证按钮存在 + await driver.assertComponentExist(ON.id('check-default-net-btn')); + let button = await driver.findComponent(ON.id('check-default-net-btn')); + await button.click(); // 点击按钮 + await driver.delayMs(DELAY_MS); + // 验证日志输出 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('The default network is activated.'); + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_003 end'); + done(); + }); + + /** + * @tc.number Logger_case_004 + * @tc.name Logger_case_004 + * @tc.desc 测试 "IsDefaultNetMetered" 按钮点击效果 + */ + it(BUNDLE + 'Logger_case_004', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_004 begin'); + await driver.delayMs(DELAY_MS); + // 验证按钮存在 + await driver.assertComponentExist(ON.id('check-default-net-metered-btn')); + let button = await driver.findComponent(ON.id('check-default-net-metered-btn')); + await button.click(); // 点击按钮 + await driver.delayMs(DELAY_MS); + // 验证日志输出 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('The default network data usage is metered'); + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_004 end'); + done(); + }); + + /** + * @tc.number Logger_case_005 + * @tc.name Logger_case_005 + * @tc.desc 测试 "GetConnectionProperties" 按钮点击效果 + */ + it(BUNDLE + 'Logger_case_005', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_005 begin'); + await driver.delayMs(DELAY_MS); + // 验证按钮存在 + await driver.assertComponentExist(ON.id('get-connection-properties-btn')); + let button = await driver.findComponent(ON.id('get-connection-properties-btn')); + await button.click(); // 点击按钮 + await driver.delayMs(DELAY_MS); + // 验证日志输出 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('Successfully fetched connection properties for network ID:'); + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_005 end'); + done(); + }); + + /** + * @tc.number Logger_case_006 + * @tc.name Logger_case_006 + * @tc.desc 测试 "GetNetCapabilities" 按钮点击效果 + */ + it(BUNDLE + 'Logger_case_006', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_006 begin'); + await driver.delayMs(DELAY_MS); + // 验证按钮存在 + await driver.assertComponentExist(ON.id('get-net-capabilities-btn')); + let button = await driver.findComponent(ON.id('get-net-capabilities-btn')); + await button.click(); // 点击按钮 + await driver.delayMs(DELAY_MS); + // 验证日志输出 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('Successfully fetched network capabilities for network ID:'); + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_006 end'); + done(); + }); + + /** + * @tc.number Logger_case_007 + * @tc.name Logger_case_007 + * @tc.desc 测试 "GetDefaultHttpProxy" 按钮点击效果 + */ + it(BUNDLE + 'Logger_case_007', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_007 begin'); + await driver.delayMs(DELAY_MS); + // 验证按钮存在 + await driver.assertComponentExist(ON.id('get-default-http-proxy-btn')); + let button = await driver.findComponent(ON.id('get-default-http-proxy-btn')); + await button.click(); // 点击按钮 + await driver.delayMs(DELAY_MS); + // 验证日志输出 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('Successfully fetched the default HTTP proxy configuration'); + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_007 end'); + done(); + }); + + /** + * @tc.number Logger_case_008 + * @tc.name Logger_case_008 + * @tc.desc 测试 "GetAddrInfo" 按钮点击效果 + */ + it(BUNDLE + 'Logger_case_008', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_008 begin'); + await driver.delayMs(DELAY_MS); + // 验证按钮存在 + await driver.assertComponentExist(ON.id('get-addr-info-btn')); + let button = await driver.findComponent(ON.id('get-addr-info-btn')); + await button.click(); // 点击按钮 + await driver.delayMs(DELAY_MS); + // 验证日志输出 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('Successfully fetched DNS information for host'); + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_008 end'); + done(); + }); + + /** + * @tc.number Logger_case_009 + * @tc.name Logger_case_009 + * @tc.desc 测试 "GetAllNets" 按钮点击效果 + */ + it(BUNDLE + 'Logger_case_009', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_009 begin'); + await driver.delayMs(DELAY_MS); + // 验证按钮存在 + await driver.assertComponentExist(ON.id('get-all-nets-btn')); + let button = await driver.findComponent(ON.id('get-all-nets-btn')); + await button.click(); // 点击按钮 + await driver.delayMs(DELAY_MS); + // 验证日志输出 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('Successfully fetched all active network handles'); + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_009 end'); + done(); + }); + + /** + * @tc.number Logger_case_010 + * @tc.name Logger_case_010 + * @tc.desc 测试 "RegisterDnsResolver" 按钮点击效果 + */ + it(BUNDLE + 'Logger_case_010', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_010 begin'); + await driver.delayMs(DELAY_MS); + // 验证按钮存在 + await driver.assertComponentExist(ON.id('register-dns-resolver-btn')); + let button = await driver.findComponent(ON.id('register-dns-resolver-btn')); + await button.click(); // 点击按钮 + await driver.delayMs(DELAY_MS); + // 验证日志输出 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('Successfully registered custom DNS resolver'); + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_010 end'); + done(); + }); + + /** + * @tc.number Logger_case_011 + * @tc.name Logger_case_011 + * @tc.desc 测试 "UnregisterDnsResolver" 按钮点击效果 + */ + it(BUNDLE + 'Logger_case_011', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_011 begin'); + await driver.delayMs(DELAY_MS); + // 验证按钮存在 + await driver.assertComponentExist(ON.id('unregister-dns-resolver-btn')); + let button = await driver.findComponent(ON.id('unregister-dns-resolver-btn')); + await button.click(); // 点击按钮 + await driver.delayMs(DELAY_MS); + // 验证日志输出 + let loggerOutput = await driver.findComponent(ON.id('logger-output')); + let result = await loggerOutput.getText(); + expect(result).assertContain('Successfully unregistered custom DNS resolver'); + hilog.info(DOMAIN, TAG, BUNDLE + 'Logger_case_011 end'); + done(); + }); + }); +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/ohosTest/ets/test/List.test.ets b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..1eac52fcebe8958e19a7b8fed2e8f39c520a3e42 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ + +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/ohosTest/module.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c3fd9dda3040d888d9d8b0b62bcb5d3b6fbeb614 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/hvigor/hvigor-config.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..43beb743cbd25c3507b1cf8a744bf8197b3bf2fb --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/hvigor/hvigor-config.json5 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/hvigorfile.ts b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/oh-package.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..952ab183532e81c9c50e5de1e64393704e1e48fe --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.18", + "@ohos/hamock": "1.0.0" + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/ohosTest.md b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..eac50821921a97260e5becc58ed98234c133460e --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/ohosTest.md @@ -0,0 +1,17 @@ +# 测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +| ------------------------------------------- | ------------ | ---- | ------------------------------------------------------------ | -------- | -------- | +| 测试 “GetDefaultNet” 按钮点击效果 | 设备正常运行 | | Toast显示 “The defaultNetId is” | 是 | pass | +| 测试 “CodeNumber” 按钮点击效果 | 设备正常运行 | | Toast显示 “success” | 是 | pass | +| 测试 “HasDefaultNet” 按钮点击效果 | 设备正常运行 | | Toast显示 “The default network is activated.” | 是 | pass | +| 测试 “IsDefaultNetMetered” 按钮点击效果 | 设备正常运行 | | Toast显示 “The default network data usage is metered” | 是 | pass | +| 测试 “GetConnectionProperties” 按钮点击效果 | 设备正常运行 | | Toast显示 “Error fetching connection properties” | 是 | pass | +| 测试 “GetNetCapabilities” 按钮点击效果 | 设备正常运行 | | Toast显示 “Error fetching network capabilities” | 是 | pass | +| 测试 “GetDefaultHttpProxy” 按钮点击效果 | 设备正常运行 | | Toast显示 “Successfully fetched the default HTTP proxy configuration” | 是 | pass | +| 测试 “GetAddrInfo” 按钮点击效果 | 设备正常运行 | | Toast显示 “Error fetching DNS info” | 是 | pass | +| 测试 “GetAllNets” 按钮点击效果 | 设备正常运行 | | Toast显示 “Successfully fetched all active network handles” | 是 | pass | +| 测试 “RegisterDnsResolver” 按钮点击效果 | 设备正常运行 | | Toast显示 “Successfully registered custom DNS resolver” | 是 | pass | +| 测试 “UnregisterDnsResolver” 按钮点击效果 | 设备正常运行 | | Toast显示 “Successfully unregistered custom DNS resolver” | 是 | pass | diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/screenshots/CodeNumber.jpg b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/screenshots/CodeNumber.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2db6db27dab5f77fe107ac350a0b9b7aa92e0d8d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/screenshots/CodeNumber.jpg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/screenshots/GetDefaultNet.jpg b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/screenshots/GetDefaultNet.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2d66e622141398d6787b88721875fbc57b3ade32 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/screenshots/GetDefaultNet.jpg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/screenshots/GetHasDefaultNet.jpg b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/screenshots/GetHasDefaultNet.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a021160c9d25b226c5a8793f403d2d945c773f25 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/screenshots/GetHasDefaultNet.jpg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/screenshots/GetIsDefaultNetMetered.jpg b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/screenshots/GetIsDefaultNetMetered.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1ea178b61f8fcbd284c7172844eb74794c574a6d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Exploitation_case/screenshots/GetIsDefaultNetMetered.jpg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/.gitignore b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/AppScope/app.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..554db477ddfa320632b8a99ff6949031a630e6ad --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "bundleName": "com.samples.NetConnection_Manage", + "vendor": "samples", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/AppScope/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..8a0909207ea31ce93e398d51081c5f1fff1f1710 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "NetConnection_Manage" + } + ] +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/AppScope/resources/base/media/app_icon.png b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/AppScope/resources/base/media/app_icon.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/README_zh.md b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..cc800b599c7aacaa163a2eaad2cf942dec33d605 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/README_zh.md @@ -0,0 +1,91 @@ +# NetConnection_Manage_case(网络连接管理) + +### 介绍 + +本示例依照指南 系统->网络->Network Kit(网络服务)->Network Kit网络管理能力->[网络连接管理](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/network/net-connection-manager.md)进行编写。网络连接管理提供管理网络一些基础能力,包括WiFi/蜂窝/Ethernet等多网络连接优先级管理、网络质量评估、订阅默认/指定网络连接状态变化、查询网络连接信息等功能。本项目展示了一个网络连接管理的示例应用,它实现了通过按钮控制网络连接、获取已连接网络、获取默认网络以及获取默认网络的IP地址的功能,使用了`@ohos.net.connection`接口。 + +### 效果预览 + +|网络连接|连接并显示网络状态|使用Socket建立连接|获取网络相关信息| +|--------------------------------|--------------------------------|--------------------------------|--------------------------------| +|![image](screenshots/NetworkConnection.jpg)|![image](screenshots/Connected.jpg)|![image](screenshots/GetSocketConnected.jpg)|![image](screenshots/GetNetworkInfo.jpg)| + +使用说明 + +1. 点击“连接网络并接收网络状态”按钮建立网络连接,并更新连接状态。 +2. 点击“断开网络”按钮将断开当前网络连接,并更新断开状态。 +3. 点击“使用Socket模块建立连接”按钮建立网络连接,并发送消息。 +4. 点击“获取已注册的网络”按钮,并更新获取状态。 +5. 点击“获取默认网络信息”按纽获取,并更新获取状态。 +6. 点击“获取默认网络IP”按钮,应用程序将获取并展示获取状态。 + +### 工程目录 + +``` +entry/src/main/ets/ +|---common +| |---Logger.ts // 日志工具 +|---entryability +| │---EntryAbility.ets +|---entrybackupability +│ |---EntryBackupAbility.ets +|---pages +│ |---Index.ets // 主页 +``` + +### 具体实现 + +1. 网络连接管理: + +- 使用 `@kit.NetworkKit` 提供的 `connection` 和 `socket` 模块,创建和管理网络连接。通过 `connection.createNetConnection()` 监听网络变化(如网络连接和断开),通过 `socket.TCPSocket` 处理TCP连接。 +- 当默认网络发生变化时,通过监听 `netAvailable` 事件重新建立TCP连接。 + +2. Socket通信: + +- 使用 `socket.TCPSocket` 对象进行TCP连接和数据发送。通过指定连接的地址和端口号建立连接,并向服务器发送数据(如“Hello, server!”)。 +- 提供错误处理机制:若连接失败,会显示提示信息,并且若数据发送失败,也会做相应的错误提示。 + +3. 网络状态显示与操作: + +- 页面通过UI组件(如 `Text`, `Button`)显示当前的网络连接状态(如是否已连接、是否已断开)以及网络信息(如可用网络、默认网络等)。 +- 提供按钮来执行不同的操作,如连接网络、断开网络、重新连接网络、获取网络信息等。 + +4. 单例管理: + +- `GlobalContext` 类作为全局上下文,用于存储和管理网络连接信息(如当前网络列表和当前网络句柄),并支持获取和设置对象。 + +5. 网络连接状态管理: + +- 通过 `connectNetwork()` 和 `disconnectNetwork()` 方法来管理网络连接的建立和断开。`connectNetwork()` 方法通过指定网络类型(如WiFi)连接网络,`disconnectNetwork()` 方法则用于取消订阅并断开连接。 +- `getAllNetworks()` 和 `getDefaultNetwork()` 方法用于获取所有网络连接及当前默认网络的信息,包括网络的能力和连接属性 + +### 相关权限 + +[ohos.permission.INTERNET](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-all.md#ohospermissioninternet) + +[ohos.permission.GET_NETWORK_INFO](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-all.md#ohospermissionget_network_info) + +### 依赖 + +不涉及。 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:华为手机。 +2. HarmonyOS系统:HarmonyOS 5.1.1 Release及以上。 +3. DevEco Studio版本:DevEco Studio 5.1.1 Release及以上。 +4. HarmonyOS SDK版本:HarmonyOS 5.1.1 Release及以上。 +5. 本示例需要用任意方式连接网络使用(示例代码为以连接wifi为例)。 +6. 在使用本示例前,用户需自行配置TCP服务器,推荐使用python脚本。确保服务器的IP地址和端口号正确,并且服务器与客户端能够通过相同的网络进行通信。客户端和服务器需要连接到相同的热点或局域网,确保两者在同一网络环境下才能正常建立连接。根据实际情况配置服务器的IP地址。客户端在启动前需确认服务器IP已正确配置,以确保能够成功连接。 + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/ > .git/info/sparse-checkout +git remote add origin https://gitee.com/harmonyos_samples/guide-snippets.git +git pull origin master +``` \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/build-profile.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..bcdaa331e902c0894a5fa0ea1902ef6ddd7ac037 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/build-profile.json5 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "targetSdkVersion": "5.1.1(19)", + "compatibleSdkVersion": "5.1.1(19)", + "runtimeOS": "HarmonyOS" + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/.gitignore b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/build-profile.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..eed6635f664fa08b992d84590bf62be7e0bb5381 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/build-profile.json5 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/hvigorfile.ts b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4f43d54667f8327c367c8096bd08bb8c75aff54 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/obfuscation-rules.txt b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..fdbb5b9852d7dd5f39bddaeb21ab5ee1f3346749 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/obfuscation-rules.txt @@ -0,0 +1,22 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/oh-package.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c9cb6c8174858277c9b0d465a51547dcab16d5ff --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/ets/common/Logger.ets b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/ets/common/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..ddec90b69c9a2425ce4dd21fc9d43b92a11fbc83 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/ets/common/Logger.ets @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; + +class Logger { + private domain: number + private prefix: string + private format: string = '%{public}s' + + constructor(prefix: string) { + this.prefix = prefix + this.domain = 0x0001 // 默认日志域值 + } + + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args) + } + + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args) + } + + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args) + } + + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args) + } + + fatal(...args: string[]) { + hilog.fatal(this.domain, this.prefix, this.format, args) + } + + isLoggable(level: number) { + hilog.isLoggable(this.domain, this.prefix, level) + } +} + +export default new Logger('[Sample_NetConnection_Manage]') diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/ets/entryability/EntryAbility.ets b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..0f2f8b94aa24b0a50e272270e4e18b6df93ac5fd --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 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. + */ + +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..b1e212947256c5533c7b06285a597c94f840a6e3 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/ets/pages/Index.ets b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..650d981428fe743aba8b53b7d2574509f4b4d09b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2025 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. + */ + +import { connection, socket } from '@kit.NetworkKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import Logger from '../common/Logger'; +import { promptAction } from '@kit.ArkUI'; + +const NETWORK_CONNECTION_TIMEOUT = 1000; // 网络连接超时 +const BUTTON_HEIGHT = 50; // 按钮的高度 +const BUTTON_FONT_SIZE = 20; // 按钮的字体大小 +const BUTTON_RADIUS = 5; // 按钮的圆角半径 +const TEXT_FONT_SIZE = 18; // 文本的字体大小 +const TEXT_MARGIN_TOP = 10; // 文本的上边距 +const TEXT_MARGIN_TOP_L = 20; // 长文本的上边距 + +let sock: socket.TCPSocket = socket.constructTCPSocketInstance(); + +let netAddress: socket.NetAddress = { + address: '', + port: 8080 // 端口号,默认设置为8080 +}; + +// [Start original_network_connection_uses_socket] +// [Start monitor_default_network_change] +// 监控默认网络变化 +async function test() { + const netConnection = connection.createNetConnection(); + /* 监听默认网络改变 */ + netConnection.on('netAvailable', (data: connection.NetHandle) => { + Logger.info(JSON.stringify(data)); + }); +} +// [End monitor_default_network_change] + +// 原网络连接使用Socket模块建立连接 +async function useSocket() { + let tcpConnectOptions: socket.TCPConnectOptions = { + address: netAddress, + timeout: 6000 // 连接超时时间 + }; + + /* 建立socket连接 */ + sock.connect(tcpConnectOptions, (err: BusinessError) => { + if (err) { + Logger.error('connect fail'); + promptAction.showToast({ + message: 'Connect fail!', + duration: 4000, // 提示消息持续时间 + bottom: 300 // 提示消息与底部的距离 + }); + return; + } + Logger.info('connect success'); + + /* 通过socket发送数据 */ + let tcpSendOptions: socket.TCPSendOptions = { + data: 'Hello, server!' + }; + sock.send(tcpSendOptions).then(() => { + Logger.info('send success'); + promptAction.showToast({ + message: 'Connect and Send successfully!', + duration: 4000, // 提示消息持续时间 + bottom: 300 // 提示消息与底部的距离 + }); + }).catch((err: BusinessError) => { + Logger.error('send fail'); + promptAction.showToast({ + message: 'Send fail!', + duration: 4000, // 提示消息持续时间 + bottom: 300 // 提示消息与底部的距离 + }); + }); + }); +} + +async function socketTest() { + const netConnection = connection.createNetConnection(); + netConnection.on('netAvailable', async (netHandle: connection.NetHandle) => { + Logger.info('default network changed'); + await sock.close(); + sock = socket.constructTCPSocketInstance(); + useSocket(); + }); + try { + netConnection.register(() => { + }); + useSocket(); + } catch (e) { + Logger.error(e.code.toString()); + }; +} +// [End original_network_connection_uses_socket] + +// [Start construct_singleton_object] +// 构造单例对象 +export class GlobalContext { + private static instance: GlobalContext; + public netList: connection.NetHandle[] = []; + public netHandle: connection.NetHandle | null = null; + private _objects = new Map(); + + private constructor() { + } + + public static getContext(): GlobalContext { + if (!GlobalContext.instance) { + GlobalContext.instance = new GlobalContext(); + } + return GlobalContext.instance; + } + + getObject(value: string): Object | undefined { + return this._objects.get(value); + } + + setObject(key: string, objectClass: Object): void { + this._objects.set(key, objectClass); + } +} +// [End construct_singleton_object] + +export function resourceToString(resource: Resource): string { + return getContext().resourceManager.getStringSync(resource); +} + +@Entry +@Component +struct NetworkConnectionPage { + @State connectStatus: string = 'Not connected'; + @State disconnectStatus: string = 'Not disconnected'; + @State allNetworksStatus: string = 'Not retrieved'; + @State defaultNetworkStatus: string = 'Not retrieved'; + @State defaultNetworkIPStatus: string = 'Not retrieved'; + private conn: connection.NetConnection | null = null; + + onInit() { + test(); // 开始监听网络变化 + } + + build() { + Column() { + Text($r('app.string.Network_Connection_Example')) + .fontSize(BUTTON_FONT_SIZE) + .margin({ bottom: TEXT_MARGIN_TOP_L }) + .id('Network_Connection_Example') + + Text(resourceToString($r('app.string.connectStatus')) + ':' + this.connectStatus) + .fontSize(TEXT_FONT_SIZE) + .fontColor(Color.Black) + .margin({ top: TEXT_MARGIN_TOP }) + .id('connectStatus') + + Text(resourceToString($r('app.string.disconnectStatus')) + ':' + this.disconnectStatus) + .fontSize(TEXT_FONT_SIZE) + .fontColor(Color.Black) + .margin({ top: TEXT_MARGIN_TOP }) + .id('disconnectStatus') + + Text(resourceToString($r('app.string.allNetworksStatus')) + ':' + this.allNetworksStatus) + .fontSize(TEXT_FONT_SIZE) + .fontColor(Color.Black) + .margin({ top: TEXT_MARGIN_TOP }) + .id('allNetworksStatus') + + Text(resourceToString($r('app.string.defaultNetworkStatus')) + ':' + this.defaultNetworkStatus) + .fontSize(TEXT_FONT_SIZE) + .fontColor(Color.Black) + .margin({ top: TEXT_MARGIN_TOP }) + .id('defaultNetworkStatus') + + Text(resourceToString($r('app.string.defaultNetworkIPStatus')) + ':' + this.defaultNetworkIPStatus) + .fontSize(TEXT_FONT_SIZE) + .fontColor(Color.Black) + .margin({ top: TEXT_MARGIN_TOP }) + .id('defaultNetworkIPStatus') + + Button($r('app.string.Connect_Network')) + .onClick(() => { + this.connectNetwork(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: TEXT_MARGIN_TOP_L }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_RADIUS) + .id('Connect_Network') + + Button($r('app.string.Disconnect_Network')) + .onClick(() => { + this.disconnectNetwork(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: TEXT_MARGIN_TOP_L }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_RADIUS) + .id('Disconnect_Network') + + Button($r('app.string.Reconnect_Network')) + .onClick(() => { + socketTest(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: TEXT_MARGIN_TOP_L }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_RADIUS) + .id('SocketConnect_Network') + + Button($r('app.string.Get_Connected_Network')) + .onClick(() => { + this.getAllNetworks(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: TEXT_MARGIN_TOP_L }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_RADIUS) + .id('Get_Connected_Network') + + Button($r('app.string.Get_Default_Network')) + .onClick(() => { + this.getDefaultNetwork(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: TEXT_MARGIN_TOP_L }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_RADIUS) + .id('Get_Default_Network') + + Button($r('app.string.Get_Default_Network_IP')) + .onClick(() => { + this.getDefaultNetworkIP(); + }) + .width('80%') + .height(BUTTON_HEIGHT) + .margin({ top: TEXT_MARGIN_TOP_L }) + .fontColor(Color.White) + .fontSize(BUTTON_FONT_SIZE) + .borderRadius(BUTTON_RADIUS) + .id('Get_Default_Network_IP') + } + .width('100%') + .height('100%') + .justifyContent(FlexAlign.Center) + } + + // [Start notification_of_network_status_change] + // 连接并接收指定网络的状态变化通知 + connectNetwork() { + if (this.conn) { + Logger.info('Network connection already established.'); + return; // 如果已有连接,直接返回 + }; + + let netSpecifier: connection.NetSpecifier = { + netCapabilities: { + // 假设当前默认网络是蜂窝网络连接,需要创建WIFI网络连接,可指定网络类型为WIFI + bearerTypes: [connection.NetBearType.BEARER_WIFI], + // 指定网络能力为Internet + networkCap: [connection.NetCap.NET_CAPABILITY_INTERNET], + } + }; + + // 指定超时时间为10s(默认值为0) + let TIMEOUT = 10 * NETWORK_CONNECTION_TIMEOUT; + + // 创建NetConnection对象 + this.conn = connection.createNetConnection(netSpecifier, TIMEOUT); + + // 订阅连接状态变化 + this.conn.register((err: BusinessError, data: void) => { + if (err) { + Logger.error('Error occurred during connection:', JSON.stringify(err)); + this.connectStatus = 'Connection failed'; + return; + } + }); + + // 订阅事件,如果当前指定网络可用,通过on_netAvailable通知用户 + this.conn.on('netAvailable', (data: connection.NetHandle) => { + Logger.info('Network available, NetId is ' + data.netId); + this.connectStatus = `Network available, NetId: ${data.netId}`; + }); + + // 订阅事件,如果当前指定网络不可用,通过on_netUnavailable通知用户 + this.conn.on('netUnavailable', (data: void) => { + Logger.info('Network unavailable, data is ' + JSON.stringify(data)); + this.connectStatus = 'Network unavailable'; + }); + + Logger.info('Network connection attempt started'); + } + + // 当不使用该网络时,可以调用该对象的unregister()方法,取消订阅 + disconnectNetwork() { + if (this.conn) { + this.conn.unregister((err: BusinessError, data: void) => { + if (err) { + Logger.error('Error occurred during unsubscription:', JSON.stringify(err)); + } + }); + this.conn = null; + this.disconnectStatus = 'Network disconnected'; + Logger.info('Network connection disconnected.'); + } else { + Logger.info('No active network connection.'); + } + } + // [End notification_of_network_status_change] + + // [Start query_the_capability_information_and_connection_information] + // [Start get_all_registered_networks] + // 获取所有注册的网络 + getAllNetworks() { + // 调用getAllNets,获取所有处于连接状态的网络列表(Array) + connection.getAllNets().then((data: connection.NetHandle[]) => { + Logger.info('getAllNets get data: ' + JSON.stringify(data)); + if (data) { + this.allNetworksStatus = 'Retrieved successfully'; + GlobalContext.getContext().netList = data; + + let itemNumber: Set = new Set(GlobalContext.getContext().netList); + let dataNumber = Array.from(itemNumber.values()); + for (let item of dataNumber) { + // 循环获取网络列表每个netHandle对应网络的能力信息 + connection.getNetCapabilities(item).then((data: connection.NetCapabilities) => { + Logger.info('getNetCapabilities get data: ' + JSON.stringify(data)); + }); + + // 循环获取网络列表每个netHandle对应的网络的连接信息 + connection.getConnectionProperties(item).then((data: connection.ConnectionProperties) => { + Logger.info('getConnectionProperties get data: ' + JSON.stringify(data)); + }); + } + } + }); + } + // [End get_all_registered_networks] + + // 根据数据网络查询网络的能力信息及连接信息 + getDefaultNetwork() { + // 调用getDefaultNet方法,获取默认的数据网络(NetHandle) + connection.getDefaultNet().then((data: connection.NetHandle) => { + if (data.netId == 0) { + // 当前无默认网络时,获取的netHandler的netid为0,属于异常情况,需要额外处理 + return; + } + if (data) { + this.defaultNetworkStatus = 'Retrieved successfully' + Logger.info('getDefaultNet get data: ' + JSON.stringify(data)); + GlobalContext.getContext().netHandle = data; + // 获取netHandle对应网络的能力信息。能力信息包含了网络类型、网络具体能力等网络信息 + connection.getNetCapabilities(GlobalContext.getContext().netHandle).then( + (data: connection.NetCapabilities) => { + Logger.info('getNetCapabilities get data: ' + JSON.stringify(data)); + // 获取网络类型(bearerTypes) + let bearerTypes: Set = new Set(data.bearerTypes); + let bearerTypesNum = Array.from(bearerTypes.values()); + for (let item of bearerTypesNum) { + if (item == 0) { + // 蜂窝网 + Logger.info(JSON.stringify('BEARER_CELLULAR')); + } else if (item == 1) { + // Wi-Fi网络 + Logger.info(JSON.stringify('BEARER_WIFI')); + } else if (item == 3) { + // 以太网网络 + Logger.info(JSON.stringify('BEARER_ETHERNET')); + } + } + + // 获取网络具体能力(networkCap) + let itemNumber: Set = new Set(data.networkCap); + let dataNumber = Array.from(itemNumber.values()); + for (let item of dataNumber) { + if (item == 0) { + // 表示网络可以访问运营商的MMSC(Multimedia Message Service,多媒体短信服务)发送和接收彩信 + Logger.info(JSON.stringify('NET_CAPABILITY_MMS')); + } else if (item == 11) { + // 表示网络流量未被计费 + Logger.info(JSON.stringify('NET_CAPABILITY_NOT_METERED')); + } else if (item == 12) { + // 表示该网络应具有访问Internet的能力,该能力由网络提供者设置 + Logger.info(JSON.stringify('NET_CAPABILITY_INTERNET')); + } else if (item == 15) { + // 表示网络不使用VPN(Virtual Private Network,虚拟专用网络) + Logger.info(JSON.stringify('NET_CAPABILITY_NOT_VPN')); + } else if (item == 16) { + // 表示该网络访问Internet的能力被网络管理成功验证,该能力由网络管理模块设置 + Logger.info(JSON.stringify('NET_CAPABILITY_VALIDATED')); + } + } + }); + } + }); + } + // [End query_the_capability_information_and_connection_information] + + // [Start resolve_the_domain_name_and_get_all_ips] + // 使用默认网络解析主机名以获取所有IP地址 + getDefaultNetworkIP() { + this.defaultNetworkIPStatus = 'Retrieving...'; + connection.getDefaultNet().then((data: connection.NetHandle) => { + if (data.netId === 0) { + this.defaultNetworkIPStatus = 'No default network'; + Logger.error('No default network available'); + return; + } + connection.getAddressesByName('localhost').then((data: connection.NetAddress[]) => { + Logger.info('Successfully retrieved default network IP address: ' + JSON.stringify(data)); + this.defaultNetworkIPStatus = 'Retrieved successfully'; + }).catch((err: BusinessError) => { + this.defaultNetworkIPStatus = 'Retrieval failed'; + Logger.error('Failed to retrieve default network IP address:', JSON.stringify(err)); + }); + }).catch((err: BusinessError) => { + this.defaultNetworkIPStatus = 'Retrieval failed'; + Logger.error('Failed to retrieve default network:', JSON.stringify(err)); + }); + } + // [End resolve_the_domain_name_and_get_all_ips] +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/module.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..e0bfb9d6fef7603464e77d64a1fd01373ca9b537 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/module.json5 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:app_name", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + } + ], + "requestPermissions": [ + { + "name": "ohos.permission.INTERNET", + "reason": "$string:grant_internet", + "usedScene": { + "abilities": [ + "MainAbility" + ], + "when": "inuse" + } + }, + { + "name": "ohos.permission.GET_NETWORK_INFO", + "reason": "$string:grant_get_network_info", + "usedScene": { + "abilities": [ + "MainAbility" + ], + "when": "inuse" + } + } + ] + } +} + diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/element/color.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..0700b01a8b57f9c9804cda8b0fc0a81d09976fdd --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/element/string.json @@ -0,0 +1,68 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "Network_Connection_Example", + "value": "网络连接示例" + }, + { + "name": "Connect_Network", + "value": "连接网络并接收网络状态变化" + }, + { + "name": "Disconnect_Network", + "value": "断开网络" + }, + { + "name": "Get_Connected_Network", + "value": "获取所有注册的网络" + }, + { + "name": "Get_Default_Network", + "value": "获取默认网络信息" + }, + { + "name": "Get_Default_Network_IP", + "value": "获取默认网络IP" + }, + { + "name": "connectStatus", + "value": "连接状态" + }, + { + "name": "disconnectStatus", + "value": "断开连接" + }, + { + "name": "allNetworksStatus", + "value": "已注册网络" + }, + { + "name": "defaultNetworkStatus", + "value": "默认网络状态" + }, + { + "name": "defaultNetworkIPStatus", + "value": "默认网络IP" + }, + { + "name": "Reconnect_Network", + "value": "使用Socket模块建立连接" + }, + { + "name": "allNetworkStatus", + "value": "已注册网络" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/media/background.png b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/media/background.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/media/foreground.png b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/media/foreground.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/media/layered_image.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/media/startIcon.png b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/media/startIcon.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/profile/backup_config.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/profile/main_pages.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/en_US/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..7df89c36d2844bd01212588b160bd8781f29b826 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,68 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "Network_Connection_Example", + "value": "Network_Connection_Example" + }, + { + "name": "Connect_Network", + "value": "Connect_Network" + }, + { + "name": "Disconnect_Network", + "value": "Disconnect_Network" + }, + { + "name": "Get_Connected_Network", + "value": "Get_Connected_Network" + }, + { + "name": "Get_Default_Network", + "value": "Get_Default_Network" + }, + { + "name": "Get_Default_Network_IP", + "value": "Get_Default_Network_IP" + }, + { + "name": "connectStatus", + "value": "connectStatus" + }, + { + "name": "disconnectStatus", + "value": "disconnectStatus" + }, + { + "name": "allNetworkStatus", + "value": "allNetworksStatus" + }, + { + "name": "defaultNetworkStatus", + "value": "defaultNetworkStatus" + }, + { + "name": "defaultNetworkIPStatus", + "value": "defaultNetworkIPStatus" + }, + { + "name": "grant_internet", + "value": "grant_internet" + }, + { + "name": "grant_get_network_info", + "value": "grant_get_network_info" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/zh_CN/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..0e79dd08230b748c6db9c9140e5fce7955198e58 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,68 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "Network_Connection_Example", + "value": "网络连接示例" + }, + { + "name": "Connect_Network", + "value": "连接网络并接收网络状态变化" + }, + { + "name": "Disconnect_Network", + "value": "断开网络" + }, + { + "name": "Get_Connected_Network", + "value": "获取已注册的网络" + }, + { + "name": "Get_Default_Network", + "value": "获取默认网络信息" + }, + { + "name": "Get_Default_Network_IP", + "value": "获取默认网络IP" + }, + { + "name": "connectStatus", + "value": "连接状态" + }, + { + "name": "disconnectStatus", + "value": "断开连接" + }, + { + "name": "allNetworkStatus", + "value": "已注册网络" + }, + { + "name": "defaultNetworkStatus", + "value": "默认网络状态" + }, + { + "name": "defaultNetworkIPStatus", + "value": "默认网络IP" + }, + { + "name": "grant_internet", + "value": "网络使用权限" + }, + { + "name": "grant_get_network_info", + "value": "获取网络信息权限" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/ohosTest/ets/test/Ability.test.ets b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..98b9b330a2fb0a53739bb8ceb5afc49c5c209683 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { describe, expect, it } from '@ohos/hypium'; +import { abilityDelegatorRegistry, Driver, ON } from '@kit.TestKit'; +import { UIAbility, Want } from '@kit.AbilityKit'; + +const delegator = abilityDelegatorRegistry.getAbilityDelegator(); +const DOMAIN = 0xF811; +const TAG = 'NetConnection_Manage'; +const BUNDLE = 'NetConnectionManage_' +const BUNDLENAME = 'com.samples.NetConnection_Manage'; +const INITIAL_DELAY = 2000; // 初始延迟 +const FIRST_CLICK_DELAY = 1000; // 第一次点击延迟 +const SECOND_CLICK_DELAY = 2000; // 第二次点击延迟 +const DELAY_TIME = 500; // 延迟时间 + +let driver: Driver = Driver.create(); + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + + /** + * @tc.number StartAbility_001 + * @tc.name StartAbility_001 + * @tc.desc Start the Ability + */ + it(BUNDLE + 'StartAbility_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'StartAbility_001 begin'); + const want: Want = { + bundleName: BUNDLENAME, + abilityName: 'EntryAbility', + }; + await delegator.startAbility(want); + await driver.delayMs(DELAY_TIME); + const ability: UIAbility = await delegator.getCurrentTopAbility(); + expect(ability.context.abilityInfo.name).assertEqual('EntryAbility'); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'StartAbility_001 end'); + }); + + /** + * @tc.number NetConnection_Connect_001 + * @tc.name NetConnection_Connect_001 + * @tc.desc Test the connect network button + */ + it(BUNDLE + 'NetConnection_Connect_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLENAME + 'NetConnection_Connect_001 begin'); + let driver = Driver.create(); + await driver.delayMs(INITIAL_DELAY); + await driver.assertComponentExist(ON.id('Connect_Network')); + let stack = await driver.findComponent(ON.id('Connect_Network')); + await stack.click(); + await driver.delayMs(FIRST_CLICK_DELAY); + await stack.click(); + await driver.delayMs(SECOND_CLICK_DELAY); + let resultText = await driver.findComponent(ON.id('connectStatus')); + let result = await resultText.getText(); + expect(result).assertContain('Network available'); + hilog.info(DOMAIN, TAG, BUNDLENAME + 'NetConnection_Connect_001 end'); + done(); + }); + + /** + * @tc.number NetConnection_Disconnect_001 + * @tc.name NetConnection_Disconnect_001 + * @tc.desc Test the disconnect network button + */ + it(BUNDLE + 'NetConnection_Disconnect_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLENAME + 'NetConnection_Disconnect_001 begin'); + let driver = Driver.create(); + await driver.delayMs(INITIAL_DELAY); + await driver.assertComponentExist(ON.id('Disconnect_Network')); + let stack = await driver.findComponent(ON.id('Disconnect_Network')); + await stack.click(); + await driver.delayMs(SECOND_CLICK_DELAY); + let resultText = await driver.findComponent(ON.id('disconnectStatus')); + let result = await resultText.getText(); + expect(result).assertContain('Network disconnected'); + hilog.info(DOMAIN, TAG, BUNDLENAME + 'NetConnection_Disconnect_001 end'); + done(); + }); + + /** + * @tc.number NetConnection_SocketConnect_001 + * @tc.name NetConnection_SocketConnect_001 + * @tc.desc Test the SocketConnect button + */ + it(BUNDLE + 'NetConnection_SocketConnect_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLENAME + 'NetConnection_SocketConnect_001 begin'); + let driver = Driver.create(); + await driver.delayMs(INITIAL_DELAY); + await driver.assertComponentExist(ON.id('SocketConnect_Network')); + let stack = await driver.findComponent(ON.id('SocketConnect_Network')); + await stack.click(); + await driver.delayMs(SECOND_CLICK_DELAY); + hilog.info(DOMAIN, TAG, BUNDLENAME + 'NetConnection_SocketConnect_001 end'); + done(); + }); + + /** + * @tc.number NetConnection_Acquisition_Connected_001 + * @tc.name NetConnection_Acquisition_001 + * @tc.desc Test the button to acquire connected network + */ + it(BUNDLE + 'NetConnection_Acquisition_Connected_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLENAME + 'NetConnection_Acquisition_Connected_001 begin'); + let driver = Driver.create(); + await driver.delayMs(INITIAL_DELAY); + await driver.assertComponentExist(ON.id('Get_Connected_Network')); + let stack = await driver.findComponent(ON.id('Get_Connected_Network')); + await stack.click(); + await driver.delayMs(SECOND_CLICK_DELAY); + let resultText = await driver.findComponent(ON.id('allNetworksStatus')); + let result = await resultText.getText(); + expect(result).assertContain('Retrieved successfully'); + hilog.info(DOMAIN, TAG, BUNDLENAME + 'NetConnection_Acquisition_Connected_001 end'); + done(); + }); + + /** + * @tc.number NetConnection_Acquisition_Default_001 + * @tc.name NetConnection_Acquisition_Default_001 + * @tc.desc Test the button to acquire default network + */ + it(BUNDLE + 'NetConnection_Acquisition_Default_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLENAME + 'NetConnection_Acquisition_Default_001 begin'); + let driver = Driver.create(); + await driver.delayMs(INITIAL_DELAY); + await driver.assertComponentExist(ON.id('Get_Default_Network')); + let stack = await driver.findComponent(ON.id('Get_Default_Network')); + await stack.click(); + await driver.delayMs(SECOND_CLICK_DELAY); + let resultText = await driver.findComponent(ON.id('defaultNetworkStatus')); + let result = await resultText.getText(); + expect(result).assertContain('Retrieved successfully'); + hilog.info(DOMAIN, TAG, BUNDLENAME + 'NetConnection_Acquisition_Default_001 end'); + done(); + }); + + /** + * @tc.number NetConnection_AcquisitionIP_Default_001 + * @tc.name NetConnection_AcquisitionIP_Default_001 + * @tc.desc Test the button to acquire default network IP + */ + it(BUNDLE + 'NetConnection_AcquisitionIP_Default_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLENAME + 'NetConnection_AcquisitionIP_Default_001 begin'); + let driver = Driver.create(); + await driver.delayMs(INITIAL_DELAY); + await driver.assertComponentExist(ON.id('Get_Default_Network_IP')); + let stack = await driver.findComponent(ON.id('Get_Default_Network_IP')); + await stack.click(); + await driver.delayMs(SECOND_CLICK_DELAY); + let resultText = await driver.findComponent(ON.id('defaultNetworkIPStatus')); + let result = await resultText.getText(); + expect(result).assertContain('Retrieved successfully'); + hilog.info(DOMAIN, TAG, BUNDLENAME + 'NetConnection_AcquisitionIP_Default_001 end'); + done(); + }); + + }) +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/ohosTest/ets/test/List.test.ets b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..fc447d595a7f1e27328cb4787eb5bdf071c3e580 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 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. + */ + +import abilityTest from './Ability.test'; +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/ohosTest/module.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c3fd9dda3040d888d9d8b0b62bcb5d3b6fbeb614 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/hvigor/hvigor-config.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..43beb743cbd25c3507b1cf8a744bf8197b3bf2fb --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/hvigor/hvigor-config.json5 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/hvigorfile.ts b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/oh-package.json5 b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..952ab183532e81c9c50e5de1e64393704e1e48fe --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.18", + "@ohos/hamock": "1.0.0" + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/ohosTest.md b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..a8f5d8a412c0d35b8639e16590564c74df115d2f --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/ohosTest.md @@ -0,0 +1,12 @@ +# 测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +| ----------------------- | --------------- | ---- | ----------------------------------------------------- | -------- | -------- | +| 点击“连接网络”按钮 | 设备正常运行 | | 显示 “Network available” | 是 | pass | +| 点击“断开网络”按钮 | 设备正常运行 | | 显示 “Network disconnected” | 是 | pass | +| 点击“SocketConnect”按钮 | 建立Socke服务器 | | 成功进行网络连接操作 | 是 | pass | +| 获取当前已连接的网络 | 设备正常运行 | | 获取当前连接的网络状态并显示 “Retrieved successfully” | 是 | pass | +| 获取默认网络 | 设备正常运行 | | 获取默认网络状态并显示 “Retrieved successfully” | 是 | pass | +| 获取默认网络的 IP 地址 | 设备正常运行 | | 获取默认网络 IP 地址并显示 “Retrieved successfully” | 是 | pass | diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/screenshots/Connected.jpg b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/screenshots/Connected.jpg new file mode 100644 index 0000000000000000000000000000000000000000..717376e7c47b999693237faec69a2b3a2c58594d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/screenshots/Connected.jpg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/screenshots/GetNetworkInfo.jpg b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/screenshots/GetNetworkInfo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..163e7f6d94644ec78c1758c99664908889665eef Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/screenshots/GetNetworkInfo.jpg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/screenshots/GetSocketConnected.jpg b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/screenshots/GetSocketConnected.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2118a6b18c54a7684842dd5eb122026492e6b8c3 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/screenshots/GetSocketConnected.jpg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/screenshots/NetworkConnection.jpg b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/screenshots/NetworkConnection.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c56b3e928f826b7d0b2cd254d7404efe806c77aa Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/NetConnection_Manage_case/screenshots/NetworkConnection.jpg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/.gitignore b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/AppScope/app.json5 b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..388cb4463886165549a2c9aafb79fd59cf25f917 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "bundleName": "com.samples.vpncontrol_case", + "vendor": "samples", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/AppScope/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..c10156cc7a3a30ddd80442180cc00548b2e8b79e --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "VPNControl_Case" + } + ] +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/AppScope/resources/base/media/app_icon.png b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/AppScope/resources/base/media/app_icon.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/README_zh.md b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..3bbc624915a94569585f6506b600ff795774df0f --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/README_zh.md @@ -0,0 +1,129 @@ +# VPN 应用 + +### 介绍 + +VPN 即虚拟专网(VPN-Virtual Private Network)在公用网络上建立专用网络的技术。整个 VPN 网络的任意两个节点之间的连接并没有传统专网所需的端到端的物理链路,而是架构在公用网络服务商所提供的网络平台(如 Internet)之上的逻辑网络,用户数据在逻辑链路中传输。本项目展示了一个管理的示例应用,它实现了通过按钮实现建立VPN 网络隧道、保护建立的 UDP 隧道以及建立一个 VPN 网络等功能,使用了[@ohos.net.vpn](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/reference/apis-network-kit/js-apis-net-connection.md)接口。 + +### 效果预览 + +| 程序启动 | 创建隧道 | 启动vpnExt | 关闭VPN | +| ----------------------------------- | --------------------------------------- | -------------------------------------- | ----------------------------------- | +| | | | | + +使用说明 + +备注:需在 `entry\src\main\module.json5` 文件的 `extensionAbilities` 节点下,为 `type` 字段手动新增 `vpn` 选项。 + +1. 在设备上启动应用。 + +2. 在界面上输入 VPN 服务器 IP 地址、虚拟网卡 IP 地址和需要阻止的应用包名。(也可以使用默认数据作为选项) + +3. 点击 `创建隧道` 按钮以建立 UDP 隧道。 + +4. 点击 `保护隧道` 按钮以将隧道与虚拟网卡绑定。 + +5. 点击启动VPN拓展程序启动VPN拓展能力。 + +6. 点击 `启动VPN` 按钮配置 VPN 并启动 VPN 服务。 + +7. 点击 `关闭VPN` 按钮以停止 VPN 连接。 + +8. 点击 `关闭VPN拓展程序` 按钮以停止 VPN 扩展能力。 + + + +### 工程目录 + +``` +entry/src/main/ets/ +|---entryability +| |---EntryAbility.ets // 请求相关权限 +|---notification +| |---NotificationContentUtil.ets // 初始化通知内容,提供基本通知内容创建功能 +| |---NotificationManagementUtil.ets // 管理通知,包括分类、统计、取消和设置角标 +| |---NotificationOperations.ets // 操作通知,支持发布可点击跳转的通知 +| |---NotificationRequestUtil.ets // 创建通知请求,支持基本和 WantAgent 类型的通知请求 +| |---NotificationUtil.ets // 提供通知发布、取消等功能,管理通知状态 +| |---WantAgentUtil.ets // 创建 WantAgent,用于启动能力或发送通用事件 +|---pages +| |---Index.ets // 首页 +| |---StartVpn.ets // 打开vpn +| |---StopVpn.ets // 关闭vpn +|---model +| |---Logger.ets // 日志 +| |---ShowToast.ets // 输出气泡 +| |---component.ets // 标题栏组件 +| |---CommonConstant.ets // 字符串常量 +|---serviceextability +| |---MyVpnExtAbility.ts // 三方vpn能力 + +entry/src/main/cpp/ +|---types +| |---libentry +| | |---index.d.ts // C++ 导出接口 +|---napi_init.cpp // 业务底层实现 +``` + +### 具体实现 + +1. 创建 VPN 隧道 + + - 创建 VPN 隧道的过程是通过 UDP 隧道与指定的 VPN 服务器建立连接。该过程是通过调用 `vpn_client.udpConnect()` 方法实现的,传入目标服务器的 IP 地址和端口号进行连接。成功连接后,可以使用该隧道进行数据传输。 + - 通过 `vpn_client.udpConnect()` 函数与指定的 VPN 服务器建立 UDP 隧道。 + - 如果连接成功,调用 `showToast()` 弹出成功提示,并记录日志。 + - 如果连接失败,弹出失败提示,并记录错误日志。 + +2. 保护 VPN 连接 + + - VPN 连接需要通过保护措施来确保数据安全性。`VpnConnection.protect()` 方法用于为已建立的隧道创建保护,防止数据泄漏。 + - 使用 `VpnConnection.protect()` 方法为 VPN 隧道添加保护。 + - 如果保护成功,显示成功提示并记录日志。 + - 如果保护失败,显示失败提示,并记录错误信息。 + +3. 启动 VPN 连接 + + - 启动 VPN 连接涉及创建一个 VPN 配置并启用连接。我们通过 `VpnConnection.create()` 创建配置,并通过 `vpn_client.startVpn()` 启动 VPN 服务。 + - 创建一个 `Config` 对象,配置包括虚拟网卡的 IP 地址、MTU 大小、DNS 地址等。 + - 调用 `VpnConnection.create()` 来建立 VPN 配置。 + - 调用 `vpn_client.startVpn()` 启动 VPN 服务。 + +4. 停止 VPN 连接 + + - 停止 VPN 连接的实现通过 `vpn_client.stopVpn()` 停止当前的 VPN 隧道连接,并清理相关资源。 + - 使用 `vpn_client.stopVpn()` 停止 VPN 连接。 + - 调用 `VpnConnection.destroy()` 销毁 VPN 连接对象。 + - 销毁操作成功后,弹出成功提示;失败则弹出错误信息。 + +5. 扩展 VPN 功能 + + - 通过 `vpnext.startVpnExtensionAbility()` 和 `vpnext.stopVpnExtensionAbility()` 方法,可以启动和停止 VPN 扩展能力,实现自定义的 VPN 功能。 + - 调用 `vpnext.startVpnExtensionAbility()` 启动自定义 VPN 扩展功能。 + - 启动成功后,显示提示并记录日志。 + - 启动失败时,显示错误信息并记录日志。 + +### 相关权限 + +[ohos.permission.INTERNET](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-all.md#ohospermissioninternet) + +### 依赖 + +不涉及。 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:华为手机。 +2. HarmonyOS系统:HarmonyOS 5.1.1 Release及以上。 +3. DevEco Studio版本:DevEco Studio 5.1.1 Release及以上。 +4. HarmonyOS SDK版本:HarmonyOS 5.1.1 Release及以上。 + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/ > .git/info/sparse-checkout +git remote add origin https://gitee.com/harmonyos_samples/guide-snippets.git +git pull origin master +``` \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/build-profile.json5 b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..b5f88409d7774aff8de42a519faf0e5106636aac --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/build-profile.json5 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "targetSdkVersion": "5.1.1(19)", + "compatibleSdkVersion": "5.1.1(19)", + "runtimeOS": "HarmonyOS", + "buildOption": { + "externalNativeOptions": { + "abiFilters": [ + "arm64-v8a", + "x86_64", + ] + } + } + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/.gitignore b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/build-profile.json5 b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..9aa8901faabcab3459fed187dcf9b2d155fd63f0 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/build-profile.json5 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "apiType": "stageMode", + "buildOption": { + "externalNativeOptions": { + "path": "./src/main/cpp/CMakeLists.txt", + "arguments": "", + "cppFlags": "", + } + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + }, + "nativeLib": { + "debugSymbol": { + "strip": true, + "exclude": [] + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/hvigorfile.ts b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..6f728dcca5aa1d7f7875caebfc8e9f2e4f4c79ad --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. +*/ + +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/obfuscation-rules.txt b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..69c4d6a8a5531548e4886fa766090c5c157a87d9 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/oh-package.json5 b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..118bdd4fe7699368a010e04c24f5bfc887cf1298 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/oh-package.json5 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "libentry.so": "file:./src/main/cpp/types/libentry" + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/CMakeLists.txt b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c49ee702590efceb71e788d6c33066a0eb44d163 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/CMakeLists.txt @@ -0,0 +1,15 @@ +# the minimum version of CMake. +cmake_minimum_required(VERSION 3.5.0) +project(VPNControl_Case) + +set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +if(DEFINED PACKAGE_FIND_FILE) + include(${PACKAGE_FIND_FILE}) +endif() + +include_directories(${NATIVERENDER_ROOT_PATH} + ${NATIVERENDER_ROOT_PATH}/include) + +add_library(entry SHARED napi_init.cpp) +target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so) diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/napi_init.cpp b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/napi_init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..23df034ffb07f48141c3fb1850422dfeb7452003 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/napi_init.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2025 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 "hilog/log.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define BUFFER_SIZE 2048 + +#define VPN_LOG_TAG "NetMgrVpn" +#define VPN_LOG_DOMAIN 0x15b0 +#define MAKE_FILE_NAME (strrchr(__FILE__, '/') + 1) + +#define NETMANAGER_VPN_LOGE(fmt, ...) \ + OH_LOG_Print(LOG_APP, LOG_ERROR, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ + __LINE__, ##__VA_ARGS__) + +#define NETMANAGER_VPN_LOGI(fmt, ...) \ + OH_LOG_Print(LOG_APP, LOG_INFO, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ + __LINE__, ##__VA_ARGS__) + +#define NETMANAGER_VPN_LOGD(fmt, ...) \ + OH_LOG_Print(LOG_APP, LOG_DEBUG, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ + __LINE__, ##__VA_ARGS__) + +struct FdInfo { + int32_t tunFd = 0; + int32_t tunnelFd = 0; + struct sockaddr_in serverAddr; +}; + +static FdInfo g_fdInfo; +static bool g_threadRunF = false; +static std::thread g_threadT1; +static std::thread g_threadT2; +// 获取对应字符串数据, 用于获取udp server 的IP地址 +static constexpr const int MAX_STRING_LENGTH = 1024; + +std::string GetStringFromValueUtf8(napi_env env, napi_value value) +{ + std::string result; + char str[MAX_STRING_LENGTH] = {0}; + size_t length = 0; + napi_get_value_string_utf8(env, value, str, MAX_STRING_LENGTH, &length); + if (length > 0) { + return result.append(str, length); + } + return result; +} + +void HandleReadTunfd(FdInfo fdInfo) +{ + uint8_t buffer[BUFFER_SIZE] = {0}; + while (g_threadRunF) { + int readResult = read(fdInfo.tunFd, buffer, sizeof(buffer)); + if (readResult <= 0) { + if (errno != EAGAIN) { + NETMANAGER_VPN_LOGE("read tun device error: %{public}d, tunfd: %{public}d", errno, fdInfo.tunFd); + } + continue; + } + + // 读取到虚拟网卡的数据,通过udp隧道,发送给服务器 + NETMANAGER_VPN_LOGD("buffer: %{public}s, len: %{public}d", buffer, readResult); + readResult = sendto(fdInfo.tunnelFd, buffer, readResult, 0, + reinterpret_cast(&fdInfo.serverAddr), sizeof(fdInfo.serverAddr)); + if (readResult <= 0) { + NETMANAGER_VPN_LOGE("send to server[%{public}s:%{public}d] failed, ret: %{public}d, error: %{public}s", + inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), readResult, + strerror(errno)); + continue; + } + } +} + +void HandleTcpReceived(FdInfo fdInfo) +{ + int addrlen = sizeof(struct sockaddr_in); + uint8_t buffer[BUFFER_SIZE] = {0}; + while (g_threadRunF) { + int length = recvfrom(fdInfo.tunnelFd, buffer, sizeof(buffer), + 0, reinterpret_cast(&fdInfo.serverAddr), reinterpret_cast(&addrlen)); + if (length < 0) { + if (errno != EAGAIN) { + NETMANAGER_VPN_LOGE("read tun device error: %{public}d,tunnelfd: %{public}d", errno, fdInfo.tunnelFd); + } + continue; + } + + // 接收到udp server的数据,写入到虚拟网卡中 + NETMANAGER_VPN_LOGD("from [%{public}s:%{public}d] data: %{public}s, len: %{public}d", + inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), buffer, length); + int ret = write(fdInfo.tunFd, buffer, length); + if (ret <= 0) { + NETMANAGER_VPN_LOGE("error Write To Tunfd, errno: %{public}d", errno); + } + } +} + +static napi_value UdpConnect(napi_env env, napi_callback_info info) +{ + size_t argc = 2; + napi_value args[2] = { nullptr }; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + int32_t port = 0; + napi_get_value_int32(env, args[1], &port); + std::string ipAddr = GetStringFromValueUtf8(env, args[0]); + + NETMANAGER_VPN_LOGI("ip: %{public}s port: %{public}d", ipAddr.c_str(), port); + + // 建立udp隧道 + int32_t sockFd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockFd == -1) { + NETMANAGER_VPN_LOGE("socket() error"); + return 0; + } + + struct timeval timeout = {1, 0}; + setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&timeout), sizeof(struct timeval)); + + memset(&g_fdInfo.serverAddr, 0, sizeof(g_fdInfo.serverAddr)); + g_fdInfo.serverAddr.sin_family = AF_INET; + g_fdInfo.serverAddr.sin_addr.s_addr = inet_addr(ipAddr.c_str()); // server's IP addr + g_fdInfo.serverAddr.sin_port = htons(port); // port + + NETMANAGER_VPN_LOGI("Connection successful"); + + napi_value tunnelFd; + napi_create_int32(env, sockFd, &tunnelFd); + return tunnelFd; +} + +static napi_value StartVpn(napi_env env, napi_callback_info info) +{ + size_t argc = 2; + napi_value args[2] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + napi_get_value_int32(env, args[0], &g_fdInfo.tunFd); + napi_get_value_int32(env, args[1], &g_fdInfo.tunnelFd); + + if (g_threadRunF) { + g_threadRunF = false; + g_threadT1.join(); + g_threadT2.join(); + } + + // 启动两个线程, 一个处理读取虚拟网卡的数据,另一个接收服务端的数据 + g_threadRunF = true; + std::thread tt1(HandleReadTunfd, g_fdInfo); + std::thread tt2(HandleTcpReceived, g_fdInfo); + + g_threadT1 = std::move(tt1); + g_threadT2 = std::move(tt2); + + NETMANAGER_VPN_LOGI("StartVpn successful"); + + napi_value retValue; + napi_create_int32(env, 0, &retValue); + return retValue; +} + +static napi_value StopVpn(napi_env env, napi_callback_info info) +{ + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + int32_t tunnelFd; + napi_get_value_int32(env, args[0], &tunnelFd); + if (tunnelFd) { + close(tunnelFd); + tunnelFd = 0; + } + + // 停止两个线程 + if (g_threadRunF) { + g_threadRunF = false; + g_threadT1.join(); + g_threadT2.join(); + } + + NETMANAGER_VPN_LOGI("StopVpn successful"); + + napi_value retValue; + napi_create_int32(env, 0, &retValue); + return retValue; +} + +EXTERN_C_START +static napi_value Init(napi_env env, napi_value exports) +{ + napi_property_descriptor desc[] = { + {"udpConnect", nullptr, UdpConnect, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"startVpn", nullptr, StartVpn, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"stopVpn", nullptr, StopVpn, nullptr, nullptr, nullptr, napi_default, nullptr}, + }; + napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); + return exports; +} +EXTERN_C_END + +static napi_module demoModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "entry", + .nm_priv = ((void *)0), + .reserved = {0}, +}; + +extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); } + \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/types/libentry/Index.d.ts b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/types/libentry/Index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..1dd3ce49dbadc6b54759351c027e5d4c0ad08dc5 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/types/libentry/Index.d.ts @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025 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. + */ + +export const udpConnect:(addr:string,port:number)=>number; +export const startVpn:(tunFd:number,tunnelFd:number)=>number; +export const stopVpn:(tunnelFd:number)=>number; \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/types/libentry/oh-package.json5 b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/types/libentry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..9f702df5b203aecb2f7f908426cf3f56d7ace917 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/types/libentry/oh-package.json5 @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. +*/ + +{ + "name": "libentry.so", + "types": "./Index.d.ts", + "version": "1.0.0", + "description": "Please describe the basic information." +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/CommonConstant.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/CommonConstant.ets new file mode 100644 index 0000000000000000000000000000000000000000..e9198cbd915c1bf72bc267348b9729d6f406e7e7 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/CommonConstant.ets @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +export enum ComponentId { + START_VPN_BUTTON = 'START_VPN_BUTTON', + STOP_VPN_BUTTON = 'STOP_VPN_BUTTON', + CREATE_TUNNEL_BUTTON = 'CREATE_TUNNEL_BUTTON', + PROTECT_BUTTON = 'PROTECT_BUTTON', + SETUP_VPN_BUTTON = 'SETUP_VPN_BUTTON', + START_VPN_EXT_BUTTON = 'START_VPN_EXT_BUTTON', + INNER_STOP_VPN_BUTTON = 'INNER_STOP_VPN_BUTTON', + STOP_VPN_EXT_BUTTON = 'STOP_VPN_EXT_BUTTON' +}; \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/Logger.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..db6fe6a1861744fd373d90575d2547f8f70170fb --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/Logger.ets @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 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. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; + +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0x0001; // 此处填写应用的domain值 + } + + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } + + fatal(...args: string[]) { + hilog.fatal(this.domain, this.prefix, this.format, args); + } + + isLoggable(level: number) { + hilog.isLoggable(this.domain, this.prefix, level); + } +} + +export default new Logger('[Sample_VPNControl_CASE]'); diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/ShowToast.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/ShowToast.ets new file mode 100644 index 0000000000000000000000000000000000000000..134c5e38a7210055366f3e57348390928d9fb54a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/ShowToast.ets @@ -0,0 +1,23 @@ +/* +* Copyright (c) 2025 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. +*/ + +import promptAction from '@ohos.promptAction'; + +export function showToast(msg: string, dur: number) { + promptAction.showToast({ + message: msg, + duration: dur + }); +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/component.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/component.ets new file mode 100644 index 0000000000000000000000000000000000000000..69073380445a455a9c5a9d14980c94f0a9723d51 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/common/component.ets @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 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. + */ + +import router from '@ohos.router'; + +// 常量提取 +const BACK_BUTTON_SIZE = 12; // 返回按钮宽高 +const TITLE_BAR_HEIGHT = 56; // 标题栏高度 +const BACK_BUTTON_MARGIN_LEFT = 24; // 返回按钮左边距 +const TITLE_MARGIN_LEFT = 24; // 标题文字左边距 +const TITLE_FONT_SIZE = 20; // 标题字体大小 +const ASPECT_RATIO = 1; // 返回按钮宽高比 + +@Component +export default struct TitleBar { + public title: string | Resource = $r('app.string.VPN_Case'); + public hasBackPress: boolean = false; + + build() { + Row() { + if (this.hasBackPress) { + Row() { + Image($r('app.media.back')) + .id('btnBack') + .width(BACK_BUTTON_SIZE) + .height(BACK_BUTTON_SIZE) + } + .height('100%') + .aspectRatio(ASPECT_RATIO) + .margin({ left: BACK_BUTTON_MARGIN_LEFT }) + .onClick(() => { + router.back(); + }) + } + Text(this.title) + .fontSize(TITLE_FONT_SIZE) + .fontColor(Color.Black) + .margin(this.hasBackPress ? {} : { left: TITLE_MARGIN_LEFT }) + Blank() + } + .width('100%') + .height(TITLE_BAR_HEIGHT) + .backgroundColor(Color.Transparent) + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/entryability/EntryAbility.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..3a652790788fff60d3002b6a3931eefa67a6cc60 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 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. +*/ + +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +}; diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..0ae63603b4b2c1a3a1dcf6c2d03990fbe6e52c92 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. +*/ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationContentUtil.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationContentUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..337a9566ff7b3b5a28cd911c53e1be9e66feffd4 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationContentUtil.ets @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 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. + */ + +import notification from '@ohos.notificationManager'; + +class NotificationContentUtil { + /** + * init basic notification content + * @param basicContent + * @return return the created NotificationContent + */ + initBasicNotificationContent( + basicContent: notification.NotificationBasicContent + ): notification.NotificationContent { + return { + notificationContentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, // 通知内容类型 + normal: basicContent // 基本类型通知内容 + }; + } +} + +export let notificationContentUtil = new NotificationContentUtil(); \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationManagementUtil.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationManagementUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..df1f28de79810c193ac0445fef30c4ae380896b2 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationManagementUtil.ets @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2025 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. + */ + +import { notificationManager } from '@kit.NotificationKit'; +import Logger from '../common/Logger'; + +const NOTIFICATION_TYPE_SIZE = 5; + +export interface getAllNotificationsResultType { + groupNotifications: Array>; + countsByType: Array; +} + +// Notification management +class NotificationManagementUtil { + public typeNotifications: Array> = + new Array(NOTIFICATION_TYPE_SIZE + 1); + public countsByType: Array = new Array(NOTIFICATION_TYPE_SIZE + 1); + public badgeNum: number = 0; + + constructor() { + this.countsByType.fill(0); + for (let i = 0; i < NOTIFICATION_TYPE_SIZE + 1; i++) { + this.typeNotifications[i] = new Array(); + } + // Get all notifications for the current application. + notificationManager.getActiveNotifications().then((notifications) => { + for (let i = 0; i < notifications.length; i++) { + let typeId = notifications[i].content.notificationContentType; + this.countsByType[typeId as number] += 1; + this.typeNotifications[typeId as number].push(notifications[i]); + } + Logger.info(`getAllActiveNotifications success, data: ${JSON.stringify(notifications)}`); + // Calculate the number of badge. + this.countsByType.forEach((num: number) => { + this.badgeNum += num; + }) + }) + } + + // Cancel notifications of this type. + cancelNotificationType(typeId: number) { + this.typeNotifications[typeId].forEach(item => { + notificationManager.cancel(item.id); + }) + } + + // Display badge + async setBadgeNumber(num: number) { + await notificationManager.setBadgeNumber(num).then(() => { + this.badgeNum = num; + Logger.info('displayBadge success'); + }); + } + + getBadgeNumber(): number { + return this.badgeNum; + } + + // Add a notification. + async addNotification(notification: notificationManager.NotificationRequest) { + const typeId: notificationManager.ContentType = + notification.content.notificationContentType as notificationManager.ContentType; + this.typeNotifications[typeId].push(notification); + this.countsByType[typeId] += 1; + this.badgeNum += 1; + await notificationManagement.setBadgeNumber(this.badgeNum); + Logger.info('add Message success'); + } + + // Get all current notifications and quantities. + async getAllNotifications() { + let result: getAllNotificationsResultType = { + groupNotifications: this.typeNotifications, + countsByType: this.countsByType + } + return result; + } +} + +export let notificationManagement = new NotificationManagementUtil(); \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationOperations.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationOperations.ets new file mode 100644 index 0000000000000000000000000000000000000000..696639326cc50e9c896104d9beef7267864893b3 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationOperations.ets @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025 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. + */ + +import notification from '@ohos.notificationManager'; +import Logger from '../common/Logger'; +import { notificationContentUtil } from './NotificationContentUtil'; +import { notificationRequestUtil } from './NotificationRequestUtil'; +import { notificationUtil } from './NotificationUtil'; +import { wantAgentUtil } from './WantAgentUtil'; + +const BUNDLE_NAME: string = 'com.samples.vpncontrol_case'; +const ABILITY_NAME: string = 'EntryAbility'; + +export default class NotificationOperations { + private context: Context | undefined = undefined; + private basicContent: notification.NotificationBasicContent | undefined = undefined; + + // 在初始化函数初始化基本通知类型的参数 + constructor(context: Context) { + this.context = context; + let notificationTitle = ''; + let notificationText = this.context.resourceManager.getStringSync($r('app.string.notification_content')); + let notificationAdditional = this.context.resourceManager.getStringSync($r('app.string.notification_additional')); + this.basicContent = { + title: notificationTitle, + text: notificationText, + additionalText: notificationAdditional + }; + } + + // 发布点击跳转到应用的通知 + publishNotificationWithWantAgent = async () => { + try { + if (this.context !== undefined && this.context !== null && this.basicContent !== undefined && + this.basicContent !== null) { + this.basicContent.title = this.context.resourceManager.getStringSync($r('app.string.vpn_clickable_notification')); + this.basicContent.text = this.context.resourceManager.getStringSync($r('app.string.vpn_clickable_information')); + let notificationWantAgent = await wantAgentUtil.createWantAgentForStartAbility(BUNDLE_NAME, ABILITY_NAME); + let notificationContent = notificationContentUtil.initBasicNotificationContent(this.basicContent); + let notificationRequest = + notificationRequestUtil.initWantAgentNotificationRequest(notificationContent, notificationWantAgent); + notificationUtil.publishNotification(notificationRequest); + } + } catch (error) { + Logger.info(`publishNotificationWithWantAgent error, error = ${JSON.stringify(error)}`); + } + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationRequestUtil.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationRequestUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..c396caf6d67a34e136ae992a34cdf798fdf36763 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationRequestUtil.ets @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025 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. + */ + +import notification from '@ohos.notificationManager'; +import { WantAgent } from '@ohos.wantAgent'; + +class NotificationRequestUtil { + /** + * init basic NotificationRequest + * @param notificationContent + * @return return the created NotificationRequest + */ + initBasicNotificationRequest( + notificationContent: notification.NotificationContent + ): notification.NotificationRequest { + return { + notificationSlotType: notification.SlotType.CONTENT_INFORMATION, + id: 1, // 通知id,默认为1 + content: notificationContent + }; + } + + /** + * init wantAgent NotificationRequest + * @param notificationContent + * @param notificationWantAgent + * @return return the created NotificationRequest + */ + + initWantAgentNotificationRequest( + notificationContent: notification.NotificationContent, notificationWantAgent: WantAgent + ): notification.NotificationRequest { + return { + notificationSlotType: notification.SlotType.CONTENT_INFORMATION, + id: 1, // 通知id,默认为1 + content: notificationContent, + wantAgent: notificationWantAgent + }; + } +} + +export let notificationRequestUtil = new NotificationRequestUtil(); \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationUtil.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..b3dac2e64ae410cf74d5b209382da83ebff6082e --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/NotificationUtil.ets @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025 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. + */ + +import { notificationManager } from '@kit.NotificationKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { notificationManagement } from '../notification/NotificationManagementUtil'; +import Logger from '../common/Logger'; + +const TAG: string = 'NotificationUtilModel'; + +class NotificationUtil { + /** + * enable notification + */ + private id: number = 0; + + async enableNotification() { + try { + notificationManager.requestEnableNotification(AppStorage.get('context'), (err: BusinessError): void => { + if (err) { + Logger.error(TAG, `requestEnableNotification failed, code is ${err.code}, message is ${err.message}`); + } else { + Logger.info(TAG, 'requestEnableNotification success'); + } + }); + Logger.info(TAG, `enableNotification success`); + } catch (err) { + Logger.info(TAG, `enableNotification err ${JSON.stringify(err)}`); + } + } + + /** + * + * @param notificationRequest + * @param id, Support specifying notification id when publishing notifications + */ + async publishNotification(notificationRequest: notificationManager.NotificationRequest, group?: string) { + notificationRequest.id = this.id; + this.id++; + if (group) { + notificationRequest.groupName = group; + } + + try { + await notificationManager.publish(notificationRequest); + // Notify manager add new notification. + await notificationManagement.addNotification(notificationRequest); + } catch (err) { + if (err) { + Logger.info(TAG, `publishNotification err ${JSON.stringify(err)}`); + } + } + } + + /** + * cancel notification by id + */ + async cancelNotificationById(id: number) { + try { + await notificationManager.cancel(id); + Logger.info(TAG, `cancel success`); + } catch (err) { + if (err) { + Logger.info(TAG, `cancel err ${JSON.stringify(err)}`); + } + } + } + + /** + * cancel all notification + */ + async cancelAllNotifications() { + try { + await notificationManager.cancelAll(); + Logger.info(TAG, `cancel all success`); + } catch (err) { + if (err) { + Logger.info(TAG, `cancel all err ${JSON.stringify(err)}`); + } + } + } +} + +export let notificationUtil = new NotificationUtil(); \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/WantAgentUtil.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/WantAgentUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..8beaa1915145326d60d5a532ccd430b88807a07b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/notification/WantAgentUtil.ets @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 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. + */ + +import wantAgent from '@ohos.app.ability.wantAgent'; + +const REQUEST_CODE: number = 0; // WantAgentInfo的请求码,默认定义成0 +class WantAgentUtil { + /** + * create wantAgent for start ability + * + * @param bundleName + * @param abilityName + * @return return the created WantAgent object. + */ + async createWantAgentForStartAbility(bundleName: string, abilityName: string) { + let wantAgentInfo: wantAgent.WantAgentInfo = { + wants: [ + { + bundleName: bundleName, + abilityName: abilityName + } + ], + actionType: wantAgent.OperationType.START_ABILITY, + requestCode: REQUEST_CODE // requestCode是WantAgentInfo的请求码,是使用者定义的一个私有值 + } + return await wantAgent.getWantAgent(wantAgentInfo); + } + + /** + * create wantAgent for common event + * + * @param mAction + * @return return the created WantAgent object. + */ + async createWantAgentForCommonEvent(action: string) { + let wantAgentInfo: wantAgent.WantAgentInfo = { + wants: [{ action: action }], + actionType: wantAgent.OperationType.SEND_COMMON_EVENT, + requestCode: REQUEST_CODE // requestCode是WantAgentInfo的请求码,是使用者定义的一个私有值 + } + return await wantAgent.getWantAgent(wantAgentInfo); + } +} + +export let wantAgentUtil = new WantAgentUtil(); \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/Index.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..aee7d5d54eeb08d0cdc0d2d1992d202be1390f68 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2025 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. + */ + +import router from '@ohos.router'; +import { BusinessError } from '@ohos.base'; +import { ComponentId } from '../common/CommonConstant'; +import Logger from '../common/Logger'; +import { notificationUtil } from '../notification/NotificationUtil'; + +// 常量定义 +const TITLE_FONT_SIZE = 35; // 标题字体大小 +const BUTTON_FONT_SIZE = 25; // 按钮字体大小 +const BUTTON_MARGIN = 16; // 按钮边距 + +@Entry +@Component +struct Index { + @State message: string = 'VPN'; + @State flag: string = 'wait'; + + + aboutToAppear(): void { + notificationUtil.enableNotification(); + } + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(TITLE_FONT_SIZE) + .fontWeight(FontWeight.Bold) + .id(this.flag) + .onClick(() => { + Logger.info('developTag', '%{public}s', 'vpn Client'); + }) + + // Start VPN 按钮 + Button($r('app.string.Start_VPN')) + .onClick(() => { + Logger.info('developTag', '%{public}s', 'Succeeded in clicking the button.'); + router.pushUrl({ url: 'pages/StartVpn' }).then(() => { + Logger.info('developTag', '%{public}s', 'Succeeded in jumping to the second page.'); + this.flag = 'Pass'; + }).catch((err: BusinessError) => { + Logger.error('developTag', 'Failed to jump to the second page: %{public}s', + JSON.stringify(err) ?? ''); + this.flag = 'Error'; + }); + }) + .id(ComponentId.START_VPN_BUTTON) + .width('70%') + .fontSize(BUTTON_FONT_SIZE) + .margin(BUTTON_MARGIN) + + // Stop VPN 按钮 + Button($r('app.string.Stop_VPN')) + .onClick(() => { + Logger.info('developTag', '%{public}s', 'Succeeded in clicking the button.'); + router.pushUrl({ url: 'pages/StopVpn' }).then(() => { + Logger.info('developTag', '%{public}s', 'Succeeded in jumping to the second page.'); + this.flag = 'Pass'; + }).catch((err: BusinessError) => { + Logger.error('developTag', 'Failed to jump to the second page: %{public}s', + JSON.stringify(err) ?? ''); + this.flag = 'Error'; + }); + }) + .id(ComponentId.STOP_VPN_BUTTON) + .width('70%') + .fontSize(BUTTON_FONT_SIZE) + }.width('100%') + }.height('100%') + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/StartVpn.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/StartVpn.ets new file mode 100644 index 0000000000000000000000000000000000000000..75ac05e8d691aefcb7c7f4e1cb960a962e4b244a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/StartVpn.ets @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2025 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. +*/ + +import vpn_client from 'libentry.so'; +import common from '@ohos.app.ability.common'; +import vpnext from '@ohos.net.vpnExtension'; +import Want from '@ohos.app.ability.Want'; +import { showToast } from '../common/ShowToast'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { ComponentId } from '../common/CommonConstant'; // 引入枚举 +import TitleBar from '../common/component'; +import Logger from '../common/Logger'; +import NotificationOperations from '../notification/NotificationOperations'; + +// 提取常量 +const TITLE_FONT_SIZE = 35; // 标题字体大小 +const LABEL_FONT_SIZE = 20; // 标签字体大小 +const BUTTON_FONT_SIZE = 25; // 按钮字体大小 +const INPUT_MARGIN = 2; // 输入框边距 +const BUTTON_SUCCESS_MESSAGE = 4000; // Toast 显示时间 +const BUTTON_MARGIN = 16; + +let want: Want = { + deviceId: '', + bundleName: 'com.samples.vpncontrol_case', + abilityName: 'MyVpnExtAbility', +}; + +let g_tunFd = -1; +let g_tunnelFd = -1; + +@Entry +@Component +struct StartVpn { + @State message: string = 'VPN'; + @State vpnServerIp: string = '192.168.xxx.xxx'; // 示例服务端地址,若有vpn服务器可填写实际服务器ip + @State tunIp: string = '10.0.0.5'; // 示例地址,代表客户端设备在虚拟网络中的地址 + @State prefix: string = '24'; + @State blockedAppName: string = 'com.example.baidumyapplication'; // 示例 + @State flag: string = 'wait'; + private context = getContext(this) as common.VpnExtensionContext; + private vpnConnection: vpnext.VpnConnection = vpnext.createVpnConnection(this.context); + + private notificationOperations: NotificationOperations = new NotificationOperations(getContext(this)); + + CreateTunnel() { + g_tunnelFd = vpn_client.udpConnect(this.vpnServerIp, 8888); + if (g_tunnelFd) { + showToast('CreateTunnel Success', BUTTON_SUCCESS_MESSAGE); + Logger.info('developTag', '%{public}s', 'CreateTunnel Success'); + this.flag = 'Pass'; + } else { + showToast('CreateTunnel Fail', BUTTON_SUCCESS_MESSAGE); + Logger.error('developTag', 'CreateTunnel Fail %{public}s', 'CreateTunnel Fail'); + this.flag = 'Error'; + } + } + + Protect() { + Logger.info('developTag', '%{public}s', 'vpn Protect'); + this.vpnConnection.protect(g_tunnelFd).then(() => { + showToast('vpn Protect Success', BUTTON_SUCCESS_MESSAGE); + Logger.info('developTag', '%{public}s', 'vpn Protect Success'); + this.flag = 'Pass'; + }).catch((err: Error) => { + showToast('vpn Protect Failed: ' + JSON.stringify(err), BUTTON_SUCCESS_MESSAGE); + Logger.error('developTag', 'vpn Protect Failed %{public}s', JSON.stringify(err) ?? ''); + this.flag = 'Error'; + }); + } + + SetupVpn() { + Logger.info('developTag', '%{public}s', 'vpn SetupVpn'); + + // 定义 Address 类,表示 IP 地址和地址族 + class Address { + address: string; // 存储 IP 地址 + family: number; // 地址族,1 表示 IPv4,2 表示 IPv6 + constructor(address: string, family: number) { + this.address = address; + this.family = family; + } + } + + // 定义 AddressWithPrefix 类,表示地址和网络前缀长度 + // 例如,"10.0.0.5" 地址与 "24" 前缀结合,表示 10.0.0.5/24 的网络 + class AddressWithPrefix { + address: Address; // 地址对象 + prefixLength: number; // 网络前缀长度(例如,24 表示子网掩码为 255.255.255.0) + constructor(address: Address, prefixLength: number) { + this.address = address; + this.prefixLength = prefixLength; + } + } + + // 定义目标地址的接口类型 + interface DestinationItem { + address: Address; // 目标地址 + prefixLength: number; // 目标地址的前缀长度(子网掩码) + port: number; // 目标端口 + } + + // 定义网关的接口类型 + interface GatewayItem { + address: string; // 网关地址 + family: number; // 网关地址族,1 表示 IPv4,2 表示 IPv6 + port: number; // 网关端口 + } + + // 定义路由配置的接口类型 + interface RouteConfig { + interface: string; // VPN 虚拟接口名称(如 vpn-tun) + destination: DestinationItem; // 目标地址配置 + gateway: GatewayItem; // 网关配置 + hasGateway: boolean; // 是否存在网关 + isDefaultRoute: boolean; // 是否为默认路由 + } + + // 配置 VPN 设置的类 + class Config { + addresses: AddressWithPrefix[]; // VPN 虚拟网卡的 IP 地址 + mtu: number; // 最大传输单元,决定每个数据包的最大大小 + dnsAddresses: string[]; // DNS 服务器的 IP 地址 + trustedApplications: string[]; // 受信任的应用程序(可通过 VPN 使用) + blockedApplications: string[]; // 不允许通过 VPN 使用的应用程序 + routes: RouteConfig[]; // 路由配置 + + // 构造函数,初始化 VPN 配置 + constructor(tunIp: string, blockedAppName: string) { + // 设置虚拟网卡的 IP 地址,这里使用一个 IPv4 地址和前缀长度 24(即 255.255.255.0) + this.addresses = [ + new AddressWithPrefix(new Address(tunIp, 1), 24) // 这里的 `1` 是 IPv4 的地址族 + ]; + this.mtu = 1400; // 设置最大传输单元(MTU),通常用于 VPN 隧道数据包的大小限制 + this.dnsAddresses = ['114.114.114.114']; // 设置 DNS 服务器地址 + this.trustedApplications = []; // 初始化为空数组,受信任的应用程序 + this.blockedApplications = [blockedAppName]; // 被阻止的应用是传入的 `blockedAppName` + + // 配置路由:这里配置了一个默认路由和目标地址的路由 + this.routes = [ + { + interface: 'vpn-tun', // 虚拟接口名称(通过 VPN 隧道通信的接口) + destination: { + address: new Address('10.0.0.8', 1), // 示例目标地址 + prefixLength: 24, // 目标网络的子网掩码 + port: 8080 // 目标端口 + }, + gateway: { + address: '10.0.0.5', // 示例网关地址 + family: 1, // 地址族(IPv4) + port: 8080 // 网关端口 + }, + hasGateway: true, // 该路由有网关 + isDefaultRoute: true // 该路由是默认路由 + } + ]; + } + } + + // 使用上述配置创建 VPN 配置对象 + let config = new Config(this.tunIp, this.blockedAppName); + + // 创建 VPN 连接并应用配置 + this.vpnConnection.create(config).then((data) => { + g_tunFd = data; // 获取到的虚拟网络接口文件描述符 + Logger.info('developTag', 'tunfd: %{public}s', JSON.stringify(data) ?? ''); + + // 启动 VPN 连接,传入隧道文件描述符和虚拟网络接口文件描述符 + vpn_client.startVpn(g_tunFd, g_tunnelFd); + showToast('VPN started successfully', BUTTON_SUCCESS_MESSAGE); + Logger.info('developTag', '%{public}s', 'vpn start Success'); + this.flag = 'Pass'; // 设置状态为成功 + + this.notificationOperations.publishNotificationWithWantAgent(); + }).catch((err: BusinessError) => { + // 如果 VPN 创建失败,捕获异常并处理 + showToast('VPN start failed: ' + JSON.stringify(err), BUTTON_SUCCESS_MESSAGE); + Logger.error('developTag', 'vpn start Fail %{public}s', JSON.stringify(err) ?? ''); + this.flag = 'Error'; // 设置状态为失败 + }); + } + + + build() { + Row() { + Column() { + TitleBar({ hasBackPress: true, title: 'BACK' }) + Text(this.message) + .fontSize(TITLE_FONT_SIZE) + .fontWeight(FontWeight.Bold) + .id(this.flag) + .onClick(() => { + Logger.info('developTag', '%{public}s', 'vpn Client'); + }) + + Row() { + Text('server ip') + .fontSize(LABEL_FONT_SIZE) + .width('40%') + .textAlign(TextAlign.Center) + .backgroundColor(0xAFEEEE); + TextInput({ text: '192.168.xxx.xxx' }) + .onChange((value) => { + this.vpnServerIp = value; + }) + .width('60%') + .margin(INPUT_MARGIN) + }.width('90%'); + + Row() { + Text('tun ip') + .fontSize(LABEL_FONT_SIZE) + .width('40%') + .textAlign(TextAlign.Center) + .backgroundColor(0xAFEEEE); + TextInput({ text: '10.0.0.5' }) + .onChange((value) => { + this.tunIp = value; + }) + .width('60%') + .margin(INPUT_MARGIN) + }.width('90%') + + Row() { + Text('blockedApp') + .fontSize(LABEL_FONT_SIZE) + .width('40%') + .textAlign(TextAlign.Center) + .backgroundColor(0xAFEEEE) + TextInput({ text: 'com.example.baidumyapplication' }) + .onChange((value) => { + this.blockedAppName = value; + }) + .width('60%') + .margin(INPUT_MARGIN) + }.width('90%') + + Button($r('app.string.CreateTunnel')) + .onClick(() => { + this.CreateTunnel(); + }) + .width('70%') + .id(ComponentId.CREATE_TUNNEL_BUTTON) + .fontSize(BUTTON_FONT_SIZE) + .margin(BUTTON_MARGIN); + Button($r('app.string.Protect')) + .onClick(() => { + this.Protect(); + }) + .width('70%') + .id(ComponentId.PROTECT_BUTTON) + .fontSize(BUTTON_FONT_SIZE) + .margin(BUTTON_MARGIN); + // [Start start_vpn_extension_ability] + Button($r('app.string.start_vpnExt')) + .onClick(() => { + try { + vpnext.startVpnExtensionAbility(want).then(() => { + showToast('start vpnExt Success', BUTTON_SUCCESS_MESSAGE); + Logger.info('developTag', '%{public}s', 'start vpnExt Success'); + this.flag = 'Pass'; + }) + } catch (err) { + showToast('start vpnExt Fail: ' + JSON.stringify(err), BUTTON_SUCCESS_MESSAGE); + Logger.error('developTag', 'start vpnExt Fail %{public}s', JSON.stringify(err) ?? ''); + this.flag = 'Error'; + } + }) + .width('70%') + .id(ComponentId.START_VPN_EXT_BUTTON) + .fontSize(BUTTON_FONT_SIZE) + .margin(BUTTON_MARGIN) + // [End start_vpn_extension_ability] + Button($r('app.string.SetupVpn')) + .onClick(() => { + this.SetupVpn(); + }) + .width('70%') + .id(ComponentId.SETUP_VPN_BUTTON) + .fontSize(BUTTON_FONT_SIZE) + .margin(BUTTON_MARGIN); + }.width('100%'); + }.height('100%'); + + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/StopVpn.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/StopVpn.ets new file mode 100644 index 0000000000000000000000000000000000000000..779f85e0ad66d907043a5e9ec772f07a7d62b6f9 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/pages/StopVpn.ets @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2025 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. + */ + +import vpnext from '@ohos.net.vpnExtension'; +import Want from '@ohos.app.ability.Want'; +import common from '@ohos.app.ability.common'; +import vpn_client from 'libentry.so'; +import { showToast } from '../common/ShowToast'; +import { ComponentId } from '../common/CommonConstant'; +import TitleBar from '../common/component'; +import Logger from '../common/Logger'; + +// 常量提取 +const TITLE_FONT_SIZE = 35; // 标题字体大小 +const BUTTON_FONT_SIZE = 25; // 按钮字体大小 +const TOAST_DURATION = 2000; // Toast 显示时间 +const BUTTON_MARGIN = 16; + +let want: Want = { + deviceId: '', + bundleName: 'com.samples.vpncontrol_case', + abilityName: 'MyVpnExtAbility', +}; +let g_tunnelFd = -1; + +@Entry +@Component +struct StopVpn { + @State message: string = 'VPN'; + @State flag: string = 'wait'; + @State blockedAppName: string = 'com.example.baidumyapplication'; + private context = getContext(this) as common.VpnExtensionContext; + private vpnConnection: vpnext.VpnConnection = vpnext.createVpnConnection(this.context); + + Destroy() { + Logger.info('developTag', '%{public}s', 'vpn Destroy'); + vpn_client.stopVpn(g_tunnelFd); + this.vpnConnection.destroy().then(() => { + showToast('vpn Destroy Success', TOAST_DURATION); + Logger.info('developTag', '%{public}s', 'vpn Destroy Success'); + this.flag = 'Pass'; + }).catch((err: Error) => { + showToast('vpn Destroy Failed: ' + JSON.stringify(err), TOAST_DURATION); + Logger.error('developTag', 'vpn Destroy Failed: %{public}s', JSON.stringify(err) ?? ''); + this.flag = 'Error'; + }) + } + + build() { + Row() { + Column() { + TitleBar({ hasBackPress: true, title: 'BACK' }) + Text(this.message) + .fontSize(TITLE_FONT_SIZE) + .fontWeight(FontWeight.Bold) + .onClick(() => { + Logger.info('developTag', '%{public}s', 'vpn Client'); + }) + Button($r('app.string.stop_vpn')) + .onClick(() => { + this.Destroy(); + }) + .width('70%') + .id(ComponentId.INNER_STOP_VPN_BUTTON) + .fontSize(BUTTON_FONT_SIZE) + .margin(BUTTON_MARGIN) + // [Start stop_vpn_extension_ability] + Button($r('app.string.stop_vpnExt')) + .onClick(() => { + try { + vpnext.stopVpnExtensionAbility(want).then(() => { + showToast('stop vpnExt Success', TOAST_DURATION); + Logger.info('developTag', '%{public}s', 'stop vpnExt Success'); + this.flag = 'Pass'; + }); + } catch (err) { + showToast('stop vpnExt Fail: ' + JSON.stringify(err), TOAST_DURATION); + Logger.error('developTag', 'stop vpnExt Fail %{public}s', JSON.stringify(err) ?? ''); + this.flag = 'Error'; + } + }) + .width('70%') + .id(ComponentId.STOP_VPN_EXT_BUTTON) + .fontSize(BUTTON_FONT_SIZE) + .margin(BUTTON_MARGIN) + // [End stop_vpn_extension_ability] + }.width('100%'); + }.height('100%'); + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/vpnability/VPNExtentionAbility.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/vpnability/VPNExtentionAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..35ad1a27ca6d9e3f61e9992b06e4df6b7ba5d8a8 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/ets/vpnability/VPNExtentionAbility.ets @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2025 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. + */ + +import Want from '@ohos.app.ability.Want'; +import vpnExt from '@ohos.net.vpnExtension'; +import VpnExtensionAbility from '@ohos.app.ability.VpnExtensionAbility'; +import vpn_client from 'libentry.so'; +import Logger from '../common/Logger'; + +const tag: string = '[MyVpnExtAbility]'; +let gTunFd = -1; +let gTunnelFd = -1; + +export default class MyVpnExtAbility extends VpnExtensionAbility { + private vpnConnection: vpnExt.VpnConnection = vpnExt.createVpnConnection(this.context); + private vpnServerIp: string = '192.168.xxx.xxx'; + // 统一使用第二段代码中的IP地址,可按需调整 + private tunIp: string = '10.0.0.5'; // 示例地址,代表客户端设备在虚拟网络中的地址 + private blockedAppName: string = 'com.example.testvpn'; + + onCreate(want: Want) { + super.onCreate(want); + Logger.info(tag, 'onCreate called'); + // 确保 context 有效 + if (this.context) { + Logger.info('Context is valid, initializing VpnConnection'); + this.vpnConnection = vpnExt.createVpnConnection(this.context); + } else { + Logger.error('Context is invalid, cannot create VpnConnection'); + } + Logger.info(tag, `xdw onCreate, want: ${want.abilityName}`); + this.vpnConnection = vpnExt.createVpnConnection(this.context); + Logger.info('wmw createVpnConnection success'); + this.CreateTunnel(); + this.Protect(); + Logger.info('xdw step4'); + } + + onRequest(want: Want, startId: number) { + Logger.info(tag, `xdw onRequest, want: ${want.abilityName}`); + } + + onConnect(want: Want) { + Logger.info(tag, `xdw onConnect, want: ${want.abilityName}`); + // 返回ServiceExtImpl对象,客户端获取后便可以与ServiceExtensionAbility进行通信 + let abilityName = want.parameters?.abilityName.toString(); + let bundleName = want.parameters?.bundleName.toString(); + return null; + } + + onDisconnect(want: Want) { + Logger.info(tag, `xdw onDisconnect, want: ${want.abilityName}`); + } + + onDestroy() { + Logger.info(tag, `xdw onDestroy`); + this.Destroy(); + } + +// [Start call_the_ondestroy_method] + Destroy() { + Logger.info('developTag', '%{public}s', 'vpn Destroy'); + // 关闭VPN + this.vpnConnection.destroy().then(() => { + Logger.info('developTag', '%{public}s', 'vpn Destroy Success'); + }).catch((err: Error) => { + Logger.error('developTag', 'vpn Destroy Failed: %{public}s', JSON.stringify(err) ?? ''); + }); + } +// [End call_the_ondestroy_method] + + // 创建隧道的方法 + CreateTunnel() { + Logger.info('xdw step1'); + gTunnelFd = vpn_client.udpConnect(this.vpnServerIp, 1194); + if (gTunnelFd < 0) { + Logger.error(tag, 'Failed to create UDP tunnel'); + return; + } + Logger.info(tag, `Tunnel created, FD: ${gTunnelFd}`); + this.Protect(); + } + + // 保护隧道的方法 + Protect() { + console.info('xdw step2'); + Logger.info('developTag', '%{public}s', 'vpn Protect'); + this.vpnConnection.protect(gTunnelFd).then(() => { + Logger.info('developTag', '%{public}s', 'vpn Protect Success'); + this.SetupVpn(); + }).catch((err: Error) => { + Logger.error('developTag', 'vpn Protect Failed %{public}s', JSON.stringify(err) ?? ''); + }) + } + + // 设置VPN网络配置的方法 + SetupVpn() { + console.info('xdw step3'); + Logger.info('developTag', '%{public}s', 'vpn SetupVpn'); + + class Address { + public address: string; + public family: number; + constructor(address: string, family: number) { + this.address = address; + this.family = family; + } + } + + class AddressWithPrefix { + public address: Address; + public prefixLength: number; + constructor(address: Address, prefixLength: number) { + this.address = address; + this.prefixLength = prefixLength; + } + } + + class Config { + public addresses: AddressWithPrefix[]; + public dnsAddresses: string[]; + public trustedApplications: string[]; + public blockedApplications: string[]; + constructor( + tunIp: string, + blockedAppName: string + ) { + this.addresses = [ + new AddressWithPrefix(new Address(tunIp, 1), 24) + ]; + this.dnsAddresses = ['114.114.114.114']; // 通用DNS地址 + this.trustedApplications = []; + this.blockedApplications = []; + } + } + + let config = new Config(this.tunIp, this.blockedAppName); + + try { + this.vpnConnection.create(config).then((data) => { + gTunFd = data; + Logger.error('developTag', 'tunfd: %{public}s', JSON.stringify(data) ?? ''); + vpn_client.startVpn(gTunFd, gTunnelFd); + }) + } catch (error) { + Logger.error('developTag', 'vpn setUp fail: %{public}s', JSON.stringify(error) ?? ''); + } + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/module.json5 b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c435627e6c355c920f622b8e7dd21030fd523b48 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/module.json5 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2025 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. +*/ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:icon", + "label": "$string:app_name", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + }, + // [Start create_vpn_extension_ability] + { + "name": "MyVpnExtAbility", + "srcEntry": "./ets/vpnability/VPNExtentionAbility.ets", + "type": "vpn" + } + // [End create_vpn_extension_ability] + ], + "requestPermissions": [ + { + "name": "ohos.permission.INTERNET", + "reason": "$string:grant_internet", + "usedScene": { + "abilities": [ + "MainAbility" + ], + "when": "inuse" + } + } + ], + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/element/color.json b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..554bb6263616244ed21cd8db47495066742e32b4 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/element/string.json @@ -0,0 +1,80 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "VPN_Case" + }, + { + "name": "VPNExtensionAbility_desc", + "value": "description" + }, + { + "name": "VPNExtensionAbility_label", + "value": "label" + }, + { + "name": "VPN_Case", + "value": "VPN_Case" + }, + { + "name": "Allow", + "value": "允许" + }, + { + "name": "Certain", + "value": "确定" + }, + { + "name": "notification_content", + "value": "notification_content" + }, + { + "name": "notification_additional", + "value": "notification_additional" + }, + { + "name": "vpn_clickable_notification", + "value": "vpn_clickable_notification" + }, + { + "name": "Start_VPN", + "value": "Startup interface" + }, + { + "name": "Stop_VPN", + "value": "Shutdown interface" + }, + { + "name": "CreateTunnel", + "value": "CreateTunnel" + }, + { + "name": "Protect", + "value": "Protect" + }, + { + "name": "start_vpnExt", + "value": "Start_vpnExt" + }, + { + "name": "SetupVpn", + "value": "SetupVpn" + }, + { + "name": "stop_vpn", + "value": "Shutdown vpn" + }, + { + "name": "stop_vpnExt", + "value": "Shutdown vpn_Ext" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/back.svg b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/back.svg new file mode 100644 index 0000000000000000000000000000000000000000..0614389ee6f66cd188d47ba045aa83b4babee356 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/back.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/background.png b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/background.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/foreground.png b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/foreground.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/icon.png b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/icon.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/layered_image.json b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/startIcon.png b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/media/startIcon.png differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/profile/backup_config.json b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/profile/main_pages.json b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..65b5903f306bc5bdf078467edf3e8916ed626ccc --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,7 @@ +{ + "src": [ + "pages/Index", + "pages/StartVpn", + "pages/StopVpn" + ] +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/profile/vpnextensionability.json b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/profile/vpnextensionability.json new file mode 100644 index 0000000000000000000000000000000000000000..85c3d8af6f10a4bda8e6b90f21ab6b28a8acec5c --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/base/profile/vpnextensionability.json @@ -0,0 +1,8 @@ +{ + "accessibilityCapabilities": [ + "retrieve", + "keyEventObserver", + "gesture", + "touchGuide" + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/en_US/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..9a985dcb0bd4c29b6fbdc7321427e77d0db405e8 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,88 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "VPN_Case" + }, + { + "name": "VPNExtensionAbility_desc", + "value": "description" + }, + { + "name": "VPNExtensionAbility_label", + "value": "label" + }, + { + "name": "VPN_Case", + "value": "VPN_Case" + }, + { + "name": "Certain", + "value": "Certain" + }, + { + "name": "notification_content", + "value": "notification_content" + }, + { + "name": "notification_additional", + "value": "notification_additional" + }, + { + "name": "vpn_clickable_notification", + "value": "vpn_clickable_notification" + }, + { + "name": "vpn_clickable_information", + "value": "vpn_clickable_information" + }, + { + "name": "Allow", + "value": "Allow" + }, + { + "name": "Start_VPN", + "value": "Startup interface" + }, + { + "name": "Stop_VPN", + "value": "Shutdown interface" + }, + { + "name": "CreateTunnel", + "value": "CreateTunnel" + }, + { + "name": "Protect", + "value": "Protect" + }, + { + "name": "start_vpnExt", + "value": "Start vpnExt" + }, + { + "name": "SetupVpn", + "value": "Setup Vpn" + }, + { + "name": "stop_vpn", + "value": "Shutdown Vpn" + }, + { + "name": "stop_vpnExt", + "value": "Shutdown Vpn_Ext" + }, + { + "name": "grant_internet", + "value": "grant_internet" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/zh_CN/element/string.json b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..bc978b9eea87ecd9236d6736763efe454b9c129b --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,88 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "VPN示例" + }, + { + "name": "VPNExtensionAbility_desc", + "value": "description" + }, + { + "name": "VPNExtensionAbility_label", + "value": "label" + }, + { + "name": "VPN_Case", + "value": "VPN示例" + }, + { + "name": "Certain", + "value": "确定" + }, + { + "name": "notification_content", + "value": "通知内容" + }, + { + "name": "notification_additional", + "value": "通知附加内容" + }, + { + "name": "vpn_clickable_notification", + "value": "VPN连接成功" + }, + { + "name": "vpn_clickable_information", + "value": "连接时长: 00:00 网络速度: 50 KB/s" + }, + { + "name": "Allow", + "value": "允许" + }, + { + "name": "Start_VPN", + "value": "启动接口" + }, + { + "name": "Stop_VPN", + "value": "关闭接口" + }, + { + "name": "CreateTunnel", + "value": "创建隧道" + }, + { + "name": "Protect", + "value": "保护隧道" + }, + { + "name": "start_vpnExt", + "value": "启动VPN拓展程序" + }, + { + "name": "SetupVpn", + "value": "启动VPN" + }, + { + "name": "stop_vpn", + "value": "关闭VPN" + }, + { + "name": "stop_vpnExt", + "value": "关闭VPN拓展程序" + }, + { + "name": "grant_internet", + "value": "网络使用权限" + } + ] +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/ohosTest/ets/test/Ability.test.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..47b9c91355c18aa18e6e1b8a30d7387c0a7dd9f0 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2025 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. + */ + +import { describe, expect, it } from '@ohos/hypium'; +import { abilityDelegatorRegistry, Component, Driver, ON } from '@kit.TestKit'; +import { UIAbility, Want } from '@kit.AbilityKit'; +import { ComponentId } from '../../../main/ets/common/CommonConstant'; +import Logger from '../../../main/ets/common/Logger'; + +const BUNDLE = 'VPNCASE_'; +const BUNDLE_NAME = 'com.samples.vpncontrol_case'; +const ABILITY_NAME: string = 'EntryAbility'; +const DELAY_TIME = 2000; + +const delegator = abilityDelegatorRegistry.getAbilityDelegator(); +const abilityDelegator = abilityDelegatorRegistry.getAbilityDelegator(); + +async function getResourceString(resource: Resource): Promise { + let manage = abilityDelegator.getAppContext().resourceManager; + let text = await manage.getStringValue(resource); + return text; +} + +async function goBack() { + let driver: Driver = Driver.create(); + await driver.assertComponentExist(ON.id('btnBack')); + let backButton = await driver.findComponent(ON.id('btnBack')); + await backButton.click(); + await driver.delayMs(DELAY_TIME); +} + +export default function VPNTest() { + describe('VPNTest', () => { + /** + * @tc.number StartVPNAbility_001 + * @tc.name StartVPNAbility_001 + * @tc.desc 启动VPN主界面 + */ + it(BUNDLE + 'StartVPNAbility_001', 0, async (done: Function) => { + Logger.info(`${BUNDLE} StartVPNAbility_001 begin`); + // 启动测试的Ability + const want: Want = { + bundleName: BUNDLE_NAME, + abilityName: ABILITY_NAME, + }; + let driver: Driver = Driver.create(); + await delegator.startAbility(want); + await driver.delayMs(DELAY_TIME); + let str = await getResourceString($r('app.string.Allow')); + await driver.assertComponentExist(ON.text(str)); + let confirmButton: Component = await driver.findComponent(ON.text(str)); + await confirmButton.click(); + await driver.delayMs(DELAY_TIME); + // 验证顶层显示的Ability + const ability: UIAbility = await delegator.getCurrentTopAbility(); + expect(ability.context.abilityInfo.name).assertEqual(ABILITY_NAME); + Logger.info(`${BUNDLE} StartVPNAbility_001 end`); + done(); + }); + + /** + * @tc.number StartVPNButton_001 + * @tc.name StartVPNButton_001 + * @tc.desc 测试 StartVPN 按钮点击事件 + */ + it(BUNDLE + 'StartVPNButton_001', 0, async (done: Function) => { + let driver: Driver = Driver.create(); + Logger.info(`${BUNDLE_NAME} StartVPNButton_001 begin`); + // 验证StartVPN按钮存在并点击 + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id(ComponentId.START_VPN_BUTTON)); + const startVpnButton = await driver.findComponent(ON.id(ComponentId.START_VPN_BUTTON)); + await startVpnButton.click(); + await driver.delayMs(DELAY_TIME); + try { + let errorComponent = await driver.findComponent(ON.id('Error')); + if (errorComponent) { + let errorText = await errorComponent.getText(); + Logger.error(`${BUNDLE_NAME} StartVPNButton_001 failed with error: ${errorText}`); + expect(false).assertTrue(); // 如果有 Error 组件,测试失败 + } + } catch (e) { + Logger.info(`${BUNDLE_NAME} StartVPNButton_001 passed: No error component found.`); + expect(true).assertTrue(); // 如果没有 Error 组件,测试通过 + } + Logger.info(`${BUNDLE_NAME} StartVPNButton_001 end`); + done(); + }); + + /** + * @tc.number CreateTunnelButton_001 + * @tc.name CreateTunnelButton_001 + * @tc.desc 测试 CreateTunnel 按钮点击事件 + */ + it(BUNDLE + 'CreateTunnelButton_001', 0, async (done: Function) => { + let driver: Driver = Driver.create(); + Logger.info(`${BUNDLE_NAME} CreateTunnelButton_001 begin`); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id(ComponentId.CREATE_TUNNEL_BUTTON)); + const createTunnelButton = await driver.findComponent(ON.id(ComponentId.CREATE_TUNNEL_BUTTON)); + await createTunnelButton.click(); + await driver.delayMs(DELAY_TIME); + try { + let errorComponent = await driver.findComponent(ON.id('Error')); + if (errorComponent) { + let errorText = await errorComponent.getText(); + Logger.error(`${BUNDLE_NAME} CreateTunnelButton_001 failed with error: ${errorText}`); + expect(false).assertTrue(); // 如果有 Error 组件,测试失败 + } + } catch (e) { + Logger.info(`${BUNDLE_NAME} CreateTunnelButton_001 passed: No error component found.`); + expect(true).assertTrue(); // 如果没有 Error 组件,测试通过 + } + Logger.info(`${BUNDLE_NAME} CreateTunnelButton_001 end`); + done(); + }); + + /** + * @tc.number ProtectButton_001 + * @tc.name ProtectButton_001 + * @tc.desc 测试 Protect 按钮点击事件 + */ + it(BUNDLE + 'ProtectButton_001', 0, async (done: Function) => { + let driver: Driver = Driver.create(); + Logger.info(`${BUNDLE_NAME} ProtectButton_001 begin`); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id(ComponentId.PROTECT_BUTTON)); + const protectButton = await driver.findComponent(ON.id(ComponentId.PROTECT_BUTTON)); + await protectButton.click(); + await driver.delayMs(DELAY_TIME); + try { + let errorComponent = await driver.findComponent(ON.id('Error')); + if (errorComponent) { + let errorText = await errorComponent.getText(); + Logger.error(`${BUNDLE_NAME} ProtectButton_001 failed with error: ${errorText}`); + expect(false).assertTrue(); // 如果有 Error 组件,测试失败 + } + } catch (e) { + Logger.info(`${BUNDLE_NAME} ProtectButton_001 passed: No error component found.`); + expect(true).assertTrue(); // 如果没有 Error 组件,测试通过 + } + Logger.info(`${BUNDLE_NAME} ProtectButton_001 end`); + done(); + }); + + /** + * @tc.number StartVpnExtButton_001 + * @tc.name StartVpnExtButton_001 + * @tc.desc 测试 StartVpnExt 按钮点击事件 + */ + it(BUNDLE + 'StartVpnExtButton_001', 0, async (done: Function) => { + let driver: Driver = Driver.create(); + Logger.info(`${BUNDLE_NAME} StartVpnExtButton_001 begin`); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id(ComponentId.START_VPN_EXT_BUTTON)); + const startVpnExtButton = await driver.findComponent(ON.id(ComponentId.START_VPN_EXT_BUTTON)); + await startVpnExtButton.click(); + await driver.delayMs(DELAY_TIME); + let str = await getResourceString($r('app.string.Certain')); + await driver.assertComponentExist(ON.text(str)); + let confirmButton: Component = await driver.findComponent(ON.text(str)); + await confirmButton.click(); + await driver.delayMs(DELAY_TIME); + try { + let errorComponent = await driver.findComponent(ON.id('Error')); + if (errorComponent) { + let errorText = await errorComponent.getText(); + Logger.error(`${BUNDLE_NAME} StartVpnExtButton_001 failed with error: ${errorText}`); + expect(false).assertTrue(); // 如果有 Error 组件,测试失败 + } + } catch (e) { + Logger.info(`${BUNDLE_NAME} StartVpnExtButton_001 passed: No error component found.`); + expect(true).assertTrue(); // 如果没有 Error 组件,测试通过 + } + Logger.info(`${BUNDLE_NAME} StartVpnExtButton_001 end`); + done(); + }); + + /** + * @tc.number SetupVpnButton_001 + * @tc.name SetupVpnButton_001 + * @tc.desc 测试 SetupVpn 按钮点击事件 + */ + it(BUNDLE + 'SetupVpnButton_001', 0, async (done: Function) => { + let driver: Driver = Driver.create(); + Logger.info(`${BUNDLE_NAME} SetupVpnButton_001 begin`); + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id(ComponentId.SETUP_VPN_BUTTON)); + const setupVpnButton = await driver.findComponent(ON.id(ComponentId.SETUP_VPN_BUTTON)); + await setupVpnButton.click(); + await driver.delayMs(DELAY_TIME); + await goBack(); + try { + let errorComponent = await driver.findComponent(ON.id('Error')); + if (errorComponent) { + let errorText = await errorComponent.getText(); + Logger.error(`${BUNDLE_NAME} SetupVpnButton_001 failed with error: ${errorText}`); + expect(false).assertTrue(); // 如果有 Error 组件,测试失败 + } + } catch (e) { + Logger.info(`${BUNDLE_NAME} SetupVpnButton_001 passed: No error component found.`); + expect(true).assertTrue(); // 如果没有 Error 组件,测试通过 + } + Logger.info(`${BUNDLE_NAME} SetupVpnButton_001 end`); + done(); + }); + + /** + * @tc.number StopButton_001 + * @tc.name StopButton_001 + * @tc.desc 测试 StopVPN 按钮点击事件 + */ + it(BUNDLE + 'StopButton_001', 0, async (done: Function) => { + let driver: Driver = Driver.create(); + Logger.info(`${BUNDLE_NAME} StopButton_001 begin`); + // 验证StopVPN按钮存在并点击 + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id(ComponentId.STOP_VPN_BUTTON)); + const stopVpnButton = await driver.findComponent(ON.id(ComponentId.STOP_VPN_BUTTON)); + await stopVpnButton.click(); + await driver.delayMs(DELAY_TIME); + try { + let errorComponent = await driver.findComponent(ON.id('Error')); + if (errorComponent) { + let errorText = await errorComponent.getText(); + Logger.error(`${BUNDLE_NAME} VPN_StartPageButtons_001 failed with error: ${errorText}`); + expect(false).assertTrue(); // 如果有 Error 组件,测试失败 + } + } catch (e) { + Logger.info(`${BUNDLE_NAME} VPN_StartPageButtons_001 passed: No error component found.`); + expect(true).assertTrue(); // 如果没有 Error 组件,测试通过 + } + Logger.info(`${BUNDLE_NAME} StopButton_001 end`); + done(); + }); + + /** + * @tc.number InnerStopVpnButton_001 + * @tc.name InnerStopVpnButton_001 + * @tc.desc 测试 StopVPN 按钮点击事件 + */ + it(BUNDLE + 'StopVpnButton_001', 0, async (done: Function) => { + let driver: Driver = Driver.create(); + Logger.info(`${BUNDLE_NAME} StopVpnButton_001 begin`); + // 验证 stopVpn 按钮是否存在并点击 + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id(ComponentId.INNER_STOP_VPN_BUTTON)); + const stopVpnButton = await driver.findComponent(ON.id(ComponentId.INNER_STOP_VPN_BUTTON)); + await stopVpnButton.click(); + await driver.delayMs(DELAY_TIME); + // 检查是否存在 Error 组件 + try { + let errorComponent = await driver.findComponent(ON.id('Error')); + if (errorComponent) { + let errorText = await errorComponent.getText(); + Logger.error(`${BUNDLE_NAME} StopVpnButton_001 failed with error: ${errorText}`); + expect(false).assertTrue(); // 如果有 Error 组件,测试失败 + } + } catch (e) { + Logger.info(`${BUNDLE_NAME} StopVpnButton_001 passed: No error component found.`); + expect(true).assertTrue(); // 如果没有 Error 组件,测试通过 + } + Logger.info(`${BUNDLE_NAME} StopVpnButton_001 end`); + done(); + }); + + /** + * @tc.number StopVpnExtButton_001 + * @tc.name StopVpnExtButton_001 + * @tc.desc 测试 StopVpnExt 按钮点击事件 + */ + it(BUNDLE + 'StopVpnExtButton_001', 0, async (done: Function) => { + let driver: Driver = Driver.create(); + Logger.info(`${BUNDLE_NAME} StopVpnExtButton_001 begin`); + // 验证 stopVpnExt 按钮是否存在并点击 + await driver.delayMs(DELAY_TIME); + await driver.assertComponentExist(ON.id(ComponentId.STOP_VPN_EXT_BUTTON)); + const stopVpnExtButton = await driver.findComponent(ON.id(ComponentId.STOP_VPN_EXT_BUTTON)); + await stopVpnExtButton.click(); + await driver.delayMs(DELAY_TIME); + // 检查是否存在 Error 组件 + try { + let errorComponent = await driver.findComponent(ON.id('Error')); + if (errorComponent) { + let errorText = await errorComponent.getText(); + Logger.error(`${BUNDLE_NAME} StopVpnExtButton_001 failed with error: ${errorText}`); + expect(false).assertTrue(); // 如果有 Error 组件,测试失败 + } + } catch (e) { + Logger.info(`${BUNDLE_NAME} StopVpnExtButton_001 passed: No error component found.`); + expect(true).assertTrue(); // 如果没有 Error 组件,测试通过 + } + Logger.info(`${BUNDLE_NAME} StopVpnExtButton_001 end`); + done(); + }); + }); +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/ohosTest/ets/test/List.test.ets b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..794c7dc4ed66bd98fa3865e07922906e2fcef545 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,5 @@ +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/ohosTest/module.json5 b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..fd20a9217d1037c2b9c32f251965b843fbf075a0 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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. +*/ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/hvigor/hvigor-config.json5 b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..43beb743cbd25c3507b1cf8a744bf8197b3bf2fb --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/hvigor/hvigor-config.json5 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/hvigorfile.ts b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..f080d5a6d7263d1c47c49779c8356e58c83a9475 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. +*/ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/oh-package.json5 b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..952ab183532e81c9c50e5de1e64393704e1e48fe --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "modelVersion": "5.0.0", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.18", + "@ohos/hamock": "1.0.0" + } +} diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/ohosTest.md b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..f0669da45a707a39956525828d844b9dc07f35bd --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/ohosTest.md @@ -0,0 +1,15 @@ +# 测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +| ------------------------ | ------------ | --------------------- | --------------------------------- | -------- | -------- | +| StartVPN按钮点击事件 | 设备正常运行 | | 进入StartVPN界面 | 是 | pass | +| CreateTunnel按钮点击事件 | 设备正常运行 | vpnServerIp | 弹出Toast:“CreateTunnel Success” | 是 | pass | +| Protect按钮点击事件 | 设备正常运行 | vpnServerIp | 弹出Toast:“vpn Protect Success” | 是 | pass | +| SetupVpn 按钮点击事件 | 设备正常运行 | tunIp、blockedAppName | 弹出Toast:“vpn Protect Success” | 是 | pass | +| StartVpnExt 按钮点击事件 | 设备正常运行 | tunIp、blockedAppName | 弹出对话框 | 是 | pass | +| StopVpn按钮点击事件 | 设备正常运行 | | 进入StopVpn界面 | 是 | pass | +| StopVpn按钮点击事件 | 设备正常运行 | | 弹出Tosat:”Stop Success“ | 是 | pass | +| StopVPN按钮点击事件 | 设备正常运行 | | 弹出Tosat:”Stop Success“ | 是 | pass | + diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/screenshots/Create_Tunnel.jpg b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/screenshots/Create_Tunnel.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4b9ca7fa6199f4f320248f279a599abd76dfc024 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/screenshots/Create_Tunnel.jpg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/screenshots/Start_VpnExt.jpg b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/screenshots/Start_VpnExt.jpg new file mode 100644 index 0000000000000000000000000000000000000000..788f6a01c469b449e951c877b13d037fd317cd6f Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/screenshots/Start_VpnExt.jpg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/screenshots/Stop_Vpn.jpg b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/screenshots/Stop_Vpn.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6a16c132adcbbf167f12b1dc0cc921ea388bc455 Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/screenshots/Stop_Vpn.jpg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/screenshots/Vpn_Index.jpg b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/screenshots/Vpn_Index.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cb7b1e687a0c4cac7bfe2e0a99a4dc0f2d9d779d Binary files /dev/null and b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/screenshots/Vpn_Index.jpg differ diff --git a/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/server/UDP_server.py b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/server/UDP_server.py new file mode 100644 index 0000000000000000000000000000000000000000..04a48b16edd69aca9a3088130a5f79ef2b960a47 --- /dev/null +++ b/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/server/UDP_server.py @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 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. + */ + +import socket + +# 配置服务器地址和端口 +SERVER_IP = "192.168.xxx.xxx" # 监听所有IP +SERVER_PORT = 8888 # UDP端口 + +# 创建UDP Socket +server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 使用 UDP 协议 +server_socket.bind((SERVER_IP, SERVER_PORT)) + +print(f"VPN Server is running on {SERVER_IP}:{SERVER_PORT}") + +while True: + # 接收UDP数据包 + data, client_address = server_socket.recvfrom(1024) # 接收 UDP 数据 + print(f"Connection established with {client_address}") + + # 直接打印原始字节数据 + print(f"Received: {data}") + + # 如果需要解码为字符串并且确定数据是有效的文本 + try: + print(f"Decoded data: {data.decode('utf-8')}") + except UnicodeDecodeError: + print("Received non-text data, cannot decode as UTF-8.") + + # 回传数据(可自定义处理) + server_socket.sendto(data, client_address) # 使用 sendto 发送数据 \ No newline at end of file