增强通信Socket示例

Signed-off-by: aibin <aibin@openvalley.net>
This commit is contained in:
aibin 2023-06-25 20:23:38 +08:00
parent 1d00160e65
commit dd893d1b2f
86 changed files with 4321 additions and 0 deletions

View File

@ -97,6 +97,7 @@ Note:If the text contains special characters, please escape them according to th
<filteritem type="filepath" name="code/BasicFeature/TaskManagement/WorkScheduler/signTool/OpenHarmonyProfileRelease.pem" desc="Provided by code/BasicFeature/TaskManagement/WorkScheduler"/>
<filteritem type="filepath" name="code/BasicFeature/TaskManagement/WorkScheduler/signTool/app_packing_tool.jar" desc="Provided by code/BasicFeature/TaskManagement/WorkScheduler"/>
<filteritem type="filepath" name="code/BasicFeature/TaskManagement/WorkScheduler/signTool/hap-sign-tool.jar" desc="Provided by code/BasicFeature/TaskManagement/WorkScheduler"/>
<filteritem type="filepath" name="code/BasicFeature/Connectivity/StageSocket/certificate_file.zip" desc="Provided by code/BasicFeature/Connectivity/StageSocket"/>
</filefilter>
</filefilterlist>
</oatconfig>

View File

@ -0,0 +1,11 @@
/node_modules
/oh_modules
/oh-package-lock.json5
/local.properties
/.idea
**/build
/.hvigor
.cxx
/.clangd
/.clang-format
/.clang-tidy

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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": "ohos.samples.socket",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:app_icon",
"label": "$string:app_name"
}
}

View File

@ -0,0 +1,8 @@
{
"string": [
{
"name": "app_name",
"value": "Socket"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -0,0 +1,104 @@
# 网络管理-Socket连接
### 介绍
本示例主要演示了Socket在网络通信方面的应用展示了Socket在两端设备的连接验证、聊天通信方面的应用。
### 效果预览
| 输入IP | 创建房间 | 聊天 |
|------------------------------------|-----------------------------------------|-----------------------------------|
| ![](screenshots/devices/input.png) | ![](screenshots/devices/createRoom.png) | ![](screenshots/devices/chat.png) |
使用说明
1.搭建服务器环境修改服务器脚本中的服务端IP地址与本机IP地址保持一致修改完成后双击运行脚本端口号不必修改与客户端工程中的端口保持一致即可
![server.png](screenshots/devices/modifyIp.png)
2.运行成功后为阻塞状态,等待客户端连接成功后会有相应提示。
![server.png](screenshots/devices/server.png)
3.打开应用,点击**用户**选择框选择要登录的用户不同客户端保持用户选择不一致点击协议选择框选择通信协议与运行的服务器一致并输入另一个设备的IP地址(如果是TCP和TLS则输入服务端IP地址),点击**登录**按钮进入创建房间用户页面(两个设备都要依次执行此步骤)。
4.在两台设备都成功连上服务器后,在其中一个设备上点击**创建房间**按钮,任意输入房间号,另一个设备会收到邀请进入房间的信息弹框,点击**确定**按钮后,两个设备进入聊天页面。
5.在其中一个设备上输入聊天信息并点击**发送**按钮后,另一个设备的聊天页面会收到该聊天消息。
6.点击顶部标题栏左侧的**返回**图标按钮,则返回创建房间页面。
7.以上运行完成后如需再次创建房间请重启客户端和服务端进行调试。
### 工程目录
```
/Socket
├── entry # 主entry模块目录
│ └── src
│ ├── main
│ ├── ets # ets模块目录
│ ├── components # 组件目录
│ ├── ChatComponent.ets # 聊天组件
│ ├── CreateRoomDialog.ets # 创建房间弹窗
│ ├── InvitationDialog.ets # 邀请进入房间弹窗
│ ├── SelectAgreementDialog.ets # 选择协议弹窗
│ ├── SelectUserDialog.ets # 选择用户弹窗
│ ├── ToolComponent.ets # 工具面板组件
│ ├── controller #
│ ├── LoginController.ts # 负责与服务器的连接和接发消息
│ ├── data #
│ ├── ChatBox.ts # 聊天组件对应实体
│ ├── DataSource.ts # 数据源实体
│ ├── ToolData.ts # 工具面板对应实体
│ ├── entryability
│ ├── model #
│ ├── Socket.ts # 负责创建相应Socket实例
│ ├── SocketFactory.ts # Socket工厂
│ ├── pages #
│ ├── CreateRoom.ets # 创建房间页面
│ ├── NewIndex.ets # 聊天首页
│ ├── NewLogin.ets # 连接服务器页面
│ ├── socket # tcp\tls\udp
│ ├── utils # 工具类目录
│ ├── Constants.ts # 定义UI事件常量
```
### 具体实现
- UDP Socket使用UDP Socket实现文本传输无需搭建服务器对端直接交流。代码[UdpSocket.ts](entry%2Fsrc%2Fmain%2Fets%2Fsocket%2FUdpSocket.ts)。
- TCP Socket使用TCP Socket实现文本传输和公共通知事件转发需要Python脚本搭建TCP服务器公共通知用于来起音频通话应用。代码[TcpSocket.ts](entry%2Fsrc%2Fmain%2Fets%2Fsocket%2FTcpSocket.ts),服务器代码:[TCP_Server.py](server_python%2FTCP_Server.py)。
- TLS Socket使用TLS Socket实现文本传输需要Python脚本搭建TLS服务器服务器和客户端需要同一套证书文件。代码[TlsSocket.ts](entry%2Fsrc%2Fmain%2Fets%2Fsocket%2FTlsSocket.ts),服务器代码:[TLS_Server.py](server_python%2FTLS_Server.py)。
### 相关权限
网络权限: ohos.permission.INTERNET
获取WIFI信息的权限: ohos.permission.GET_WIFI_INFO
### 依赖
1. windows上启动socket服务端模拟消息转发服务器[服务器目录](./server_python)
2. 需要使用[音频通话示例](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Media/VoiceCallDemo)启动语音通话功能
### 约束与限制
1. 本示例仅支持标准系统上运行支持设备RK3568。
2. 本示例为Stage模型仅支持API9版本SDK版本号3.2.11.10,镜像版本号: OpenHarmony 4.0.7.5。
3. 本示例需要使用DevEco Studio 3.1 Release (Build Version: 3.1.0.500, built on April 28, 2023)才可编译运行。
4. 本示例在启动前需搭建服务端环境,成功启动相应服务端后再运行客户端,服务端脚本([server_python](server_python)需要在Python 3.8.5版本下运行(需与客户端处于同一局域网)。
5. TCP和TLS脚本使用同一端口不可同时运行在使用某一类型的协议前运行对应协议的服务端脚本即可TLS脚本需与证书文件certificate_file存在于同级非中文路径目录。
6. 该示例运行测试完成后,再次运行需要重新启动服务端和客户端。
### 下载
如需单独下载本工程,执行如下命令:
```
git init
git config core.sparsecheckout true
echo code/BasicFeature/Notification/CustomNotification/ > .git/info/sparse-checkout
git remote add origin https://gitee.com/openharmony/applications_app_samples.git
git pull origin master
```

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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": {
"signingConfigs": [],
"compileSdkVersion": 9,
"compatibleSdkVersion": 9,
"products": [
{
"name": "default",
"signingConfig": "default"
}
]
},
"modules": [
{
"name": "entry",
"srcPath": "./entry",
"targets": [
{
"name": "default",
"applyToProducts": [
"default"
]
}
]
}
]
}

Binary file not shown.

View File

@ -0,0 +1,4 @@
/node_modules
/.preview
/build
/.cxx

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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": {
},
"targets": [
{
"name": "default"
},
{
"name": "ohosTest",
}
]
}

View File

@ -0,0 +1,2 @@
// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
export { hapTasks } from '@ohos/hvigor-ohos-plugin';

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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.
*/
{
"license": "ISC",
"devDependencies": {},
"name": "entry",
"description": "example description",
"repository": {},
"version": "1.0.0",
"dependencies": {}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 default class Constants {
// COMMON EVENT
public static readonly COMM_EVENT_ANSWER_FROM_HOST: string = 'COMM_EVENT_ANSWER_FROM_HOST'; // 公共事件,接听 聊天示例->Voice
public static readonly COMM_EVENT_HANGUP_FROM_HOST: string = 'COMM_EVENT_HANGUP_FROM_HOST'; // 挂断,拒接 聊天示例->Voice
public static readonly COMM_EVENT_ANSWER_FROM_VOICE: string = 'COMM_EVENT_ANSWER_FROM_VOICE'; // 公共事件,接听 Voice->聊天示例
public static readonly COMM_EVENT_HANGUP_FROM_VOICE: string = 'COMM_EVENT_HANGUP_FROM_VOICE'; // 挂断,拒接 Voice->聊天示例
// ability 参数
public static readonly START_ABILITY_EVENT_KEY = 'START_ABILITY_EVENT_KEY'; // 启动ability事件 key
public static readonly START_ABILITY_EVENT_CALL = 'START_ABILITY_CALL'; // 启动ability事件请求通话
public static readonly START_ABILITY_EVENT_ANSWER = 'START_ABILITY_ANSWER'; // 启动ability事件被请求通话
public static readonly START_ABILITY_DATA_KEY = 'START_ABILITY_DATA_KEY'; // 启动ability数据参数,这里传通话人的id
public static readonly BUNDLE_NAME = 'com.samples.voicecalldemo'; // 启动ability的BUNDLENAME
public static readonly ABILITY_NAME = 'EntryAbility'; // 启动ability的ABILITYNAME
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 { ChatBox } from '../data/ChatBox';
@Component
export default struct ChatComponent {
private item: ChatBox = undefined;
private userId: number;
build() {
Row() {
if (!this.item.isSend) {
Text(this.userId === 1 ? $r('app.string.wen') : $r('app.string.feng'))
.width(32)
.height(32)
.fontSize(16)
.fontColor($r('app.color.COLOR_FFFFFF'))
.fontFamily($r('app.string.font_family'))
.textAlign(TextAlign.Center)
.backgroundColor($r('app.color.COLOR_33000000'))
.borderRadius(52)
.margin({ left: 24, right: 8 })
}
Column() {
Text(this.item.message)
.maxLines(5)
.padding(10)
.fontSize(20)
.borderRadius(10)
.alignSelf(this.item.isSend ? ItemAlign.End : ItemAlign.Start)
.backgroundColor(this.item.isSend ? $r('app.color.COLOR_5BA854') : $r('app.color.COLOR_FFFFFF'))
}
.width('70%')
.margin({ right: 5 })
if (this.item.isSend) {
Text(this.userId === 1 ? $r('app.string.feng') : $r('app.string.wen'))
.width(32)
.height(32)
.fontSize(16)
.fontColor($r('app.color.COLOR_FFFFFF'))
.fontFamily($r('app.string.font_family'))
.textAlign(TextAlign.Center)
.backgroundColor($r('app.color.COLOR_33000000'))
.borderRadius(52)
.margin({ left: 8, right: 24 })
}
}
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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.
*/
const TAG: string = '[CreateRoomDialog]';
import Logger from '../utils/Logger';
@CustomDialog
export struct CreateRoomDialog {
controller: CustomDialogController;
private title: Resource = $r('app.string.input_room');
@Link roomNumber: string; // 当前选择项的内容
build() {
Column() {
Row() {
Text(this.title)
.fontSize(20)
.fontColor($r('app.color.COLOR_E6000000'))
.fontFamily($r('app.string.font_family'))
.textAlign(TextAlign.Center)
.margin({ top: 14, bottom: 24 })
}
.width('85%')
.justifyContent(FlexAlign.Start)
TextInput()
.id('inputNumber')
.backgroundColor($r('app.color.COLOR_FFFFFF'))
.width('85%')
.height(48)
.maxLength(10)
.borderRadius(0)
.fontSize(16)
.fontColor($r('app.color.COLOR_99000000'))
.fontFamily($r('app.string.font_family_regular'))
.padding(0)
.onChange((inputs: string) => {
this.roomNumber = inputs;
})
Divider()
.vertical(false)
.strokeWidth(1)
.width('85%')
.color($r('app.color.COLOR_33000000'))
Row() {
Row(){
Text($r('app.string.cancel'))
.fontSize(16)
.fontColor($r('app.color.COLOR_007DFF'))
.fontFamily($r('app.string.font_family'))
.textAlign(TextAlign.Center)
}
.height('100%')
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.controller.close();
});
Divider()
.vertical(true)
.strokeWidth(1)
.height(24)
.color('#33000000')
Row(){
Text($r('app.string.confirm'))
.id('roomConfirm')
.fontSize(16)
.fontColor($r('app.color.COLOR_007DFF'))
.fontFamily($r('app.string.font_family'))
.textAlign(TextAlign.Center)
}
.layoutWeight(1)
.height('100%')
.justifyContent(FlexAlign.Center)
.onClick(() => {
Logger.info(TAG, `onClick begin: ${this.roomNumber}`);
AppStorage.Set('sendMsg', 'invitationNumber&' + this.roomNumber);
AppStorage.Set('sendMsg', 'clear');
this.controller.close();
});
}
.alignItems(VerticalAlign.Center)
.height(56)
.width('85%')
}
.alignItems(HorizontalAlign.Center)
.width('100%')
.height(176)
.borderRadius(24)
.backgroundColor($r('app.color.COLOR_FFFFFF'))
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 TAG: string = '[InvitationDialog]';
@CustomDialog
export struct InvitationDialog {
controller: CustomDialogController;
private title: Resource = $r('app.string.invitation');
private userId: number;
private socketType;
@Link invitation: Resource;
build() {
Column() {
Row() {
Text(this.title)
.fontSize(20)
.fontColor($r('app.color.COLOR_E6000000'))
.fontFamily($r('app.string.font_family'))
.textAlign(TextAlign.Center)
.margin({ top: 14, bottom: 24 })
}
.width('85%')
.justifyContent(FlexAlign.Start)
Row() {
Text(this.invitation)
.fontSize(16)
.fontColor($r('app.color.COLOR_E6000000'))
.fontFamily($r('app.string.font_family'))
.textAlign(TextAlign.Center)
.margin({ top: 14, bottom: 24 })
}
.width('85%')
.justifyContent(FlexAlign.Start)
Row() {
Text($r('app.string.cancel'))
.fontSize(16)
.fontColor($r('app.color.COLOR_007DFF'))
.fontFamily($r('app.string.font_family'))
.layoutWeight(1)
.textAlign(TextAlign.Center)
.onClick(() => {
this.controller.close();
});
Divider()
.vertical(true)
.strokeWidth(1)
.height(24)
.color($r('app.color.COLOR_33000000'))
Text($r('app.string.confirm'))
.fontSize(16)
.fontColor($r('app.color.COLOR_007DFF'))
.fontFamily($r('app.string.font_family'))
.layoutWeight(1)
.textAlign(TextAlign.Center)
.onClick(() => {
AppStorage.SetOrCreate('sendMsg', 'ok');
router.pushUrl({
url: 'pages/NewIndex',
params: {
'userId': this.userId,
'socketType': this.socketType
}
});
})
}
.alignItems(VerticalAlign.Center)
.height(56)
.width('85%')
}
.alignItems(HorizontalAlign.Center)
.width('100%')
.height(176)
.borderRadius(24)
.backgroundColor($r('app.color.COLOR_FFFFFF'))
}
}

View File

@ -0,0 +1,159 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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.
*/
const TAG: string = '[SelectAgreementDialog]';
@CustomDialog
export struct SelectAgreementDialog {
controller: CustomDialogController;
private title: Resource = $r('app.string.select');
@State selected: number = 0; // 当前选择项的协议索引
@Link result: number; // 当前选择项的内容
build() {
Column() {
Text(this.title)
.fontSize(20)
.fontColor($r('app.color.COLOR_E6000000'))
.fontFamily($r('app.string.font_family'))
.width('90%')
.textAlign(TextAlign.Start)
.margin({top: 14, bottom:14})
Column(){
Row(){
Text($r('app.string.udp'))
.fontSize(16)
.fontColor($r('app.color.COLOR_000000'))
.fontFamily($r('app.string.font_family_regular'))
.textAlign(TextAlign.Center)
Blank()
Radio({group:'agreement',value:''})
.width(24)
.height(24)
.checked(this.selected == 0)
.onChange(bool=>{
if (bool) {
this.selected = 0;
}
})
}
.width('100%')
.height(56)
.alignItems(VerticalAlign.Center)
.onClick(e=>{
this.selected = 0;
})
Divider()
.vertical(false)
.strokeWidth(1)
.color($r('app.color.COLOR_33000000'))
.margin({top:10})
Row(){
Text($r('app.string.tcp'))
.fontSize(16)
.fontColor($r('app.color.COLOR_000000'))
.fontFamily($r('app.string.font_family_regular'))
.textAlign(TextAlign.Center)
Blank()
Radio({group:'agreement',value:''})
.width(24)
.height(24)
.checked(this.selected == 1)
.onChange(bool=>{
if (bool) {
this.selected = 1;
}
})
}
.width('100%')
.height(56)
.alignItems(VerticalAlign.Center)
.onClick(e=>{
this.selected = 1;
})
Divider()
.vertical(false)
.strokeWidth(1)
.color($r('app.color.COLOR_33000000'))
.margin({top:10})
Row(){
Text($r('app.string.tls'))
.fontSize(16)
.fontColor($r('app.color.COLOR_000000'))
.fontFamily($r('app.string.font_family_regular'))
.textAlign(TextAlign.Center)
Blank()
Radio({group:'agreement',value:''})
.width(24)
.height(24)
.checked(this.selected == 2)
.onChange(bool=>{
if (bool) {
this.selected = 2;
}
})
}
.width('100%')
.height(56)
.alignItems(VerticalAlign.Center)
.onClick(e=>{
this.selected = 2;
})
}
.width('90%')
Row() {
Text($r('app.string.cancel'))
.fontSize(16)
.fontColor($r('app.color.COLOR_007DFF'))
.fontWeight(FontWeight.Medium)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.onClick(() => {
this.controller.close();
})
Divider()
.vertical(true)
.strokeWidth(1)
.color($r('app.color.COLOR_33000000'))
.height(20)
Text($r('app.string.confirm'))
.id('agreementConfirm')
.fontSize(16)
.fontColor($r('app.color.COLOR_007DFF'))
.fontWeight(FontWeight.Medium)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.onClick(() => {
this.result = this.selected;
this.controller.close();
})
}
.alignItems(VerticalAlign.Center)
.height(40)
.width('100%')
}
.alignItems(HorizontalAlign.Center)
.width('90%')
.borderRadius(24)
.backgroundColor(Color.White)
}
}

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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.
*/
const TAG: string = '[SelectUserDialog]';
@CustomDialog
export struct SelectUserDialog {
controller: CustomDialogController;
private title: Resource = $r('app.string.user_select');
@Link result: number; // 当前选择项的用户id
@State selected: number = 1; // 最终返回的用户id
build() {
Column() {
Text(this.title)
.fontSize(20)
.fontColor($r('app.color.COLOR_E6000000'))
.fontFamily($r('app.string.font_family'))
.width('90%')
.textAlign(TextAlign.Start)
.margin({ top: 14, bottom: 14 })
Column() {
Row() {
Text($r('app.string.fengzi'))
.fontSize(16)
.fontColor($r('app.color.COLOR_000000'))
.fontFamily($r('app.string.font_family_regular'))
.textAlign(TextAlign.Center)
Blank()
Radio({ group: 'user', value: '' })
.width(24)
.height(24)
.checked(this.selected == 1)
.onChange(bool => {
if (bool) {
this.selected = 1;
}
})
}
.width('100%')
.height(56)
.alignItems(VerticalAlign.Center)
.onClick(e => {
this.selected = 1;
})
Divider()
.vertical(false)
.strokeWidth(1)
.color($r('app.color.COLOR_33000000'))
.margin({ top: 10 })
Row() {
Text($r('app.string.wenzi'))
.fontSize(16)
.fontColor($r('app.color.COLOR_000000'))
.fontFamily($r('app.string.font_family_regular'))
.textAlign(TextAlign.Center)
Blank()
Radio({ group: 'user', value: '' })
.width(24)
.height(24)
.checked(this.selected == 2)
.onChange(bool => {
if (bool) {
this.selected = 2;
}
})
}
.width('100%')
.height(56)
.alignItems(VerticalAlign.Center)
.onClick(e => {
this.selected = 2;
})
}
.width('90%')
Row() {
Text($r('app.string.cancel'))
.fontSize(16)
.fontColor($r('app.color.COLOR_007DFF'))
.fontWeight(FontWeight.Medium)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.onClick(() => {
this.controller.close();
})
Divider()
.vertical(true)
.strokeWidth(1)
.color($r('app.color.COLOR_33000000'))
.height(20)
Text($r('app.string.confirm'))
.id('usersConfirm')
.fontSize(16)
.fontColor($r('app.color.COLOR_007DFF'))
.layoutWeight(1)
.textAlign(TextAlign.Center)
.onClick(() => {
this.result = this.selected;
this.controller.close();
})
}
.alignItems(VerticalAlign.Center)
.height(40)
.width('100%')
}
.alignItems(HorizontalAlign.Center)
.width('90%')
.borderRadius(24)
.backgroundColor(Color.White)
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 startAbility from '../utils/AbilityUtil';
import ToolData from '../data/ToolData';
import Constants from '../Constants';
@Component
export default struct ToolComponent {
private toolArray: Array<ToolData>; // 功能数组 暂只有语音通话
build() {
Row() {
ForEach(this.toolArray, (item: ToolData, index) => {
Button() {
Image(item.getImgResource())
.width(40)
.height(40)
.objectFit(ImageFit.Contain)
}
.width(100)
.height(100)
.borderRadius(10)
.type(ButtonType.Normal)
.backgroundColor(Color.White)
.margin({ left: 10 })
.onClick(e => {
let want = {
bundleName: item.getBundleName(),
abilityName: item.getAbilityName(),
parameters: item.getParameters()
};
AppStorage.SetOrCreate('indexMsg', Constants.START_ABILITY_EVENT_ANSWER);
AppStorage.SetOrCreate('indexMsg', 'clear');
startAbility(want);
})
}, item => item.id)
}
.width('100%')
.height('99%')
.margin(10)
.alignItems(VerticalAlign.Top)
}
}

View File

@ -0,0 +1,138 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 Logger from '../utils/Logger';
import type { SocketType } from '../model/Socket';
import type Socket from '../model/Socket';
import SocketFactory from '../model/SocketFactory';
import wifiManager from '@ohos.wifiManager';
const TAG = '[SocketController_] ';
export default class SocketController {
private mSocket: Socket = null;
constructor(socketType: SocketType, localIp: string, port: number) {
Logger.info(TAG, ` mSocket constructor ${JSON.stringify(socketType)}`);
// 创建相应协议的Socket对象
this.mSocket = SocketFactory.createSocket(socketType);
this.mSocket.createSocket(localIp, port);
}
/**
* Socket
* @param oppositeAddress
* @param oppositePort
* @param callback
*/
public connectSocket(oppositeAddress: string, oppositePort: number, callback): void {
Logger.info(TAG, ` mSocket connect ing ${JSON.stringify(this.mSocket)}`);
this.mSocket.connectSocket(oppositeAddress, oppositePort).then(res => {
Logger.info(TAG, ` mSocket connect success ${JSON.stringify(res)}`);
// 连接成功订阅消息回调
this.onMessage(callback);
// 连接成功首先发送一条消息
this.sendData('connect success');
}).catch(rej => {
Logger.info(TAG, ` mSocket connect fail ${JSON.stringify(rej)}`);
});
}
/**
*
* @param callback
*/
public onMessage(callback): void {
this.mSocket.setOnMessageReceivedListener((buffer: ArrayBuffer) => {
// 获取数据 解析
let data = this.decodeMsg(buffer);
callback(data);
});
}
/**
*
* @param callback
*/
public onClose(callback): void {
this.mSocket.setOnCloseListener(callback);
this.onWifiDisconnect(callback);
}
public onWifiDisconnect(callback): void {
Logger.info(TAG, 'onWifiDisconnect begin ');
wifiManager.on('wifiConnectionChange', (data) => {
Logger.info(TAG, `onWifiDisconnect wifiConnectionChange data = ${data}}`);
if (data === 0) {
callback();
}
});
wifiManager.on('wifiStateChange', (data) => {
Logger.info(TAG, `onWifiDisconnect wifiStateChange data = ${data}}`);
if (data === 0) {
callback();
}
});
}
/**
* TCP TLS发送消息
* @param data
*/
public sendData(data: string): void {
this.mSocket.sendData(data);
}
/**
* UDP发送消息
* @param data
* @param oppositeAddress
* @param oppositePort
*/
public udpSendData(data: string, oppositeAddress: string, oppositePort: number): void {
this.mSocket.sendData(data, oppositeAddress, oppositePort);
}
/**
* Socket
*/
public closeSocket(): void {
this.mSocket.closeSocket();
}
public async isConnected(): Promise<boolean> {
return await this.mSocket.isConnected();
}
/**
*
* @param buffer
*/
private decodeMsg(buffer: ArrayBuffer): string {
Logger.info(TAG, `buffer type = ${typeof buffer}`);
Logger.info(TAG, `buffer = ${buffer}`);
let dataView = new Uint8Array(buffer);
Logger.info(TAG, `length = ${dataView.byteLength}`);
let data = '';
for (let i = 0; i < dataView.byteLength; ++i) {
let c = String.fromCharCode(dataView[i]);
if (c !== '\n') {
data += c;
}
}
return data;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2023 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 class ChatBox {
isSend: boolean;
message: string;
date: string;
constructor(isSend: boolean, message: string, date: string) {
this.isSend = isSend;
this.message = message;
this.date = date;
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2023 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 type { ChatBox } from './ChatBox';
import Logger from '../utils/Logger';
class BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = [];
public totalCount(): number {
return 0;
}
public getData(index: number): ChatBox {
return undefined;
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
Logger.info('add listener');
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
Logger.info('remove listener');
this.listeners.splice(pos, 1);
}
}
notifyDataReload(): void {
this.listeners.forEach((listener) => {
listener.onDataReloaded();
});
}
notifyDataAdd(index: number): void {
this.listeners.forEach((listener) => {
listener.onDataAdd(index);
});
}
notifyDataChange(index: number): void {
this.listeners.forEach((listener) => {
listener.onDataChange(index);
});
}
notifyDataDelete(index: number): void {
this.listeners.forEach((listener) => {
listener.onDataDelete(index);
});
}
notifyDataMove(from: number, to: number): void {
this.listeners.forEach((listener) => {
listener.onDataMove(from, to);
});
}
}
export class ChatSource extends BasicDataSource {
private chatsArray: Array<ChatBox> = [];
public totalCount(): number {
return this.chatsArray.length;
}
public getData(index: number): ChatBox {
return this.chatsArray[index];
}
public addData(index: number, data: ChatBox): void {
this.chatsArray.splice(index, 0, data);
this.notifyDataAdd(index);
}
public pushData(data: ChatBox): void {
this.chatsArray.push(data);
this.notifyDataAdd(this.chatsArray.length - 1);
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 default class ToolData {
private imgResource: Resource;
private bundleName: string;
private abilityName: string;
private parameters: { [key: string]: Object };
constructor(imgResource: Resource, bundleName: string, abilityName: string, parameters?: { [key: string]: Object }) {
this.imgResource = imgResource;
this.bundleName = bundleName;
this.abilityName = abilityName;
if (parameters) {
this.parameters = parameters;
}
}
public setImgResource(imgResource: Resource): void {
this.imgResource = imgResource;
}
public getImgResource(): Resource {
return this.imgResource;
}
public setBundleName(bundleName: string): void {
this.bundleName = bundleName;
}
public getBundleName(): string {
return this.bundleName;
}
public setAbilityName(abilityName: string): void {
this.abilityName = abilityName;
}
public getAbilityName(): string {
return this.abilityName;
}
public setParameters(parameters: { [key: string]: Object }): void {
this.parameters = parameters;
}
public getParameters(): { [key: string]: Object } {
return this.parameters;
}
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) 2023 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 UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
import hilog from '@ohos.hilog';
import CommonEvent from '@ohos.commonEventManager';
import Logger from '../utils/Logger';
import Constants from '../Constants';
const TAG: string = '[EntryAbility] ';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
globalThis.abilityContext = this.context;
let subscriber; // 用于保存创建成功的订阅者对象
// 订阅者信息
let subscribeInfo = {
events: [
Constants.COMM_EVENT_ANSWER_FROM_VOICE,
Constants.COMM_EVENT_HANGUP_FROM_VOICE
],
};
// 订阅公共事件回调
function subscribeCallBack(err, data): void {
Logger.info(TAG, 'subscribeCallBack into ' + JSON.stringify(err));
let sendEvent = ''; // 发送的事件
if (err !== null && err.code) {
Logger.error(TAG, 'subscribeCallBack failed ' + JSON.stringify(err));
} else {
Logger.info(TAG, 'subscribeCallBack data:' + JSON.stringify(data));
// 拿出数据中的event事件
sendEvent = data.event;
Logger.info(
TAG,
'subscribeCallBack data sendEvent 1:' + JSON.stringify(sendEvent)
);
// 触发sendMsg发送消息
AppStorage.SetOrCreate('sendMsg', sendEvent);
AppStorage.SetOrCreate('sendMsg', 'clear');
Logger.info(
TAG,
'subscribeCallBack data sendEvent 2:' + JSON.stringify(sendEvent)
);
}
sendEvent = '';
}
// 创建订阅者回调
function createSubscriberCallBack(err, commonEventSubscriber): void {
Logger.info(TAG, 'createSubscriber into ' + JSON.stringify(err));
if (err !== null && err.code) {
Logger.error(TAG, 'createSubscriber failed ' + JSON.stringify(err));
} else {
Logger.info(TAG, 'createSubscriber success');
subscriber = commonEventSubscriber;
// 订阅公共事件
CommonEvent.subscribe(subscriber, subscribeCallBack);
}
}
// 创建订阅者
CommonEvent.createSubscriber(subscribeInfo, createSubscriberCallBack);
}
onDestroy() {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage) {
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/NewLogin', (err, data) => {
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. Data: %{public}s',
JSON.stringify(data) ?? ''
);
});
}
onWindowStageDestroy() {
// Main window is destroyed, release UI related resources
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground() {
// Ability has brought to foreground
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground() {
// Ability has back to background
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 SocketType {
UDP,
TCP,
TLS
}
/**
* TCP TLS UDP
*/
export default interface Socket {
createSocket(localIp: string, port: number): Promise<boolean>;
connectSocket(address: string, port: number): Promise<boolean>;
closeSocket(): Promise<void>;
sendData(data: string, address?: string, port?: number): Promise<void>;
isConnected(): Promise<boolean>;
setOnMessageReceivedListener(callback): void;
setOnCloseListener(callback): void;
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 { SocketType } from '../model/Socket';
import type Socket from '../model/Socket';
import TcpSocket from '../socket/TcpSocket';
import TlsSocket from '../socket/TlsSocket';
import UdpSocket from '../socket/UdpSocket';
/**
* Socket对象的工厂
*/
export default class SocketFactory {
/**
* Socket
* @param socketType
*/
public static createSocket(socketType: SocketType): Socket {
switch (socketType) {
case SocketType.UDP:
return new UdpSocket();
case SocketType.TCP:
return new TcpSocket();
case SocketType.TLS:
return new TlsSocket();
}
return null;
}
}

View File

@ -0,0 +1,141 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 { CreateRoomDialog } from '../components/CreateRoomDialog';
import { InvitationDialog } from '../components/InvitationDialog';
import { SocketType } from '../model/Socket';
const TAG: string = '[CreateRoom] ';
@Entry
@Component
export default struct CreateRoom {
@State roomNumber: string = '';
@State roomTip: boolean = false;
@State invitation: Resource = $r('app.string.invitation_detail');
private userId: number = <number> router.getParams()['userId'];
private socketType: SocketType = <SocketType> router.getParams()['socketType'];
@StorageLink('invitationNumber') @Watch('invitationNumberChange') invitationNumber: string = '';
invitationNumberChange(): void {
this.invitation = $r('app.string.invitation_detail', this.invitationNumber);
this.invitationDialog.open();
}
// 创建房间弹窗
private createRoomDialog: CustomDialogController = new CustomDialogController({
builder: CreateRoomDialog({
roomNumber: $roomNumber
})
});
// 邀请聊天弹窗
private invitationDialog: CustomDialogController = new CustomDialogController({
builder: InvitationDialog({
invitation: $invitation,
userId: this.userId,
socketType: this.socketType
}),
alignment: DialogAlignment.Bottom,
offset: { dx: 0, dy: -20 }
});
/**
* 跳转至Index页面
*/
pushIndex(): void {
router.pushUrl({
url: 'pages/NewIndex',
params: {
'userId': this.userId,
'socketType': this.socketType
}
});
}
build() {
Column() {
Row() {
Image($r('app.media.ic_public_back'))
.width(24)
.height(24)
.objectFit(ImageFit.Contain)
.margin({ left: 26 })
.onClick(e => {
router.back();
})
Column() {
Text(this.userId === 1 ? $r('app.string.fengzi') : $r('app.string.wenzi'))
.fontSize(20)
.fontColor($r('app.color.COLOR_000000'))
.fontFamily($r('app.string.font_family'))
.margin({ left: 13, top: 3, bottom: 2 })
Row() {
Text()
.width(12)
.height(12)
.backgroundColor($r('app.color.COLOR_5BA854'))
.borderRadius(6)
.margin({ left: 13, right: 3 })
Text($r('app.string.online'))
.fontSize(14)
.fontColor($r('app.color.COLOR_99000000'))
.fontFamily($r('app.string.font_family_regular'))
}
.justifyContent(FlexAlign.Start)
}
}
.width('100%')
.height(56)
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Center)
Column({ space: 8 }) {
Image($r('app.media.ic_empty'))
.width(312)
.height(147)
.objectFit(ImageFit.Contain)
Text($r('app.string.not_room'))
.fontSize(14)
.fontColor($r('app.color.COLOR_66000000'))
.fontFamily($r('app.string.font_family_regular'))
}
.width('100%')
.padding({ top: 38 })
.layoutWeight(1)
.justifyContent(FlexAlign.Center)
Column() {
Button($r('app.string.create_room'))
.id('createRoom')
.fontSize(16)
.width('50%')
.height(40)
.backgroundColor($r('app.color.COLOR_007DFF'))
.onClick(e => {
this.createRoomDialog.open();
})
}
.width('100%')
.height(88)
.justifyContent(FlexAlign.Center)
}
.width('100%')
.height('100%')
}
}

View File

@ -0,0 +1,210 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 Logger from '../utils/Logger';
import startAbility from '../utils/AbilityUtil';
import { ChatBox } from '../data/ChatBox';
import { ChatSource } from '../data/DataSource';
import ToolData from '../data/ToolData';
import { SocketType } from '../model/Socket';
import ChatComponent from '../components/ChatComponent';
import Constants from '../Constants';
const TAG: string = '[NewIndex] ';
@Entry
@Component
struct NewIndex {
private userId: number = <number> router.getParams()['userId'];
private oppositeId: number = this.userId === 1 ? 2 : 1;
private socketType: SocketType = <SocketType> router.getParams()['socketType'];
@State chats: ChatSource = new ChatSource();
@State onlineState: boolean = true;
@StorageLink('receiveMsg') @Watch('decodeMsg') receiveMsg: string = '';
@State message: string = '';
@State isPanelShow: boolean = false; // 下拉面板是否拉出
@State toolArray: Array<ToolData> = [];
/**
* 解析收到的消息数据
*/
decodeMsg(): void {
Logger.info(TAG, `decodeMsg aray buffer begin:${this.receiveMsg}`);
if (this.receiveMsg === '' || this.receiveMsg === 'clear') {
Logger.info(TAG, `decodeMsg receiveMsg null:${this.receiveMsg}`);
return;
}
Logger.info(TAG, `decodeMsg aray buffer:${this.receiveMsg}`);
if (this.receiveMsg === 'online') {
this.onlineState = true;
}
else if (this.receiveMsg === 'offline') {
this.onlineState = false;
AppStorage.SetOrCreate('sendMsg', 'closeSocket');
} else if (this.receiveMsg === 'exit') {
AppStorage.SetOrCreate('sendMsg', 'closeSocket');
this.chats['chatsArray'] = [];
this.chats.notifyDataReload();
router.back();
} else {
Logger.info(TAG, `receive in Chat sucess`);
let date = (new Date().getMonth() + 1) + '.' + new Date().getDate() + '-' + new Date().getHours() + ':' + new Date().getMinutes();
Logger.info(TAG, `pre:this.chats=${JSON.stringify(this.chats['chatsArray'])}`);
this.chats.pushData(new ChatBox(false, this.receiveMsg, date));
Logger.info(TAG, `after:this.chats=${JSON.stringify(this.chats['chatsArray'])}`);
}
this.receiveMsg = '';
}
build() {
Column() {
Row() {
Image($r('app.media.ic_public_back'))
.id('indexBack')
.width(24)
.height(24)
.objectFit(ImageFit.Contain)
.margin({ left: 26 })
.onClick(e => {
router.back();
})
Column() {
Text(this.userId === 1 ? $r('app.string.wenzi') : $r('app.string.fengzi'))
.fontSize(20)
.fontColor($r('app.color.COLOR_000000'))
.fontFamily($r('app.string.font_family'))
.margin({ left: 13, top: 3, bottom: 2 })
Row() {
Text()
.width(12)
.height(12)
.backgroundColor(this.onlineState ? $r('app.color.COLOR_5BA854') : $r('app.color.COLOR_F1F3F5'))
.borderRadius(6)
.margin({ left: 13, right: 3 })
Text(this.onlineState === true ? $r('app.string.online') : $r('app.string.offline'))
.fontSize(14)
.fontColor($r('app.color.COLOR_99000000'))
.fontFamily($r('app.string.font_family_regular'))
}
.justifyContent(FlexAlign.Start)
}
}
.width('100%')
.height(56)
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Center)
Column() {
// 消息面板
Scroll() {
Column() {
LazyForEach(this.chats, (item, index) => {
Row() {
ChatComponent({ item: item, userId: this.userId })
.margin({ top: 24 })
}
.margin({ top: 5, bottom: 10 })
.alignSelf(item.isSend ? ItemAlign.End : ItemAlign.Start)
}, item => item.message)
}
.width('100%')
.padding(5)
}
.scrollable(ScrollDirection.Vertical)
.scrollBar(BarState.Auto)
.scrollBarColor(Color.Gray)
.scrollBarWidth(5)
.width('100%')
}
.padding({ top: 29 })
.width('100%')
.backgroundColor($r('app.color.COLOR_F1F3F5'))
.layoutWeight(1)
Row() {
if (this.socketType == SocketType.TCP) {
Image($r('app.media.ic_phone'))
.width(20)
.height(20)
.margin({ left: 14, top: 18, bottom: 18 })
.objectFit(ImageFit.Contain)
.onClick(e => {
let parameters = {
'START_ABILITY_EVENT_KEY': Constants.START_ABILITY_EVENT_CALL,
'START_ABILITY_DATA_KEY': this.oppositeId
};
let want = {
bundleName: Constants.BUNDLE_NAME,
abilityName: Constants.ABILITY_NAME,
parameters: parameters
};
AppStorage.SetOrCreate('sendMsg', Constants.START_ABILITY_EVENT_ANSWER);
AppStorage.SetOrCreate('sendMsg', 'clear');
startAbility(want);
})
}
TextInput({ placeholder: this.message, text: this.message })
.id('inputMsg')
.layoutWeight(1)
.height(40)
.backgroundColor($r('app.color.COLOR_0D000000'))
.width('85%')
.fontSize(16)
.fontColor($r('app.color.COLOR_E6000000'))
.fontFamily($r('app.string.font_family_regular'))
.margin({ left: 14, right: 12 })
.onChange((inputs: string) => {
this.message = inputs;
})
Button($r('app.string.send_message'))
.id('sendMsg')
.fontSize(16)
.width(80)
.height(40)
.margin({ right: 12 })
.backgroundColor($r('app.color.COLOR_5BA854'))
.onClick(e => {
Logger.info(TAG, `send message: ${this.message}`);
if (this.message) {
let date = (new Date().getMonth() + 1) + '.' + new Date().getDate() + '-' + new Date().getHours() + ':' + new Date().getMinutes();
let sendMessage = new ChatBox(true, this.message, date);
this.chats.pushData(sendMessage);
let bool1 = AppStorage.Set('sendMsg', this.message);
let bool2 = AppStorage.Set('sendMsg', 'clear');
Logger.info(TAG, `send message bool1: ${bool1}`);
Logger.info(TAG, `send message bool2: ${bool2}`);
}
this.message = '';
})
}
.width('100%')
.height(56)
}
.width('100%')
.height('100%')
}
}

View File

@ -0,0 +1,335 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 wifi from '@ohos.wifi';
import router from '@ohos.router';
import CommonEvent from '@ohos.commonEventManager';
import promptAction from '@ohos.promptAction';
import { SelectUserDialog } from '../components/SelectUserDialog';
import { SelectAgreementDialog } from '../components/SelectAgreementDialog';
import SocketController from '../controller/LoginController';
import { SocketType } from '../model/Socket';
import { resolveIP, checkIp } from '../utils/IpUtil';
import Logger from '../utils/Logger';
import startAbility from '../utils/AbilityUtil';
import Constants from '../Constants';
const TAG: string = '[NewLogin] ';
let oppositeAddress = '192.168.8.160';
let oppositePort = 9090;
let localAddress = resolveIP(wifi.getIpInfo().ipAddress);
let localPort = 8081;
@Entry
@Component
export default struct NewLogin {
@State userId: number = 1;
@State selectAgreementIndex: number = 0;
@State receiveMsg: string = '';
private socketType: Array<SocketType> = [SocketType.UDP, SocketType.TCP, SocketType.TLS];
private socketTypeResource: Array<Resource> = [$r('app.string.udp'), $r('app.string.tcp'), $r('app.string.tls')];
private socketController: SocketController = null;
@StorageLink('sendMsg') @Watch('action') sendMsg: string = '';
// 发送公共事件的回调
private publishCallback = (err) => {
if (err !== null && err.code) {
Logger.info(TAG, `publishCallback publish failed :${JSON.stringify(err)}`);
} else {
Logger.info(TAG, `publishCallback publish success`);
}
}
// 选择用户弹窗
private selectUserDialog: CustomDialogController = new CustomDialogController({
builder: SelectUserDialog({
result: $userId
}),
alignment: DialogAlignment.Bottom,
offset: { dx: 0, dy: -20 }
});
// 选择协议弹窗
private selectAgreementDialog: CustomDialogController = new CustomDialogController({
builder: SelectAgreementDialog({
result: $selectAgreementIndex
}),
alignment: DialogAlignment.Bottom,
offset: { dx: 0, dy: -20 }
});
/**
* 聊天页面传来的相应动作
*/
action(): void {
Logger.info(TAG, `action_sendMsg:${this.sendMsg}`);
if (this.sendMsg === 'closeSocket') {
this.socketController.closeSocket();
} else if (this.sendMsg === 'clear') {
} else {
// 转发消息 包括事件
this.sendData(this.sendMsg);
}
}
/**
* 判断是否为UDP发送
* @param message
*/
sendData(message: string): void {
if (this.socketType[this.selectAgreementIndex] !== SocketType.UDP) {
this.socketController.sendData(message);
} else {
this.socketController.udpSendData(message, oppositeAddress, oppositePort);
}
}
/**
* TCP或TLS连接 服务端或对端
*/
connectSocketAndOnClose(): void {
this.socketController.connectSocket(oppositeAddress, oppositePort, (data: string) => {
this.receiveMessage(data);
})
// 连接同时订阅关闭连接回调
this.socketController.onClose(() => {
CommonEvent.publish(Constants.COMM_EVENT_HANGUP_FROM_HOST, this.publishCallback);
})
}
/**
* UDP直接订阅消息
*/
onUdpMessage(): void {
this.socketController.onMessage((data: string) => {
this.receiveMessage(data);
})
this.sendData('connect success');
}
/**
* 对接收的消息处理
* @param data
*/
receiveMessage(data: string): void {
Logger.info(TAG, 'receiveMessage begin data ' + JSON.stringify(data));
if (data.split('&')[0] && data.split('&')[0] === 'invitationNumber') {
AppStorage.SetOrCreate('invitationNumber', data.split('&')[1]);
return;
}
if (data == 'ok') {
// router.clear();
this.pushIndex();
} else if (data === Constants.START_ABILITY_EVENT_ANSWER) {
// 如果是通话请求,则拉起语音通话应用
Logger.info(TAG, 'receiveMessage data userId ' + JSON.stringify(data));
let oppositeId = this.userId === 1 ? 2 : 1
Logger.info(TAG, 'receiveMessage data oppositeId ' + JSON.stringify(data));
let want = {
bundleName: Constants.BUNDLE_NAME,
abilityName: Constants.ABILITY_NAME,
parameters: {
'START_ABILITY_EVENT_KEY': Constants.START_ABILITY_EVENT_ANSWER,
'START_ABILITY_DATA_KEY': oppositeId
}
};
// 拉起语音通话应用
startAbility(want);
} else if (data === Constants.COMM_EVENT_ANSWER_FROM_VOICE) {
// 发布公共事件至语音通话示例:接听
CommonEvent.publish(Constants.COMM_EVENT_ANSWER_FROM_HOST, this.publishCallback);
Logger.info(TAG, 'receiveMessage data COMM_EVENT_ANSWER' + JSON.stringify(data));
} else if (data === Constants.COMM_EVENT_HANGUP_FROM_VOICE) {
// 发布公共事件至语音通话示例:挂断
CommonEvent.publish(Constants.COMM_EVENT_HANGUP_FROM_HOST, this.publishCallback);
Logger.info(TAG, 'receiveMessage data COMM_EVENT_HANGUP' + JSON.stringify(data));
} else {
AppStorage.SetOrCreate('receiveMsg', data);
AppStorage.SetOrCreate('sendMsg', 'clear');
this.receiveMsg = data;
}
}
/**
* 跳转至CreateRoom页面
*/
pushCreateRoom(): void {
router.pushUrl({
url: 'pages/CreateRoom',
params: {
'userId': this.userId,
'socketType': this.socketType[this.selectAgreementIndex]
}
});
}
/**
* 跳转至Index页面
*/
pushIndex(): void {
router.pushUrl({
url: 'pages/NewIndex',
params: {
'userId': this.userId,
'socketType': this.socketType[this.selectAgreementIndex]
}
});
}
build() {
Column() {
Row() {
Text($r('app.string.EntryAbility_label'))
.fontSize(20)
.fontColor($r('app.color.COLOR_E6000000'))
.fontFamily($r('app.string.font_family'))
.margin({ left: 24, top: 14, bottom: 14 })
}
.width('100%')
.height(56)
.justifyContent(FlexAlign.Start)
Column() {
Text(this.userId === 1 ? $r('app.string.feng') : $r('app.string.wen'))
.width(104)
.height(104)
.fontSize(52)
.fontColor($r('app.color.COLOR_FFFFFF'))
.fontFamily($r('app.string.font_family'))
.textAlign(TextAlign.Center)
.backgroundColor($r('app.color.COLOR_33000000'))
.borderRadius(52)
Row({ space: 8 }) {
Text(this.userId === 1 ? $r('app.string.fengzi') : $r('app.string.wenzi'))
.fontSize(20)
.fontColor($r('app.color.COLOR_E6000000'))
.fontFamily($r('app.string.font_family'))
.textAlign(TextAlign.Center)
Image($r('app.media.ic_select'))
.width(12)
.height(24)
.objectFit(ImageFit.Contain)
}
.id('selectUser')
.width('100%')
.margin({ left: 8, top: 16, bottom: 4 })
.justifyContent(FlexAlign.Center)
.onClick(e => {
this.selectUserDialog.open();
})
Text($r('app.string.local_ip', localAddress))
.fontSize(16)
.fontColor($r('app.color.COLOR_99000000'))
.fontFamily($r('app.string.font_family_regular'))
.margin({ bottom: 42 })
Column() {
Row() {
Text(this.socketTypeResource[this.selectAgreementIndex])
.fontSize(16)
.fontColor($r('app.color.COLOR_FF000000'))
.fontFamily($r('app.string.font_family_regular'))
Blank()
Image($r('app.media.ic_select'))
.width(12)
.height(24)
.objectFit(ImageFit.Contain)
}
.id('selectAgreement')
.width('86%')
.height(55)
.onClick(e => {
this.selectAgreementDialog.open();
})
Divider()
.vertical(false)
.height(1)
.width('86%')
.backgroundColor($r('app.color.COLOR_33000000'))
Row() {
TextInput({ placeholder: $r('app.string.please_input_remote_address') })
.id('inputIp')
.width('100%')
.fontSize(16)
.fontColor($r('app.color.COLOR_E6000000'))
.fontFamily($r('app.string.font_family'))
.borderRadius(0)
.backgroundColor($r('app.color.COLOR_FFFFFF'))
.padding({ left: 0 })
.onChange((value: string) => {
oppositeAddress = value;
})
}
.width('86%')
.height(55)
.justifyContent(FlexAlign.Start)
Divider()
.vertical(false)
.height(1)
.width('86%')
.backgroundColor($r('app.color.COLOR_33000000'))
}
}
.width('100%')
.padding({ top: 38 })
.layoutWeight(1)
Column() {
Button($r('app.string.login'))
.id('login')
.width('86%')
.height(40)
.backgroundColor($r('app.color.COLOR_007DFF'))
.onClick(e => {
localPort = this.userId === 1 ? 8081 : 8082;
this.socketController = new SocketController(this.socketType[this.selectAgreementIndex], localAddress, localPort);
if (oppositeAddress === '' || !checkIp(oppositeAddress)) {
promptAction.showToast({ message: $r('app.string.input_ip'), duration: 1000, bottom: 64 });
} else {
if (this.socketType[this.selectAgreementIndex] != SocketType.UDP) {
// TCP或TLS点击连接 服务端或对端
this.connectSocketAndOnClose();
} else {
// UDP则直接订阅消息且发送连接成功信息
this.onUdpMessage();
}
promptAction.showToast({ message: $r('app.string.login_success'), duration: 1000, bottom: 64 });
this.pushCreateRoom();
}
})
}
.width('100%')
.height('30%')
.justifyContent(FlexAlign.Center)
}
.width('100%')
.height('100%')
}
}

View File

@ -0,0 +1,171 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 Logger from '../utils/Logger';
import type Socket from '../model/Socket';
const TAG = 'Socket TcpSocket';
export default class TcpSocket implements Socket {
private tcpSocket: socket.TCPSocket = null;
/**
* Socket
* @param localIp
* @param port
*/
async createSocket(localIp: string, port: number): Promise<boolean> {
Logger.info(`${TAG} tcp bind localIp: ${localIp}`);
try {
if (this.tcpSocket) {
this.tcpSocket.close();
this.tcpSocket = null;
}
this.tcpSocket = socket.constructTCPSocketInstance();
await this.tcpSocket.bind({
address: localIp,
port: port,
family: 1
});
Logger.info(`${TAG} tcp bind sucess`);
return true;
} catch (e) {
Logger.error(`${TAG} tcp bind error ${JSON.stringify(e)}}`);
}
return false;
}
/**
* Socket
* @param address
* @param port
*/
async connectSocket(address: string, port: number): Promise<boolean> {
Logger.info(`${TAG} tcp connectSocket address: ${address}`);
try {
if (!this.tcpSocket) {
return false;
}
if (await this.isConnected()) {
Logger.info(`${TAG} tcp connectSocket sucess`);
return true;
}
await this.tcpSocket.connect({
address: {
address: address,
port: port,
family: 1,
},
timeout: 6000,
});
await this.tcpSocket.setExtraOptions({});
Logger.info(`${TAG} tcp connectSocket sucess`);
return true;
} catch (e) {
Logger.error(`${TAG} tcp connectSocket error ${JSON.stringify(e)}}`);
}
return false;
}
/**
* Socket
*/
async closeSocket(): Promise<void> {
if (!this.tcpSocket) {
return;
}
await this.tcpSocket.close();
this.tcpSocket.off('connect');
this.tcpSocket.off('message');
this.tcpSocket = null;
}
/**
*
* @param data
*/
async sendData(data: string): Promise<void> {
if (!this.tcpSocket) {
return;
}
Logger.info(`${TAG} tcp sendData data ${JSON.stringify(data)}`);
try {
await this.tcpSocket.send({
data: data,
});
} catch (e) {
Logger.error(`${TAG} tcp sendData error ${JSON.stringify(e)}}`);
}
}
/**
*
*/
async isConnected(): Promise<boolean> {
if (!this.tcpSocket) {
return false;
}
try {
let state = await this.tcpSocket.getState();
if (state.isConnected) {
return true;
}
} catch (e) {
Logger.error(`${TAG} tcp getState error ${JSON.stringify(e)}}`);
}
return false;
}
/**
*
* @param callback
*/
setOnMessageReceivedListener(callback): void {
if (!this.tcpSocket) {
return;
}
this.tcpSocket.on('message', (data) => {
Logger.info(`${TAG} TCP data: ` + JSON.stringify(data));
let buffer = data.message;
callback(buffer);
});
}
/**
* TCP
* @param callback
*/
setOnCloseListener(callback): void {
Logger.info(`${TAG} TCP setOnCloseListener into`);
this.tcpSocket.on('close', () => {
Logger.info(`${TAG} TCP setOnCloseListener onClose`);
callback();
this.closeSocket();
});
this.tcpSocket.on('error', (data) => {
Logger.info(
`${TAG} TCP setOnCloseListener onClose` + JSON.stringify(data)
);
callback();
this.closeSocket();
});
}
}

View File

@ -0,0 +1,250 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 type Socket from '../model/Socket';
import Logger from '../utils/Logger';
const TAG = 'Socket TlsSocket';
/**
*
*/
const key =
'-----BEGIN RSA PRIVATE KEY-----\r\n' +
'MIIEowIBAAKCAQEAoDQ2S1RvsS5d9EZhVfWziC4+kWV2jqcJ3jnU9KSHx8y4sbis\r\n' +
'4EegBWFjHIXoHSV6JWBSet63LfYohZoamJkYncZ+W4aHcFVUDub/Oae7Zjsvpzlb\r\n' +
'isD9wD7x4xp0PEqOe2WYfGmMeF7Itp/4BYT8hysRRrU0kzNQiNZY2De9n27M6i0R\r\n' +
'if323rd7WKyGT/JO1Ap5r+I4D7YzoKSkhBfWCCvgzM8Tf7SeKqyFCXVtX6FlFnyO\r\n' +
'H1falcfTzgMf0uKXlrjyh00IXTLbgCVTAQl0/B1E9h1PKlzmTqPLNk0Lg+xQd8JG\r\n' +
'MjN6YTs59XMTEPtb/SCS63UIaGKCHEvL/tRGqwIDAQABAoIBAEjnec3OHwYCk78W\r\n' +
'SDVWGt1eLhr8kXdZjKQUrVSniyLhhytZX1KxRaEtMG1KgtbAt1VLAD3qHHJOEyK5\r\n' +
'fmDzSsCCE/6Sug/kxNwDQyJ5sk5ToNv4vi/mo5NZEbrHzx2g0DZXhY0Ye1RLzgtN\r\n' +
'wRDB9CmO7829qK9UKtIauVOsCh8DFOnkk5RSqrkHjfQMJhkJnFqLnHss4PxDlrB0\r\n' +
'dTGmMTl9D41rVI4NXEvso3Io/aZt9OEvh3vCfZyvDWFdnoNCaGIjJn2hSE+WqvYP\r\n' +
'7aPF+pYA3u4mv0rvrXWrXb09zaD/WPpv4bsa5UnfXcM6q1vEi6iG9NThHnN1GW3h\r\n' +
'dwZLqAECgYEAzDj7fAaJ9SNWxTvJF+y5yZMxw5ij4J4Z/jheHA6xA7llRjqKf/F1\r\n' +
'l4on67YStA34G33lUfI29N6DUK1qPeXy6duredKug5H49sZ1nFYBjRj3A7OzyXs9\r\n' +
'Jz+1sWPFiOg/SieothzJPYfhOCnmT+2g4wqJnx2RZziCWvs8xfy3cqkCgYEAyNI2\r\n' +
'ACZInaYSF++d/OaoIYJtHU+/MtVHcfBrkOvT2BQlkJfOLFVfEwuQ09esegK6oemV\r\n' +
'POdGT3lbOV9ncQZ5MUMxp6s2o7AKshNGeuTRzu0Tq/ryeJ9Wv0l/g3/jtW8RDyfr\r\n' +
'uXc6two0T4G59Xx8vlQ3HMRcmMUh9wjuS29pVzMCgYB1YcdCyWtgSFimcy+pnbpH\r\n' +
'31/6IfEHuTgNhA6rp9Ic5uIMFLfoxDunx5QcioGDlofmHV3C1Sq7Ym4t4A8x1srh\r\n' +
'mg6crfj6zNtB1F5jZpfrps7MBO0wwLIsrhTcChHPGFbQY604R6FvyXstUf/8LwSL\r\n' +
'm1Hw8AjUQInEi6Wp0xEKSQKBgHKUxizlqw8QH0i69kjDDnE4KqvLwYrud/pBtA4X\r\n' +
'EGM8c4aBpyhSeZGzSqokZ4uiHPye+wCp8MsNsggVMD8j82gxL5a8MMvKeP/L0a3y\r\n' +
'+ub3C/9XiptaRrOT6waLpM/zlCcynuPObJjdYKVwnjq8EazC/kMSosajy9JYrSYr\r\n' +
'Zk5TAoGBAL2gxQyU2swvRemOA5PnLviqf++TUZdbCKlW7x1Xoj1xJVFH9Ql0ocGD\r\n' +
'cgEiYCSS4eaEbUnsMtAStUwXBH9DPVQRUOpxCvAuMFnP5OeUEAZ6rKrp5E3iuDz2\r\n' +
'dbxwe2wphU8yH+wC1xWd4uFKK2MRUH+3XAR02MmzGaXTarDHO1fN\r\n' +
'-----END RSA PRIVATE KEY-----\r\n';
const cert =
'-----BEGIN CERTIFICATE-----\r\n' +
'MIIC+DCCAeACFE+6YAe2yhFoBO5u1tsROSqQcaqYMA0GCSqGSIb3DQEBBQUAMDMx\r\n' +
'MTAvBgNVBAoMKHd3dy50ZXN0LmNvbSBEb2RneSBDZXJ0aWZpY2F0ZSBBdXRob3Jp\r\n' +
'dHkwHhcNMjMwNDE0MDM1NzQzWhcNMzYxMjIxMDM1NzQzWjA+MSgwJgYDVQQKDB93\r\n' +
'd3cudGVzdC5jb20gRGV2aWNlIENlcnRpZmljYXRlMRIwEAYDVQQDDAkxMjcuMC4w\r\n' +
'LjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgNDZLVG+xLl30RmFV\r\n' +
'9bOILj6RZXaOpwneOdT0pIfHzLixuKzgR6AFYWMchegdJXolYFJ63rct9iiFmhqY\r\n' +
'mRidxn5bhodwVVQO5v85p7tmOy+nOVuKwP3APvHjGnQ8So57ZZh8aYx4Xsi2n/gF\r\n' +
'hPyHKxFGtTSTM1CI1ljYN72fbszqLRGJ/fbet3tYrIZP8k7UCnmv4jgPtjOgpKSE\r\n' +
'F9YIK+DMzxN/tJ4qrIUJdW1foWUWfI4fV9qVx9POAx/S4peWuPKHTQhdMtuAJVMB\r\n' +
'CXT8HUT2HU8qXOZOo8s2TQuD7FB3wkYyM3phOzn1cxMQ+1v9IJLrdQhoYoIcS8v+\r\n' +
'1EarAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAAcEuP2YbnjMb69hPtvKlrDxW71K\r\n' +
'O9PJnDc40eZ6ZFnepTgCmyZrBepgCVylBwCh2OMze9ovwrw8gnNI5MVHijb1EFh6\r\n' +
'j2JPTQ+giG9SVxrpyrbNyRydKeBUz8RqhXHRlS6RfDReL4sG2KDkYO3XHaaU2Bu0\r\n' +
'ErKqDXbBcFkCuUanM2JWL9F5i0brqdGQ8+dmCac8Tz2ABkRF+ZlpqDg1IO3OHryD\r\n' +
'hIOxG8Ehshk0rpYBQv1McR4ezancG/eswxrFKu0L3SxFeFA7gz+Rv7Fdeb67Uupr\r\n' +
'VY4NzyUuHYBoJMjJMsUH7sNdKUNQz66G89jG6K/kPOaCpVzmK6miJiX2oPg=\r\n' +
'-----END CERTIFICATE-----\r\n';
const ca =
'-----BEGIN CERTIFICATE-----\r\n' +
'MIIC7TCCAdUCFC8NFFp/DPsC5oZ52erAUBE3SVxBMA0GCSqGSIb3DQEBBQUAMDMx\r\n' +
'MTAvBgNVBAoMKHd3dy50ZXN0LmNvbSBEb2RneSBDZXJ0aWZpY2F0ZSBBdXRob3Jp\r\n' +
'dHkwHhcNMjMwNDE0MDM1NzQzWhcNMzYxMjIxMDM1NzQzWjAzMTEwLwYDVQQKDCh3\r\n' +
'd3cudGVzdC5jb20gRG9kZ3kgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MIIBIjANBgkq\r\n' +
'hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv8IEpaZ6WaHbuNHzQ5gr5XH0ozRxrnF3\r\n' +
'2uKxtu/YkfEBQriM8ZlT2lTBjEy748lxPx3SS8OkPu8opE1E16UlSSGtI0gQAPUI\r\n' +
'3Jkdx0z3xj9iKSp14J+3ZZ61i+K3fDBkdZSEk+0KjinzjERvsgkkzDk4ZQCORwcV\r\n' +
'jsUi+M3dqQm8ARELQgTRkTBfof+F2vOR0Ufc5m14z50TvhnMRUw+fEcDe5cnKok0\r\n' +
'C1QUgwC9m6Q9fzS1JMdPYMdFZQJ12ACr1B7g5m/i6xCxuqBxmtP/2DiqhsSIMQy4\r\n' +
'XDU4CyPneoWQMz4nObJymxDvoAe86foFfPWzM6sAEZeGxJUooI19dQIDAQABMA0G\r\n' +
'CSqGSIb3DQEBBQUAA4IBAQAQ1vZ9QWS/YS5cVuv5wvXxHu2M2LKWOzDvb7FTn46q\r\n' +
'irTOCVx0H1su6wktGMuFsgYNkKdd2xTiJeggDqHGMNkTul9XbY5VM0sWJaXoYrPQ\r\n' +
'55BylXSl4miGIkVWTYHZNgeSBzE1vIRWuvXgo0DGWJ8a1NpUxYG0DCkGY1UPipo9\r\n' +
'YTjnK8Rtx6fsyg3Wn3G5HG+8xMkOGsamCYTee8ASxHbdziS8U4VLAAhZgvyPI1ZK\r\n' +
'uhLreTaRrguLyz4qpX/JrLhkrOkwZN+ueHToQGIOV1pleEXRNO0y9JZ4tZCkKbsH\r\n' +
'G4o77ax02PiiQ0R5Np1ju5t+s63NXV/bURIjhRafb2B5\r\n' +
'-----END CERTIFICATE-----\r\n';
export default class TlsSocket implements Socket {
private tlsSocket: socket.TLSSocket = null;
/**
* Socket
* @param localIp
* @param port
*/
async createSocket(localIp: string, port: number): Promise<boolean> {
Logger.info(`${TAG} tls bind localIp: ${localIp}`);
try {
if (this.tlsSocket) {
await this.closeSocket();
}
this.tlsSocket = socket.constructTLSSocketInstance();
await this.tlsSocket.bind({
address: localIp,
port: port,
family: 1,
});
Logger.info(`${TAG} tls bind sucess`);
return true;
} catch (e) {
Logger.error(`${TAG} tls bind error ${JSON.stringify(e)}}`);
}
return false;
}
/**
* Socket
* @param address
* @param port
*/
async connectSocket(address: string, port: number): Promise<boolean> {
Logger.info(`${TAG} tls connectSocket address: ${address}`);
try {
if (!this.tlsSocket) {
return false;
}
if (await this.isConnected()) {
Logger.info(`${TAG} tls connectSocket sucess`);
return true;
}
let options = {
address: {
address: address,
port: port,
family: 1,
},
secureOptions: {
key: key,
cert: cert,
ca: [ca],
protocols: [socket.Protocol.TLSv12],
cipherSuite: 'ALL:@SECLEVEL=0',
},
};
await this.tlsSocket.connect(options);
await this.tlsSocket.setExtraOptions({});
Logger.info(`${TAG} tls connectSocket sucess`);
return true;
} catch (e) {
Logger.error(`${TAG} tls connectSocket error ${JSON.stringify(e)}}`);
}
return false;
}
/**
* Socket
*/
async closeSocket(): Promise<void> {
if (!this.tlsSocket) {
return;
}
await this.tlsSocket.close();
this.tlsSocket.off('connect');
this.tlsSocket.off('message');
this.tlsSocket = null;
}
/**
*
* @param data
*/
async sendData(data: string): Promise<void> {
if (!this.tlsSocket) {
return;
}
Logger.info(`${TAG} tls sendData data ${JSON.stringify(data)}`);
try {
await this.tlsSocket.send(data);
} catch (e) {
Logger.error(`${TAG} tls sendData error ${JSON.stringify(e)}}`);
}
}
/**
*
*/
async isConnected(): Promise<boolean> {
if (!this.tlsSocket) {
return false;
}
try {
let state = await this.tlsSocket.getState();
if (state.isConnected) {
return true;
}
} catch (e) {
Logger.error(`${TAG} tls getState error ${JSON.stringify(e)}}`);
}
return false;
}
/**
*
* @param callback
*/
setOnMessageReceivedListener(callback): void {
if (!this.tlsSocket) {
return;
}
this.tlsSocket.on('message', (data) => {
Logger.info(`${TAG} TLS data: ` + JSON.stringify(data));
let buffer = data.message;
callback(buffer);
});
}
/**
* TLS
* @param callback
*/
setOnCloseListener(callback): void {
Logger.info(`${TAG} TLS setOnCloseListener into`);
this.tlsSocket.on('close', () => {
Logger.info(`${TAG} TLS setOnCloseListener onClose`);
callback();
this.closeSocket();
});
this.tlsSocket.on('error', (data) => {
Logger.info(
`${TAG} TLS setOnCloseListener onClose` + JSON.stringify(data)
);
callback();
this.closeSocket();
});
}
}

View File

@ -0,0 +1,127 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 type Socket from '../model/Socket';
import Logger from '../utils/Logger';
const TAG = 'Socket UdpSocket';
export default class UdpSocket implements Socket {
private udpSocket: socket.UDPSocket = null;
/**
* Socket
* @param localIp
* @param port
*/
async createSocket(localIp: string, port: number): Promise<boolean> {
Logger.info(`${TAG} tls bind localIp: ${localIp}`);
try {
if (this.udpSocket) {
await this.closeSocket();
}
this.udpSocket = socket.constructUDPSocketInstance();
await this.udpSocket.bind({
address: localIp,
port: port,
family: 1,
});
Logger.info(`${TAG} tls bind sucess`);
return true;
} catch (e) {
Logger.error(`${TAG} tls bind error ${JSON.stringify(e)}}`);
}
return false;
}
/**
* Socket
*/
async closeSocket(): Promise<void> {
if (!this.udpSocket) {
return;
}
await this.udpSocket.close();
this.udpSocket.off('message');
this.udpSocket = null;
}
/**
*
* @param data
* @param address
* @param port
*/
async sendData(data: string, address: string, port: number): Promise<void> {
let options = {
address: {
address: address,
port: port,
family: 1,
},
data: data,
};
if (!this.udpSocket) {
return;
}
Logger.info(`${TAG} tls sendData data ${JSON.stringify(data)}`);
try {
await this.udpSocket.send(options);
} catch (e) {
Logger.error(`${TAG} tls sendData error ${JSON.stringify(e)}}`);
}
}
/**
*
* @param callback
*/
setOnMessageReceivedListener(callback): void {
if (!this.udpSocket) {
return;
}
this.udpSocket.on('message', (data) => {
Logger.info(`${TAG} TLS data: ` + JSON.stringify(data));
let buffer = data.message;
callback(buffer);
});
}
/**
*
* @param address
* @param port
*/
async connectSocket(address: string, port: number): Promise<boolean> {
return false;
}
/**
*
*/
async isConnected(): Promise<boolean> {
return false;
}
/**
*
*/
setOnCloseListener(): void {
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 Logger from './Logger';
const TAG = 'Socket AbilityUtil';
export default function startAbility(want) {
try {
globalThis.abilityContext.startAbility(want, () => {
Logger.info(TAG, 'startAbility callback ');
});
} catch (error) {
Logger.info(TAG, `callback startAbility failed, error code: ${error.code}, message: ${error.message}.`);
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2023 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 resolveIP(ip: number): string {
if (ip < 0 || ip > 0xFFFFFFFF) {
throw ('The number is not normal!');
}
return (ip >>> 24) + '.' + (ip >> 16 & 0xFF) + '.' + (ip >> 8 & 0xFF) + '.' + (ip & 0xFF);
}
export function checkIp(ip: string): boolean {
let ipRegex = /^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$/;
return ipRegex.test(ip);
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2023 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 '@ohos.hilog';
class Logger {
private domain: number;
private prefix: string;
private format: string = '%{public}s, %{public}s';
constructor(prefix: string) {
this.prefix = prefix;
this.domain = 0xff00;
}
debug(...args: string[]): void {
hilog.debug(this.domain, this.prefix, this.format, args);
}
info(...args: string[]): void {
hilog.info(this.domain, this.prefix, this.format, args);
}
warn(...args: string[]): void {
hilog.warn(this.domain, this.prefix, this.format, args);
}
error(...args: string[]): void {
hilog.error(this.domain, this.prefix, this.format, args);
}
}
export default new Logger('[Sample_Socket]');

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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.
*/
const TAG = 'ResourceUtil';
import Logger from './Logger';
export default function getStringValue(id: number): string {
let result = '';
try {
globalThis.abilityContext.resourceManager.getStringValue(id, (error, value) => {
if (error !== null) {
Logger.info(TAG, 'error is ' + error);
} else {
Logger.info(TAG, 'value is ' + value);
result = value;
}
}
);
} catch (error) {
Logger.info(TAG, `callback getStringValue failed, error code: ${error.code}, message: ${error.message}.`);
}
return result;
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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.ts",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"minWindowWidth": 400,
"minWindowHeight": 600,
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:internet_permission",
"usedScene": {
"abilities": [
"ohos.samples.socket.EntryAbility"
],
"when": "always"
}
},
{
"name": "ohos.permission.GET_WIFI_INFO",
"reason": "$string:get_wifi_info_permission",
"usedScene": {
"abilities": [
"ohos.samples.socket.EntryAbility"
],
"when": "always"
}
}
]
}
}

View File

@ -0,0 +1,52 @@
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
},
{
"name": "COLOR_FFFFFF",
"value": "#FFFFFF"
},
{
"name": "COLOR_000000",
"value": "#000000"
},
{
"name": "COLOR_33000000",
"value": "#33000000"
},
{
"name": "COLOR_5BA854",
"value": "#5BA854"
},
{
"name": "COLOR_E6000000",
"value": "#e6000000"
},
{
"name": "COLOR_99000000",
"value": "#99000000"
},
{
"name": "COLOR_007DFF",
"value": "#007DFF"
},
{
"name": "COLOR_66000000",
"value": "#66000000"
},
{
"name": "COLOR_0D000000",
"value": "#0d000000"
},
{
"name": "COLOR_FF000000",
"value": "#ff000000"
},
{
"name": "COLOR_F1F3F5",
"value": "#F1F3F5"
}
]
}

View File

@ -0,0 +1,156 @@
{
"string": [
{
"name": "module_desc",
"value": "module description"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "Socket communication"
},
{
"name": "internet_permission",
"value": "get internet permission"
},
{
"name": "get_wifi_info_permission",
"value": "get internet permission"
},
{
"name": "confirm",
"value": "confirm"
},
{
"name": "input_roomNumber",
"value": "input roomNumber"
},
{
"name": "cancel",
"value": "cancel"
},
{
"name": "create_room",
"value": "create room"
},
{
"name": "send_message",
"value": "send message"
},
{
"name": "classmate_fengzi",
"value": "classmate fengzi"
},
{
"name": "classmate_wenzi",
"value": "classmate wenzi"
},
{
"name": "online",
"value": "online"
},
{
"name": "offline",
"value": "offline"
},
{
"name": "udp",
"value": "UDP data transfer"
},
{
"name": "tcp",
"value": "TCP data transfer"
},
{
"name": "tls",
"value": "TLS security transfer"
},
{
"name": "select",
"value": "select agreement"
},
{
"name": "select_user",
"value": "please select user"
},
{
"name": "input_ip",
"value": "please input correct ip"
},
{
"name": "user_name",
"value": "username:\"%s\""
},
{
"name": "confirm_code",
"value": "confirm code:\"%s\""
},
{
"name": "local_ip",
"value": "local ip:\"%s\""
},
{
"name": "user",
"value": "user:\"%s\""
},
{
"name": "fengzi",
"value": "fengzi"
},
{
"name": "wenzi",
"value": "wenzi"
},
{
"name": "input_room",
"value": "please input room number"
},
{
"name": "font_family",
"value": "HarmonyOS Sans SC-Medium"
},
{
"name": "login",
"value": "login"
},
{
"name": "feng",
"value": "feng"
},
{
"name": "wen",
"value": "wen"
},
{
"name": "please_input_remote_address",
"value": "please input remote address"
},
{
"name": "user_select",
"value": "user select"
},
{
"name": "font_family_regular",
"value": "HarmonyOS Sans SC-Regular"
},
{
"name": "not_room",
"value": "not room"
},
{
"name": "login_success",
"value": "login success"
},
{
"name": "invitation",
"value": "into room invitation"
},
{
"name": "invitation_detail",
"value": "opposite invitation you into %s room"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24" version="1.1">
<title>Public/ic_public_add_norm</title>
<defs>
<path d="M12,1 C18.0751322,1 23,5.92486775 23,12 C23,18.0751322 18.0751322,23 12,23 C5.92486775,23 1,18.0751322 1,12 C1,5.92486775 5.92486775,1 12,1 Z M12,2.5 C6.75329488,2.5 2.5,6.75329488 2.5,12 C2.5,17.2467051 6.75329488,21.5 12,21.5 C17.2467051,21.5 21.5,17.2467051 21.5,12 C21.5,6.75329488 17.2467051,2.5 12,2.5 Z M12.75,17.25 C12.75,17.6642136 12.4142136,18 12,18 C11.5857864,18 11.25,17.6642136 11.25,17.25 L11.25,12.749 L6.75,12.75 C6.33578644,12.75 6,12.4142136 6,12 C6,11.5857864 6.33578644,11.25 6.75,11.25 L11.25,11.249 L11.25,6.75 C11.25,6.33578644 11.5857864,6 12,6 C12.4142136,6 12.75,6.33578644 12.75,6.75 L12.75,17.25 Z M17.25,11.25 C17.6642136,11.25 18,11.5857864 18,12 C18,12.4142136 17.6642136,12.75 17.25,12.75 L13.75,12.75 L13.75,11.25 L17.25,11.25 Z" id="_path-1"/>
</defs>
<g id="_Public/ic_public_add_norm" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<mask id="_mask-2" fill="white">
<use xlink:href="#_path-1"/>
</mask>
<use id="_形状结合" fill="#000000" fill-rule="nonzero" xlink:href="#_path-1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24" version="1.1">
<title>Public/ic_public_phone_filled</title>
<defs>
<path d="M2.10287352,6.89676965 C1.78889745,5.53060044 2.20441737,4.09964255 3.20178976,3.11036909 C3.75560866,2.4884392 4.27983656,2.13012867 4.77447347,2.03543751 C5.51642882,1.89340077 6.27613877,2.18619581 6.74803733,2.77272034 L8.35355762,4.75737645 C8.43389824,4.85572364 8.49683558,4.93227089 8.53513097,4.97801383 L8.57021988,5.01914357 C9.17872549,5.70466341 9.16280649,6.73467275 8.54281368,7.40067341 L8.49857851,7.44653906 L7.41461351,8.46914131 C6.98383087,8.89867787 6.90689427,9.56681123 7.22896893,10.0823174 C7.85356938,11.087123 9.02395518,12.4418965 10.3020442,13.6978864 C11.5581035,14.9760448 12.912877,16.1464306 13.9176826,16.7710311 C14.4331888,17.0931057 15.1013221,17.0161691 15.5308587,16.5853865 L16.5534609,15.5014215 L16.5993266,15.4571863 C17.2473272,14.8539501 18.2399263,14.8225762 18.9245328,15.3818098 L19.0970635,15.5271144 C19.489809,15.8507448 20.7189584,16.8429839 21.2272797,17.2519627 C21.8138042,17.7238612 22.1065992,18.4835712 21.9645625,19.2255265 C21.8698713,19.7201634 21.5115608,20.2443913 20.8896309,20.7982102 C19.9003574,21.7955826 18.4693996,22.2111026 17.1032304,21.8971265 C14.1939187,21.2212397 10.7536044,18.9411009 7.89180085,16.1082685 C5.05889912,13.2463956 2.77876032,9.8060813 2.10287352,6.89676965 Z" id="_path-1"/>
</defs>
<g id="_Public/ic_public_phone_filled" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<mask id="_mask-2" fill="white">
<use xlink:href="#_path-1"/>
</mask>
<use id="_路径" fill="#000000" xlink:href="#_path-1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24" version="1.1">
<title>Public/ic_public_back</title>
<defs>
<path d="M5.31079777,13.7499686 L11.2803301,19.7196699 L11.3140714,19.7556673 C11.5727547,20.0502619 11.5615076,20.4991526 11.2803301,20.7803301 C10.9991526,21.0615076 10.5502619,21.0727547 10.2556673,20.8140714 L10.2196699,20.7803301 L3.18929777,13.7499686 L5.31079777,13.7499686 Z M11.2803301,3.21966991 C11.5615076,3.5008474 11.5727547,3.94973814 11.3140714,4.24433269 L11.2803301,4.28033009 L4.3105,11.25 L21,11.25 C21.3994202,11.25 21.7259152,11.56223 21.7487268,11.9559318 L21.75,12 C21.75,12.3994202 21.43777,12.7259152 21.0440682,12.7487268 L21,12.75 L3.10355339,12.75 C2.8383369,12.75 2.58398299,12.6446432 2.39644661,12.4571068 C2.01893979,12.0796 2.00635623,11.4753589 2.35869593,11.0827365 L2.39644661,11.0428932 L10.2196699,3.21966991 C10.5125631,2.9267767 10.9874369,2.9267767 11.2803301,3.21966991 Z" id="_path-1"/>
</defs>
<g id="_Public/ic_public_back" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<mask id="_mask-2" fill="white">
<use xlink:href="#_path-1"/>
</mask>
<use id="_形状结合" fill="#000000" fill-rule="nonzero" xlink:href="#_path-1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24" version="1.1">
<title>ic_contacts_out_filled</title>
<defs>
<path d="M4.78336169,2.03562126 C5.52775989,1.892957 6.28985811,2.18672133 6.76352073,2.77524245 L6.76352073,2.77524245 L8.29669184,4.67677106 C8.39217225,4.79586471 8.48939774,4.91495836 8.59160914,5.02908978 C9.216411,5.73275452 9.18499521,6.79831407 8.51981186,7.46430681 L8.51981186,7.46430681 L7.43213291,8.49024898 C7.00005324,8.92127336 6.92292245,9.59159993 7.24590871,10.1086821 C8.46471742,12.0692613 11.7546783,15.3579828 13.8826699,16.6628026 C14.4012435,16.9798455 15.0707032,16.9010007 15.5006017,16.4722527 L15.5006017,16.4722527 L16.4928002,15.5080904 C17.1479457,14.8600751 18.1909401,14.8146111 18.9005021,15.4031391 C19.0164248,15.4999027 19.1325968,15.5964182 19.2532561,15.6872271 L19.2532561,15.6872271 L21.4054295,17.3091338 C22.1981902,18.1027003 22.1981902,19.3844836 21.4054295,20.1780502 L21.4054295,20.1780502 L20.7821892,20.7983296 C19.8136738,21.7622438 18.4245958,22.1907328 17.0818869,21.920291 C11.2865001,20.7529251 3.46209228,12.7627337 2.10317915,6.91275434 C1.78827737,5.54196923 2.20495666,4.10641241 3.20581587,3.11391502 L3.20581587,3.11391502 L3.73183067,2.59039918 C4.01853009,2.30473568 4.38489368,2.11144568 4.78336169,2.03562126 Z M19.1439232,3 C19.6962079,3 20.1439232,3.44771525 20.1439232,4 C20.1439232,4.22101374 20.0707587,4.43448403 19.9378425,4.60802951 L19.8510299,4.70710678 L13.2803301,11.2778066 C12.9874369,11.5706998 12.5125631,11.5706998 12.2196699,11.2778066 C11.9511845,11.0093212 11.9288107,10.5879215 12.1525486,10.2939504 L12.2196699,10.2171465 L17.9364002,4.49996014 L12.9163691,4.5 C12.5398113,4.5 12.2280693,4.22249055 12.174501,3.8608295 L12.1663691,3.75 C12.1663691,3.37344222 12.4438785,3.06170024 12.8055396,3.00813193 L12.9163691,3 L19.1439232,3 Z M20.4968671,5.47423973 L20.4972486,10.5808795 C20.4972486,10.9950931 20.1614621,11.3308795 19.7472486,11.3308795 C19.3706908,11.3308795 19.0589488,11.05337 19.0053805,10.691709 L18.9972486,10.5808795 L18.9963866,6.97472032 L20.4968671,5.47423973 Z" id="_path-1"/>
</defs>
<g id="_ic_contacts_out_filled" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<mask id="_mask-2" fill="white">
<use xlink:href="#_path-1"/>
</mask>
<use id="_形状结合" fill="#000000" xlink:href="#_path-1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24" version="1.1">
<title>ic_details voWiFI</title>
<g id="_ic_details-voWiFI" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M4.78336169,2.03562126 C5.52775989,1.892957 6.28985811,2.18672133 6.76352073,2.77524245 L6.76352073,2.77524245 L8.29669184,4.67677106 C8.39217225,4.79586471 8.48939774,4.91495836 8.59160914,5.02908978 C9.216411,5.73275452 9.18499521,6.79831407 8.51981186,7.46430681 L8.51981186,7.46430681 L7.43213291,8.49024898 C7.00005324,8.92127336 6.92292245,9.59159993 7.24590871,10.1086821 C8.46471742,12.0692613 11.7546783,15.3579828 13.8826699,16.6628026 C14.4012435,16.9798455 15.0707032,16.9010007 15.5006017,16.4722527 L15.5006017,16.4722527 L16.4928002,15.5080904 C17.1479457,14.8600751 18.1909401,14.8146111 18.9005021,15.4031391 C19.0164248,15.4999027 19.1325968,15.5964182 19.2532561,15.6872271 L19.2532561,15.6872271 L21.4054295,17.3091338 C22.1981902,18.1027003 22.1981902,19.3844836 21.4054295,20.1780502 L21.4054295,20.1780502 L20.7821892,20.7983296 C19.8136738,21.7622438 18.4245958,22.1907328 17.0818869,21.920291 C11.2865001,20.7529251 3.46209228,12.7627337 2.10317915,6.91275434 C1.78827737,5.54196923 2.20495666,4.10641241 3.20581587,3.11391502 L3.20581587,3.11391502 L3.73183067,2.59039918 C4.01853009,2.30473568 4.38489368,2.11144568 4.78336169,2.03562126 Z M20.8,2 C21.4627417,2 22,2.5372583 22,3.2 L22,6.8 C22,7.4627417 21.4627417,8 20.8,8 L12.2,8 C11.5372583,8 11,7.4627417 11,6.8 L11,3.2 C11,2.5372583 11.5372583,2 12.2,2 L20.8,2 Z M12.4549896,3.06836429 L11.7865629,3.06836429 L12.4795629,7.02656429 L13.0489896,7.02656429 L13.3705629,4.88276429 L13.6924896,7.02656429 L14.2567896,7.02656429 L14.9299896,3.06836429 L14.3112396,3.06836429 L13.9843629,5.48756429 L13.6380396,3.06836429 L13.1232396,3.06836429 L12.8014896,5.48756429 L12.4549896,3.06836429 Z M15.9517821,4.22859286 L15.2764221,4.22859286 L15.2764221,7.02579286 L15.9517821,7.02579286 L15.9517821,4.22859286 Z M21.1635586,4.22859286 L20.4881986,4.22859286 L20.4881986,7.02579286 L21.1635586,7.02579286 L21.1635586,4.22859286 Z M20.1419429,3.07222143 L18.5133929,3.07222143 L18.5133929,7.02502143 L19.2111661,7.02502143 L19.2111661,5.45381429 L20.0528429,5.45381429 L20.0528429,4.77322143 L19.2111661,4.77322143 L19.2111661,3.76882143 L20.1419429,3.76882143 L20.1419429,3.07222143 Z M18.1073593,4.73079286 L16.3600093,4.73079286 L16.3600093,5.45507857 L18.1073593,5.45507857 L18.1073593,4.73079286 Z M15.6141021,2.97579286 C15.5032221,2.97579286 15.4081821,3.01822143 15.3293421,3.10269286 C15.2503221,3.18735714 15.2109021,3.28899286 15.2109021,3.40779286 C15.2109021,3.52659286 15.2503221,3.62842143 15.3293421,3.71289286 C15.4081821,3.79755714 15.5032221,3.83979286 15.6141021,3.83979286 C15.7248021,3.83979286 15.8198421,3.79755714 15.8988621,3.71289286 C15.9777021,3.62842143 16.0173021,3.52659286 16.0173021,3.40779286 C16.0173021,3.28899286 15.9777021,3.18735714 15.8988621,3.10269286 C15.8198421,3.01822143 15.7248021,2.97579286 15.6141021,2.97579286 Z M20.8258786,2.97579286 C20.7149986,2.97579286 20.6199586,3.01822143 20.5411186,3.10269286 C20.4620986,3.18735714 20.4226786,3.28899286 20.4226786,3.40779286 C20.4226786,3.52659286 20.4620986,3.62842143 20.5411186,3.71289286 C20.6199586,3.79755714 20.7149986,3.83979286 20.8258786,3.83979286 C20.9365786,3.83979286 21.0316186,3.79755714 21.1106386,3.71289286 C21.1894786,3.62842143 21.2290786,3.52659286 21.2290786,3.40779286 C21.2290786,3.28899286 21.1894786,3.18735714 21.1106386,3.10269286 C21.0316186,3.01822143 20.9365786,2.97579286 20.8258786,2.97579286 Z" id="_形状结合" fill="#000000"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24" version="1.1">
<title>ic_Encryption dialback_filled</title>
<defs>
<path d="M4.78336169,2.03562126 C5.52775989,1.892957 6.28985811,2.18672133 6.76352073,2.77524245 L6.76352073,2.77524245 L8.29669184,4.67677106 C8.39217225,4.79586471 8.48939774,4.91495836 8.59160914,5.02908978 C9.216411,5.73275452 9.18499521,6.79831407 8.51981186,7.46430681 L8.51981186,7.46430681 L7.43213291,8.49024898 C7.00005324,8.92127336 6.92292245,9.59159993 7.24590871,10.1086821 C8.46471742,12.0692613 11.7546783,15.3579828 13.8826699,16.6628026 C14.4012435,16.9798455 15.0707032,16.9010007 15.5006017,16.4722527 L15.5006017,16.4722527 L16.4928002,15.5080904 C17.1479457,14.8600751 18.1909401,14.8146111 18.9005021,15.4031391 C19.0164248,15.4999027 19.1325968,15.5964182 19.2532561,15.6872271 L19.2532561,15.6872271 L21.4054295,17.3091338 C22.1981902,18.1027003 22.1981902,19.3844836 21.4054295,20.1780502 L21.4054295,20.1780502 L20.7821892,20.7983296 C19.8136738,21.7622438 18.4245958,22.1907328 17.0818869,21.920291 C11.2865001,20.7529251 3.46209228,12.7627337 2.10317915,6.91275434 C1.78827737,5.54196923 2.20495666,4.10641241 3.20581587,3.11391502 L3.20581587,3.11391502 L3.73183067,2.59039918 C4.01853009,2.30473568 4.38489368,2.11144568 4.78336169,2.03562126 Z M16.9753255,2 C17.1341439,2 17.2918568,2.0272332 17.4422891,2.08065086 L17.5900658,2.14275783 L20.1267127,3.37927281 C20.616557,3.61832686 20.9421573,4.11850843 20.9930224,4.68140904 L21,4.83633333 L21,6.69088889 C21,6.81032572 20.9955562,6.93096256 20.9870834,7.06461099 C20.8381572,9.36917542 19.3324306,11.4177663 17.3352165,11.9524332 C17.2174595,11.9839784 17.0966462,12 16.9752235,12 C16.8536844,12 16.7325143,11.9838932 16.6157823,11.9524717 C14.6952452,11.4383317 13.2680394,9.52040584 13.0369911,7.3011312 L13.016845,7.06224888 L13.0049316,6.87199627 L13.001149,6.78026366 L13.001149,6.78026366 L13,6.69088889 L13,4.842 C13,4.26777878 13.2895024,3.74027781 13.7420376,3.45929399 L13.8694034,3.38892415 L16.3606771,2.14276273 C16.5541361,2.04845623 16.763627,2 16.9753255,2 Z M17.0000269,7.00206346 L13.9043267,7.00215123 C14.0297869,8.87572255 15.2200107,10.5555701 16.8430963,10.9827778 C16.8863407,10.9942222 16.9306222,11 16.9748,11 C16.9834121,11 16.9920202,10.9997825 17.0006191,10.9993483 C16.997607,11.000615 16.9959041,10.8840529 16.9949182,10.6503136 L16.9941115,10.3379227 C16.9940555,10.29772 16.9940107,10.2556863 16.9939771,10.2118217 L16.9939771,9.76760658 L16.9940359,9.68395852 L16.9940359,9.68395852 L16.9949182,9.02152464 C16.9959041,8.46554809 16.997607,7.79239436 17.0000269,7.00206346 Z M16.9749037,3 L16.9593808,3.00103614 C16.9768921,3.00226716 16.9905941,3.00415627 17.0000269,3.00415627 L17.0000269,7.00206346 L20.099,7.002 C20.1034889,6.94544444 20.1062111,6.89469444 20.1081167,6.84383333 L20.1111111,6.69088889 L20.1111111,4.83633333 C20.1111111,4.633 20.0157968,4.44615646 19.8621315,4.33625753 L19.7806074,4.288 L17.2009778,3.05166667 C17.1290074,3.01711111 17.0519556,3 16.9749037,3 Z" id="_path-1"/>
</defs>
<g id="_ic_Encryption-dialback_filled" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<mask id="_mask-2" fill="white">
<use xlink:href="#_path-1"/>
</mask>
<use id="_形状结合" fill="#000000" xlink:href="#_path-1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,7 @@
{
"src": [
"pages/NewLogin",
"pages/NewIndex",
"pages/CreateRoom"
]
}

View File

@ -0,0 +1,156 @@
{
"string": [
{
"name": "module_desc",
"value": "module description"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "Socket communication"
},
{
"name": "internet_permission",
"value": "get internet permission"
},
{
"name": "get_wifi_info_permission",
"value": "get internet permission"
},
{
"name": "confirm",
"value": "confirm"
},
{
"name": "input_roomNumber",
"value": "input roomNumber"
},
{
"name": "cancel",
"value": "cancel"
},
{
"name": "create_room",
"value": "create room"
},
{
"name": "send_message",
"value": "send message"
},
{
"name": "classmate_fengzi",
"value": "classmate fengzi"
},
{
"name": "classmate_wenzi",
"value": "classmate wenzi"
},
{
"name": "online",
"value": "online"
},
{
"name": "offline",
"value": "offline"
},
{
"name": "udp",
"value": "UDP data transfer"
},
{
"name": "tcp",
"value": "TCP data transfer"
},
{
"name": "tls",
"value": "TLS security transfer"
},
{
"name": "select",
"value": "select agreement"
},
{
"name": "select_user",
"value": "please select user"
},
{
"name": "input_ip",
"value": "please input correct ip"
},
{
"name": "user_name",
"value": "username:\"%s\""
},
{
"name": "confirm_code",
"value": "confirm code:\"%s\""
},
{
"name": "local_ip",
"value": "local ip:\"%s\""
},
{
"name": "user",
"value": "user:\"%s\""
},
{
"name": "fengzi",
"value": "fengzi"
},
{
"name": "wenzi",
"value": "wenzi"
},
{
"name": "input_room",
"value": "please input room number"
},
{
"name": "font_family",
"value": "HarmonyOS Sans SC-Medium"
},
{
"name": "login",
"value": "login"
},
{
"name": "feng",
"value": "feng"
},
{
"name": "wen",
"value": "wen"
},
{
"name": "please_input_remote_address",
"value": "please input remote address"
},
{
"name": "user_select",
"value": "user select"
},
{
"name": "font_family_regular",
"value": "HarmonyOS Sans SC-Regular"
},
{
"name": "not_room",
"value": "not room"
},
{
"name": "login_success",
"value": "login success"
},
{
"name": "invitation",
"value": "into room invitation"
},
{
"name": "invitation_detail",
"value": "opposite invitation you into %s room"
}
]
}

View File

@ -0,0 +1,156 @@
{
"string": [
{
"name": "module_desc",
"value": "模块描述"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "Socket通信"
},
{
"name": "internet_permission",
"value": "获取网络权限"
},
{
"name": "get_wifi_info_permission",
"value": "获取Wifi权限"
},
{
"name": "confirm",
"value": "确定"
},
{
"name": "input_roomNumber",
"value": "输入房间号"
},
{
"name": "cancel",
"value": "取消"
},
{
"name": "create_room",
"value": "创建房间"
},
{
"name": "send_message",
"value": "发送"
},
{
"name": "classmate_fengzi",
"value": "风子同学"
},
{
"name": "classmate_wenzi",
"value": "文子同学"
},
{
"name": "online",
"value": "在线"
},
{
"name": "offline",
"value": "离线"
},
{
"name": "udp",
"value": "UDP数据传输"
},
{
"name": "tcp",
"value": "TCP数据传输"
},
{
"name": "tls",
"value": "TLS安全传输"
},
{
"name": "select",
"value": "数据传输类型"
},
{
"name": "select_user",
"value": "请先选择用户"
},
{
"name": "input_ip",
"value": "登录失败请检查IP地址是否正确"
},
{
"name": "user_name",
"value": "用户名:\"%s\""
},
{
"name": "confirm_code",
"value": "确认码:\"%s\""
},
{
"name": "local_ip",
"value": "本地IP%s"
},
{
"name": "user",
"value": "用户:\"%s\""
},
{
"name": "fengzi",
"value": "风子"
},
{
"name": "wenzi",
"value": "文子"
},
{
"name": "input_room",
"value": "输入房间号"
},
{
"name": "font_family",
"value": "HarmonyOS Sans SC-Medium"
},
{
"name": "login",
"value": "登录"
},
{
"name": "feng",
"value": "风"
},
{
"name": "wen",
"value": "文"
},
{
"name": "please_input_remote_address",
"value": "请输入远程IP地址"
},
{
"name": "user_select",
"value": "用户选择"
},
{
"name": "font_family_regular",
"value": "HarmonyOS Sans SC-Regular"
},
{
"name": "not_room",
"value": "暂无房间"
},
{
"name": "login_success",
"value": "登录成功"
},
{
"name": "invitation",
"value": "加入房间邀请"
},
{
"name": "invitation_detail",
"value": "对方邀请您加入 %s 房间"
}
]
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 '@ohos.hilog';
import TestRunner from '@ohos.application.testRunner';
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
var abilityDelegator = undefined
var abilityDelegatorArguments = undefined
async function onAbilityCreateCallback() {
hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');
}
async function addAbilityMonitorCallback(err: any) {
hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');
}
export default class OpenHarmonyTestRunner implements TestRunner {
constructor() {
}
onPrepare() {
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare ');
}
async onRun() {
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');
abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
let testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility';
let lMonitor = {
abilityName: testAbilityName,
onAbilityCreate: onAbilityCreateCallback,
};
abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)
let cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName;
let debug = abilityDelegatorArguments.parameters['-D'];
if (debug == 'true') {
cmd += ' -D';
}
hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd);
abilityDelegator.executeShellCommand(cmd,
(err: any, d: any) => {
hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? '');
hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? '');
hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? '');
});
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 '@ohos.hilog'
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'
export default function abilityTest() {
describe('ActsAbilityTest', function () {
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
beforeAll(function () {
// Presets an action, which is performed only once before all test cases of the test suite start.
// This API supports only one parameter: preset action function.
})
beforeEach(function () {
// Presets an action, which is performed before each unit test case starts.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: preset action function.
})
afterEach(function () {
// Presets a clear action, which is performed after each unit test case ends.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: clear action function.
})
afterAll(function () {
// Presets a clear action, which is performed after all test cases of the test suite end.
// This API supports only one parameter: clear action function.
})
it('assertContain',0, function () {
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
hilog.info(0x0000, 'testTag', '%{public}s', '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)
})
})
}

View File

@ -0,0 +1,169 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
import hilog from '@ohos.hilog';
import { Driver, ON } from '@ohos.UiTest';
import abilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
import { getString } from '../util/ResourceUtil';
const TAG = '[Sample_Socket]';
const DOMAIN = 0xF811;
const BUNDLE = 'Socket_';
const delegator = abilityDelegatorRegistry.getAbilityDelegator();
let driver = Driver.create();
export default function IndexTest() {
describe('IndexTest',function () {
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
beforeAll(async function () {
// Presets an action, which is performed only once before all test cases of the test suite start.
// This API supports only one parameter: preset action function.
// 启动Ability
delegator.executeShellCommand('aa start -a EntryAbility -b ohos.samples.socket');
await driver.delayMs(1000);
})
beforeEach(function () {
// Presets an action, which is performed before each unit test case starts.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: preset action function.
})
afterEach(function () {
// Presets a clear action, which is performed after each unit test case ends.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: clear action function.
})
afterAll(function () {
// Presets a clear action, which is performed after all test cases of the test suite end.
// This API supports only one parameter: clear action function.
})
/**
* 连接服务器
*/
it(BUNDLE + 'ConnectTcpServer_001', 0,async function (done) {
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
hilog.info(DOMAIN, TAG, BUNDLE + 'ConnectTcpServer_001 begin');
await driver.delayMs(1000);
// 选择协议
await checkAndClickById('selectAgreement','ConnectTcpServer_001');
await checkAndClickByText(getString($r('app.string.tcp')),'ConnectTcpServer_001');
await checkAndClickById('agreementConfirm','ConnectTcpServer_001');
await driver.delayMs(1000);
// 输入服务端IP
await checkAndInputById('inputIp','192.168.8.160','ConnectTcpServer_001');
// 点击登录按钮
await checkAndClickById('login','ConnectTcpServer_001');
await driver.delayMs(3000);
done();
hilog.info(DOMAIN, TAG, BUNDLE + 'ConnectTcpServer_001 end');
})
/**
* 创建房间
*/
it(BUNDLE + 'CreateRoom_001', 0,async function (done) {
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
hilog.info(DOMAIN, TAG, BUNDLE + 'CreateRoom_001 begin');
await driver.delayMs(2000);
// 点击Text组件
await checkAndClickById('createRoom','CreateRoom_001');
// 输入房间号
await checkAndInputById('inputNumber','2332','CreateRoom_001');
// 点击确认按钮,发送房间号至客户端
await checkAndClickById('roomConfirm','CreateRoom_001');
await driver.delayMs(3000);
done();
hilog.info(DOMAIN, TAG, BUNDLE + 'CreateRoom_001 end');
})
/**
* 聊天
*/
it(BUNDLE + 'Chat_001', 0,async function (done) {
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
hilog.info(DOMAIN, TAG, BUNDLE + 'Chat_001 begin');
await driver.delayMs(2000);
// 输入第一条消息
await checkAndInputById('inputMsg','hello','Chat_001');
// 发送第一条消息
await checkAndClickById('sendMsg','Chat_001');
await driver.delayMs(2000);
// 输入第二条消息
await checkAndInputById('inputMsg','I,am,fine,thank,you','Chat_001');
// 发送第二条消息
await checkAndClickById('sendMsg','Chat_001');
await driver.delayMs(3000);
// 点击返回按钮
await checkAndClickById('indexBack','Chat_001');
await driver.delayMs(1000);
done();
hilog.info(DOMAIN, TAG, BUNDLE + 'Chat_001 end');
})
})
/**
* 根据id拿到组件并点击
* @param id
*/
async function checkAndClickById(id:string,log:string){
hilog.info(DOMAIN, TAG, BUNDLE + `${log} id:${id}`);
await driver.assertComponentExist(ON.id(id));
let res = await driver.findComponent(ON.id(id));
await res.click();
}
/**
* 根据text拿到组件并点击
* @param text
*/
async function checkAndClickByText(text:string,log:string){
hilog.info(DOMAIN, TAG, BUNDLE + `${log} text:${text}`);
await driver.assertComponentExist(ON.text(text));
let res = await driver.findComponent(ON.text(text));
await res.click();
}
/**
* 根据id拿到组件并输入消息
* @param id
* @param msg
*/
async function checkAndInputById(id:string,msg:string,log:string){
hilog.info(DOMAIN, TAG, BUNDLE + `${log} id:${id}`);
await driver.assertComponentExist(ON.id(id));
let res = await driver.findComponent(ON.id(id));
await res.inputText(msg);
}
}

View File

@ -0,0 +1,21 @@
import IndexTest from './Index.test'
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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()
IndexTest()
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 UIAbility from '@ohos.app.ability.UIAbility';
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
import hilog from '@ohos.hilog';
import { Hypium } from '@ohos/hypium';
import testsuite from '../test/List.test';
import window from '@ohos.window';
export default class TestAbility extends UIAbility {
onCreate(want, launchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');
hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? '');
var abilityDelegator: any
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
var abilityDelegatorArguments: any
abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');
Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite)
}
onDestroy() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage) {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');
windowStage.loadContent('testability/pages/Index', (err, data) => {
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. Data: %{public}s',
JSON.stringify(data) ?? '');
});
}
onWindowStageDestroy() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
}
onForeground() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
}
onBackground() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 '@ohos.hilog';
@Entry
@Component
struct Index {
aboutToAppear() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear');
}
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
Button() {
Text('next page')
.fontSize(20)
.fontWeight(FontWeight.Bold)
}.type(ButtonType.Capsule)
.margin({
top: 20
})
.backgroundColor('#0D9FFB')
.width('35%')
.height('5%')
.onClick(()=>{
})
}
.width('100%')
}
.height('100%')
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2023 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 AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
const delegator = AbilityDelegatorRegistry.getAbilityDelegator();
export function getString(resource: Resource): string {
let manage = delegator.getAppContext().resourceManager
return manage.getStringSync(resource)
}
export async function getStringArray(resource: Resource): Promise<Array<string>> {
let manage = delegator.getAppContext().resourceManager
let menuList: Array<string> = await manage.getStringArrayValue(resource);
return menuList;
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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",
"description": "$string:module_test_desc",
"mainElement": "TestAbility",
"deviceTypes": [
"default",
"tablet"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:test_pages",
"abilities": [
{
"name": "TestAbility",
"srcEntry": "./ets/testability/TestAbility.ets",
"description": "$string:TestAbility_desc",
"icon": "$media:icon",
"label": "$string:TestAbility_label",
"exported": true,
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"skills": [
{
"actions": [
"action.system.home"
],
"entities": [
"entity.system.home"
]
}
]
}
]
}
}

View File

@ -0,0 +1,8 @@
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
}
]
}

View File

@ -0,0 +1,20 @@
{
"string": [
{
"name": "module_test_desc",
"value": "test ability description"
},
{
"name": "TestAbility_desc",
"value": "the test ability"
},
{
"name": "TestAbility_label",
"value": "test label"
},
{
"name": "tcp",
"value": "TCP数据传输"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -0,0 +1,5 @@
{
"src": [
"testability/pages/Index"
]
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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.
*/
{
"hvigorVersion": "2.0.0",
"dependencies": {
"@ohos/hvigor-ohos-plugin": "2.0.0"
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
export { appTasks } from '@ohos/hvigor-ohos-plugin';

View File

@ -0,0 +1,61 @@
#!/bin/bash
# Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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.
# ----------------------------------------------------------------------------
# Hvigor startup script, version 1.0.0
#
# Required ENV vars:
# ------------------
# NODE_HOME - location of a Node home dir
# or
# Add /usr/local/nodejs/bin to the PATH environment variable
# ----------------------------------------------------------------------------
HVIGOR_APP_HOME=$(dirname $(readlink -f $0))
HVIGOR_WRAPPER_SCRIPT=${HVIGOR_APP_HOME}/hvigor/hvigor-wrapper.js
warn() {
echo ""
echo -e "\033[1;33m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m"
}
error() {
echo ""
echo -e "\033[1;31m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m"
}
fail() {
error "$@"
exit 1
}
# Determine node to start hvigor wrapper script
if [ -n "${NODE_HOME}" ];then
EXECUTABLE_NODE="${NODE_HOME}/bin/node"
if [ ! -x "$EXECUTABLE_NODE" ];then
fail "ERROR: NODE_HOME is set to an invalid directory,check $NODE_HOME\n\nPlease set NODE_HOME in your environment to the location where your nodejs installed"
fi
else
EXECUTABLE_NODE="node"
which ${EXECUTABLE_NODE} > /dev/null 2>&1 || fail "ERROR: NODE_HOME is not set and not 'node' command found in your path"
fi
# Check hvigor wrapper script
if [ ! -r "$HVIGOR_WRAPPER_SCRIPT" ];then
fail "ERROR: Couldn't find hvigor/hvigor-wrapper.js in ${HVIGOR_APP_HOME}"
fi
# start hvigor-wrapper script
exec "${EXECUTABLE_NODE}" \
"${HVIGOR_WRAPPER_SCRIPT}" "$@"

View File

@ -0,0 +1,71 @@
:: Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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.
@Echo off
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Hvigor startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
set WRAPPER_MODULE_PATH=%APP_HOME%\hvigor\hvigor-wrapper.js
set NODE_EXE=node.exe
goto start
:start
@rem Find node.exe
if defined NODE_HOME goto findNodeFromNodeHome
%NODE_EXE% --version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH.
echo.
echo Please set the NODE_HOME variable in your environment to match the
echo location of your NodeJs installation.
goto fail
:findNodeFromNodeHome
set NODE_HOME=%NODE_HOME:"=%
set NODE_EXE_PATH=%NODE_HOME%/%NODE_EXE%
if exist "%NODE_EXE_PATH%" goto execute
echo.
echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH.
echo.
echo Please set the NODE_HOME variable in your environment to match the
echo location of your NodeJs installation.
goto fail
:execute
@rem Execute hvigor
"%NODE_EXE%" %WRAPPER_MODULE_PATH% %*
:fail
exit /b 1

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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.
*/
{
"license": "ISC",
"devDependencies": {
"@ohos/hypium": "1.0.6"
},
"name": "socket",
"description": "example description",
"repository": {},
"version": "1.0.0",
"dependencies": {}
}

View File

@ -0,0 +1,13 @@
# Socket通信 测试用例归档
## 用例表
| 测试功能 | 预置条件 | 输入 | 预期输出 |测试结果|
|------|-------|------|---------|---|
| 拉起应用 | 设备正常运行 | 点击应用 | 成功拉起应用 |Pass|
| 选择用户 | 在操作界面 | 选择风子 | 当前用户为风子 |Pass|
| 选择协议 | 在操作界面 | 点击TCP传输协议 | 当前协议为TCP |Pass|
| 输入IP地址 | 在操作界面 | 本端输入服务器IP地址 | 点击登录后成功连接服务器 |Pass|
| 输入房间号 | 在操作界面 | 输入房间号后发送至对端 | 对端成功收到邀请进入房间信息 |Pass|
| 发送文本消息 | 在操作界面 | 本端输入“Hello”后发送 | 对端成功接收信息 |Pass|
| 接收文本消息 | 在操作界面 | 对端输入“Hello”后发送 | 本地成功接收信息 |Pass|

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,68 @@
#!/usr/bin/env python
# # -*- coding: utf-8 -*-
# Copyright (c) 2023 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
import threading
# 处理客户端消息的线程函数
def handle_client(conn):
# 将新客户端连接加入列表
clients.append(conn)
# 循环接收和发送消息
while True:
# 接收客户端数据
data = conn.recv(1024)
print('data recv ', data.decode())
# 发送消息给所有客户端
for client in clients:
if client != conn:
client.send(data)
# 处理客户端连接的线程函数
def accept_clients():
while True:
# 接受客户端连接请求
conn, addr = server_socket.accept()
print('accept ', addr)
# 创建新线程处理客户端消息
client_thread = threading.Thread(target=handle_client, args=(conn,))
client_thread.start()
if __name__ == '__main__':
# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定IP地址和端口号此处IP地址与运行服务端的本机一致端口号与客户端端口一致
server_socket.bind(('192.168.76.57', 9090))
# 监听客户端连接
server_socket.listen(5)
# 存储所有客户端连接的列表
clients = []
# 启动处理客户端连接的线程
accept_thread = threading.Thread(target=accept_clients)
accept_thread.start()

View File

@ -0,0 +1,74 @@
#!/usr/bin/env python
# # -*- coding: utf-8 -*-
# Copyright (c) 2023 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
import ssl
import threading
# 处理客户端消息的线程函数
def handle_client(conn):
# 使用SSL包装socket对象
conn = context.wrap_socket(conn, server_side=True)
# 将新客户端连接加入列表
clients.append(conn)
# 循环接收和发送消息
while True:
# 接收客户端数据
data = conn.recv(1024)
print('data recv ', data.decode())
# 发送消息给所有客户端
for client in clients:
if client != conn:
client.send(data)
# 处理客户端连接的线程函数
def accept_clients():
while True:
# 接受客户端连接请求
conn, addr = server_socket.accept()
print('accept ', addr)
# 创建新线程处理客户端消息
client_thread = threading.Thread(target=handle_client, args=(conn,))
client_thread.start()
if __name__ == '__main__':
# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定IP地址和端口号此处IP地址与运行服务端的本机一致端口号与客户端端口一致
server_socket.bind(('192.168.76.57', 9090))
# 监听客户端连接
server_socket.listen(5)
# 加载SSL证书
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.set_ciphers('ALL:@SECLEVEL=0')
context.load_cert_chain(certfile="server/server.crt", keyfile="server/server.key")
context.load_verify_locations(cafile="ca/ca.crt")
# 存储所有客户端连接的列表
clients = []
# 启动处理客户端连接的线程
accept_thread = threading.Thread(target=accept_clients)
accept_thread.start()