鸿蒙网络编程系列25-TCP回声服务器的实现

1. TCP回声服务器实现可行性

在前文鸿蒙网络编程系列2-UDP回声服务器的实现中,介绍了什么是回声服务器,并且基于UDP协议实现了一个简单的回声服务器,本节将基于TCP协议实现一个类似的回声服务器。在鸿蒙API10以后,提供了TCPSocketServer类,该类封装了TCP服务端的相关接口,包括用来监听的listen方法,订阅各种事件的on方法,以及发送数据的send方法,关于这些接口的简介见前文鸿蒙网络编程系列23-实现一个基于鸿蒙API的HTTP服务器中的第一部分,或者直接查看鸿蒙官方文档,有了这些接口的支持,编写回声服务器也是易如反掌了。

2. 实现TCP回声服务器示例

本示例运行后的界面如下所示:

cke_32349.jpg

输入要监听的端口,然后单击“启动”按钮即可启动对该端口的监听了,如果有TCP客户端发送数据到这个端口,服务器会接收数据并在下面的日志区域显示,然后回写到发送端。

下面详细介绍创建该示例应用的步骤并演示用法。

步骤1:创建Empty Ability项目。

步骤2:在module.json5配置文件加上对权限的声明:

    "requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.KEEP_BACKGROUND_RUNNING"}]

这里添加了对互联网的访问权限以及后台执行权限。

在module.json5配置文件的abilities节点下加上长时任务类型的配置:

        "backgroundModes": ["dataTransfer"]

步骤3:在文件EntryAbility.ets中的onCreate生命周期回调函数添加如下的代码:

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {let wantAgentInfo: wantAgent.WantAgentInfo = {// 点击通知后,将要执行的动作列表wants: [{bundleName: "com.zl.network.demo.tcp.echoserver",abilityName: "EntryAbility"}],// 点击通知后,动作类型actionType: wantAgent.OperationType.START_ABILITY,// 使用者自定义的一个私有值requestCode: 0,// 点击通知后,动作执行属性wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]};
​try {// 通过wantAgent模块下getWantAgent方法获取WantAgent对象wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {try {let list: Array<string> = ["dataTransfer"];backgroundTaskManager.startBackgroundRunning(this.context, list, wantAgentObj).then((res: backgroundTaskManager.ContinuousTaskNotification) => {console.info("Operation startBackgroundRunning succeeded");
​}).catch((error: BusinessError) => {console.error(`Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);});} catch (error) {console.error(`Operation startBackgroundRunning failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);}});} catch (error) {console.error(`Operation getWantAgent failed. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);}}

该段代码主要是启动后台长时服务,因为TCP服务端有可能需要长时间在后台运行,通过该步骤可以获得授权。

步骤4:在页面文件Index.ets里添加如下的代码:

import { socket } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { ArrayList, util } from '@kit.ArkTS';
​
//TCP服务端实例
let tcpSocketServer: socket.TCPSocketServer = socket.constructTCPSocketServerInstance()
​
@Entry
@Component
struct Index {@State title: string = 'TCP回声服务器示例';@State running: boolean = false//连接、通讯历史记录@State msgHistory: string = ''//本地端口@State port: number = 9999scroller: Scroller = new Scroller()//已连接的客户端列表clientList = new ArrayList<socket.TCPSocketConnection>()
​build() {RelativeContainer() {Text(this.title).id('txtTitle').fontSize(20).fontWeight(FontWeight.Bold).alignRules({middle: { anchor: '__container__', align: HorizontalAlign.Center },top: { anchor: '__container__', align: VerticalAlign.Top }}).margin(10)
​Text("绑定的服务器端口:").id('txtPort').fontSize(15).textAlign(TextAlign.Start).height(40).width(150).alignRules({left: { anchor: '__container__', align: HorizontalAlign.Start },top: { anchor: 'txtTitle', align: VerticalAlign.Bottom }}).margin(10)
​TextInput({ text: this.port.toString() }).onChange((value) => {this.port = parseInt(value)}).type(InputType.Number).height(40).id('txtInputPort').fontSize(15).alignRules({left: { anchor: 'txtPort', align: HorizontalAlign.End },right: { anchor: 'btnStart', align: HorizontalAlign.Start },top: { anchor: 'txtTitle', align: VerticalAlign.Bottom }}).margin(5)
​Button(this.running ? "停止" : "启动").onClick(() => {if (!this.running) {this.startServer()} else {this.stopServer()}}).height(40).width(80).id('btnStart').fontSize(15).alignRules({right: { anchor: '__container__', align: HorizontalAlign.End },top: { anchor: 'txtTitle', align: VerticalAlign.Bottom }}).margin(5)
​Scroll(this.scroller) {Text(this.msgHistory).textAlign(TextAlign.Start).padding(10).width('100%').fontSize(12).backgroundColor(0xeeeeee)}.alignRules({left: { anchor: '__container__', align: HorizontalAlign.Start },top: { anchor: 'txtPort', align: VerticalAlign.Bottom },bottom: { anchor: '__container__', align: VerticalAlign.Bottom }}).align(Alignment.Top).backgroundColor(0xeeeeee).scrollable(ScrollDirection.Vertical).scrollBar(BarState.On).scrollBarWidth(20).margin(5).id('scrollHis')}.height('100%').width('100%')}
​//停止服务stopServer() {tcpSocketServer.off('connect')for (let client of this.clientList) {client.off('message')}this.running = falsethis.msgHistory += "停止服务\r\n"}
​//启动服务startServer() {//订阅连接事件消息tcpSocketServer.on('connect', (clientSocket: socket.TCPSocketConnection) => {this.clientList.add(clientSocket)clientSocket.on('message', (msgInfo: socket.SocketMessageInfo) => {//收到的信息转化为字符串let content = buf2String(msgInfo.message)//显示信息日志,最后加上回车换行this.msgHistory += `[${msgInfo.remoteInfo.address}:${msgInfo.remoteInfo.port}]${content}\r\n`//把收到的信息发回客户端clientSocket.send({ data: msgInfo.message })})});
​let listenAddress: socket.NetAddress = { address: '0.0.0.0', port: this.port }//绑定到指定的端口并启动客户端连接监听tcpSocketServer.listen(listenAddress).then(() => {this.msgHistory += "监听成功\r\n"this.running = truethis.msgHistory += "服务启动\r\n"}).catch((err: BusinessError) => {this.msgHistory += "监听失败\r\n"});}
}
​
//ArrayBuffer转utf8字符串
function buf2String(buf: ArrayBuffer) {let msgArray = new Uint8Array(buf);let textDecoder = util.TextDecoder.create("utf-8");return textDecoder.decodeWithStream(msgArray)
}

步骤5:编译运行,可以使用模拟器或者真机。

步骤6:单击“启动”按钮启动TCP服务端。

步骤7:启动TCP客户端,输入服务端地址和端口,单击“连接”按钮连接到服务端,然后输入要发送给服务端的消息,最后单击“发送”按钮,界面如下图所示。

cke_80666.png

步骤8:此时查看TCP服务端界面,可以看到服务端也接收到了客户端的消息,如下图所示。

cke_59600.jpg

在本示例中,要注意的是TCP服务端是可以接受多个客户端的连接的,也就是说在订阅连接事件的消息并进行处理时,每一个连接(TCPSocketConnection)都代表一个新的TCP客户端,这些客户端都是互相独立的,要处理与客户端的消息收发,就要订阅每一个连接的接收消息事件。

3.注意事项

服务端需要长时间运行,为了在应用进入后台后仍然可以响应,在代码里申请了后台运行权限,在oncreate回调里也做了响应的处理,不过本示例只在模拟器里调试通过,还没有机会在实体机中实际运行。

(本文作者原创,除非明确授权禁止转载)

本文源码地址:

https://gitee.com/zl3624/harmonyos_network_samples/tree/master/code/tcp/TCPEchoServer

本系列源码地址:

https://gitee.com/zl3624/harmonyos_network_samples

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/454681.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】使用vscode进行 C/C++ 开发,内含c_cpp_properties.json、launch.json 和 tasks.json解释

在 Visual Studio Code (VSCode) 中进行 C/C 开发时&#xff0c;这三个 .json 文件&#xff08;c_cpp_properties.json、launch.json 和 tasks.json&#xff09;分别用于配置编译、调试和代码提示等功能。它们是 VSCode 配置环境的一部分&#xff0c;由 C/C 扩展生成&#xff0…

将java项目jar包打包成exe服务

1.结构展示 2.注意事项 前提: 环境准备:jdk8 和 .net支持 { 1.控制面板》程序和功能》启用和关闭windows功能》.net的勾选》2.jdk8自行百度安装环境3.其他项目必须的软件环境安装等&#xff08;数据库...&#xff09; }第一次准备: 1.将打包好的jar包放到premiumServices.exe…

智和信通助力某大型服饰集团建设综合监控运维

某大型服饰集团成立于90年代&#xff0c;是广受认可的国民生活时尚品牌&#xff0c;近年来随着集团公司业务规模的不断扩大&#xff0c;信息化作为支撑集团公司业务发展的重要技术手段&#xff0c;信息系统无论在规模上还是在复杂程度上均有了很大程度的增加。 项目现状 当前信…

计算机网络—vlan(虚拟局域网)

内容补充 冲突域 如果两台设备同时发送数据&#xff0c;他们的数据会互相干扰&#xff0c;那么他们就处于同一冲突域&#xff0c;例如集线器&#xff08;总线型&#xff0c;所有设备共享带宽&#xff09;的所有端口都处于冲突域。 广播域 如果一台设备发送数据&#xff0c;…

babylonjs shader学习之copy shadertoy案例

shadertoy案例&#xff1a; 准备 const onSceneReady (scene: Scene) > {const light new HemisphericLight(light, new Vector3(0, 1, 0), scene);light.intensity 0.7;Effect.ShadersStore[planeMatVertexShader] precision highp float;attribute vec3 position;attr…

单片机输出方波

从P1.0上输出一个方波,高电平5ms&#xff0c;低电平10ms. &#xff03;include〈reg51。h〉 unsigned char flag; sbit outP1^0&#xff1b; void main() &#xff5b; flag0&#xff1b; TMOD0X02; TH06&#xff1b; TL06; TR01&#xff1b; EA1&#xff1b; ET0…

Redis JSON介绍和命令大全

Redis JSON介绍和命令大全 Redis JSON先说说JSON是什么再说说JSON Path先推荐两个网站JSONPath JAVA clents Redis JSON 安装内存json命令语法命令url命令解释JSON.ARRAPPENDJSON.ARRINDEXJSON.ARRINSERTJSON.ARRLENJSON.ARRPOPJSON.ARRTRIMJSON.CLEARJSON.DEBUG MEMORYJSON.DE…

centOS部署Jenkins实现项目可持续自动化部署

个人看的是尚硅谷的视频&#xff0c;跟着实战&#xff0c;但因为视频是21年的&#xff0c;所以很容易出现jenkins插件不适配问题。 因而个人直接用较新版的jdk和jenkins. 先切换到root用户 sudo su一、安装jdk 先查询可安装版本 yum list java*安装jdk&#xff08;只复制圈…

【算法】归并排序概念及例题运用

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

爬虫日常实战

爬取美团新闻信息&#xff0c;此处采用两种方法实现&#xff1a; 注意点&#xff1a;因为此处的数据都是动态数据&#xff0c;所以一定要考虑好向下滑动数据包会更新的情况&#xff0c;不然就只能读取当前页即第一页数据&#xff0c;方法一通过更新ajax数据包网址页数&#xf…

vscode 预览markdown 文件

1. 点击左边扩展 2. 搜索“Markdown Preview Enhanced” 3. 选第一个安装即可 4. 重启vscode 5. 打开一个markdown 文件 6. 点击右上角的预览按钮

[mysql]mysql的全部单行函数

单行函数 几乎我们认识的语言都会对一些常用的功能进行,封装,有些叫函数,有些叫方法(Java),后期我们还可以自定义函数. 现在我们就当大家是没有语言基础,我们来从头开始讲.不过大家肯定接触过,中学说的函数,yf(x)f代表的就是function的缩写,这里其y2x1fx代表的就是封装的内容…

FileLink内外网文件交换——致力企业高效安全文件共享

随着数字化转型的推进&#xff0c;企业之间的文件交流需求日益增加。然而&#xff0c;传统的文件传输方式往往无法满足速度和安全性的双重要求。FileLink作为一款专注于跨网文件交换的工具&#xff0c;致力于为企业提供高效、安全的文件共享解决方案。 应用场景一&#xff1a;项…

C++大沥2019年真题——数字圈

Hi&#xff01;大家好&#xff01;Im#张亿&#xff0c;今天来讲C大沥2019年真题——数字圈 题目描述 当我们写数字时会发现有些数字有封闭区域&#xff0c;有的数字没有封闭区域。 数字 0 有一个封闭区域&#xff0c;数字 1、2、 3 都没有封闭区域&#xff0c;数字 4 有一个封…

word中的内容旋转90度

在vsto、Aspose.Words 中&#xff0c;默认没有直接的 API 可以让表格整体旋转 90 度。然而&#xff0c;我们可以通过一些方式来实现类似的效果&#xff0c;具体思路如下&#xff1a; 将表格插入到一个形状&#xff08;Shape&#xff09;或文本框中&#xff0c;然后旋转该形状。…

《RECONX: RECONSTRUCT ANY SCENE FROM SPARSEVIEWS WITH VIDEO DIFFUSION MODEL》论文阅读

论文地址&#xff1a;https://arxiv.org/pdf/2408.16767 项目地址&#xff1a;GitHub - liuff19/ReconX: ReconX: Reconstruct Any Scene from Sparse Views with Video Diffusion Model ---------------------------------------------------------------------------------…

2019年计算机网络408真题解析

第一题&#xff1a; 解析&#xff1a;OSI参考模型第5层完成的功能 首先&#xff0c;我们需要对OSI参考模型很熟悉&#xff1a;从下到上依次是&#xff1a;物理层-数据链路层-网络层- 运输层-会话层-表示层-应用层&#xff0c;由此可知&#xff0c;题目要问的是会话层的主要功能…

什么是感知与计算融合?

感知与计算融合&#xff08;Perception-Computing Fusion&#xff09;是指将感知技术&#xff08;如传感器、摄像头等&#xff09;与计算技术&#xff08;如数据处理、人工智能等&#xff09;有机结合&#xff0c;以实现对环境的更深层次理解和智能反应的过程。该技术广泛应用于…

基于SSM品牌银饰售卖系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;促销活动管理&#xff0c;饰品管理&#xff0c;我的收藏管理&#xff0c;系统管理&#xff0c;订单管理 用户账号功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;…

新书速览|Android智能座舱开发:从源码到实践

《Android智能座舱开发:从源码到实践》 本书内容 《Android智能座舱开发:从源码到实践》是一本专注于Android智能座舱系统开发与优化的实战指南。《Android智能座舱开发:从源码到实践》共9章&#xff0c;第1章从搭建源码编译环境开始&#xff0c;详细指导读者如何下载和编译An…