鸿蒙网络编程系列3-TCP客户端通讯示例

1. TCP简介

TCP协议是传输层最重要的协议,提供了可靠、有序的数据传输,是多个广泛使用的表示层协议的运行基础,相对于UDP来说,TCP需要经过三次握手后才能建立连接,建立连接后才能进行数据传输,所以效率差了一些,但是它的发送-确认机制决定了传输是可靠的,再加上滑动窗口机制的设计,也可以极大的提高传输效率。

2. TCP通讯的常用方法

鸿蒙封装的TCP操作类位于模块socket中,使用如下的方式导入:

import socket from '@ohos.net.socket';

        socket模块包括了众多的TCP操作方法,就本文而言,重点需要掌握的是如下五个:

1)constructTCPSocketInstance(): TCPSocket

创建一个TCPSocket对象,在使用TCPSocket的方法以前需要创建该对象。

2)bind(address: NetAddress): Promise<void>

绑定IP地址和端口,端口可以指定或由系统随机分配,可以使用0.0.0.0表示本机IP地址;使用Promise方式作为异步方法。

3)connect(options: TCPConnectOptions): Promise<void>

连接到指定的IP地址和端口,参数options包含了连接的地址address和连接超时时间timeout,其中address是必选的,timeout是可选的,使用promise方法作为异步方法。

4)send(options: TCPSendOptions): Promise<void>

通过TCPSocket连接发送数据,参数options包括要发送的数据data和字符编码encoding,其中data是必选的,encoding是可选的,默认使为utf-8编码,使用Promise方式作为异步方法。

5)on(type: 'message', callback: Callback<{message: ArrayBuffer, remoteInfo: SocketRemoteInfo}>): void

订阅TCPSocket连接的接收消息事件,当套接字接收到消息时触发该事件,其中message表示接收到的消息,remoteInfo是发送方信息;使用callback方式作为异步方法。

3. TCP客户端通讯示例

为演示TCP通讯的方式,本示例实现了一个使用TCP协议发送、接收消息的功能,运行后的初始界面如下所示:

本示例的实现思路是这样的,首先把套接字绑定到本地的给定端口上,然后再连接到指定的服务端,最后发送消息给服务端,因为TCP是面向连接的协议,所以,本示例默认情况下连接和发送按钮都是不可用的,在绑定成功后才可以使用连接按钮,在成功连接带服务端后才可以使用发送按钮。

下面详细介绍创建该应用的步骤。

步骤1:创建Empty Ability项目。

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

"requestPermissions": [

      {

        "name": "ohos.permission.INTERNET"

      },

      {

        "name": "ohos.permission.GET_WIFI_INFO"

      }

    ]

这里分别添加了访问互联网和访问WIFI信息的权限。

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

import socket from '@ohos.net.socket';
import wifiManager from '@ohos.wifiManager';
import systemDateTime from '@ohos.systemDateTime';
import util from '@ohos.util';//执行TCP通讯的对象
let tcpSocket = socket.constructTCPSocketInstance();//说明:本地的IP地址不是必须知道的,绑定时绑定到IP:0.0.0.0即可,显示本地IP地址的目的是方便对方发送信息过来
//本地IP的数值形式
let ipNum = wifiManager.getIpInfo().ipAddress
//本地IP的字符串形式
let localIp = (ipNum >>> 24) + '.' + (ipNum >> 16 & 0xFF) + '.' + (ipNum >> 8 & 0xFF) + '.' + (ipNum & 0xFF);@Entry
@Component
struct Index {//连接、通讯历史记录@State msgHistory: string = ''//要发送的信息@State sendMsg: string = ''//本地端口@State localPort: number = 9990//服务端IP地址@State serverIp: string = "0.0.0.0"//服务端端口@State serverPort: number = 9999//是否可以连接@State canConnect: boolean = false//是否可以发送消息@State canSend: boolean = falsescroller: Scroller = new Scroller()build() {Row() {Column() {Text("TCP通讯示例").fontSize(14).fontWeight(FontWeight.Bold).width('100%').textAlign(TextAlign.Center).padding(10)Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {Text("本地IP和端口:").width(100).fontSize(14).flexGrow(0)Text(localIp).width(110).fontSize(12).flexGrow(0)TextInput({ text: this.localPort.toString() }).type(InputType.Number).onChange((value) => {this.localPort = parseInt(value)}).width(50).fontSize(12).flexGrow(3)Button("绑定").onClick(() => {this.bind2Port()}).width(70).fontSize(14).flexGrow(0)}.width('100%').padding(10)Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {Text("服务端地址:").fontSize(14).width(90).flexGrow(1)TextInput({ text: this.serverIp }).onChange((value) => {this.serverIp = value}).width(110).fontSize(12).flexGrow(4)Text(":").width(5).flexGrow(0)TextInput({ text: this.serverPort.toString() }).type(InputType.Number).onChange((value) => {this.serverPort = parseInt(value)}).fontSize(12).flexGrow(2).width(50)Button("连接").onClick(() => {this.connect2Server()}).enabled(this.canConnect).width(70).fontSize(14).flexGrow(0)}.width('100%').padding(10)Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {TextInput({ placeholder: "输入要发送的消息" }).onChange((value) => {this.sendMsg = value}).width(200).flexGrow(1)Button("发送").enabled(this.canSend).width(70).fontSize(14).flexGrow(0).onClick(() => {this.sendMsg2Server()})}.width('100%').padding(10)Scroll(this.scroller) {Text(this.msgHistory).textAlign(TextAlign.Start).padding(10).width('100%').backgroundColor(0xeeeeee)}.align(Alignment.Top).backgroundColor(0xeeeeee).height(300).flexGrow(1).scrollable(ScrollDirection.Vertical).scrollBar(BarState.On).scrollBarWidth(20)}.width('100%').justifyContent(FlexAlign.Start).height('100%')}.height('100%')}//发送消息到服务端sendMsg2Server() {tcpSocket.send({ data: this.sendMsg + "\r\n" }).then(async () => {this.msgHistory += "我:" + this.sendMsg + await getCurrentTimeString() + "\r\n"}).catch((e) => {this.msgHistory += '发送失败' + e.message + "\r\n";})}//绑定本地端口async bind2Port() {//本地地址let localAddress = { address: "0.0.0.0", port: this.localPort, family: 1 }await tcpSocket.bind(localAddress).then(() => {this.msgHistory = 'bind success' + "\r\n";this.canConnect = true}).catch((e) => {this.msgHistory = 'bind fail ' + e.message + "\r\n";})//收到消息时的处理tcpSocket.on("message", async (value) => {let msg = buf2String(value.message)let time = await getCurrentTimeString()this.msgHistory += "服务端:" + msg + time + "\r\n"this.scroller.scrollEdge(Edge.Bottom)})}//连接服务端connect2Server() {//本地地址let serverAddress = { address: this.serverIp, port: this.serverPort, family: 1 }tcpSocket.connect({ address: serverAddress }).then(() => {this.msgHistory = 'connect success ' + "\r\n";this.canSend = true}).catch((e) => {this.msgHistory = 'connect fail ' + e.message + "\r\n";})}
}//同步获取当前时间的字符串形式
async function getCurrentTimeString() {let time = ""await  systemDateTime.getDate().then((date) => {time = date.getHours().toString() + ":" + date.getMinutes().toString()+ ":" + date.getSeconds().toString()})return "[" + time + "]"
}//ArrayBuffer转utf8字符串
function buf2String(buf: ArrayBuffer) {let msgArray = new Uint8Array(buf);let textDecoder = util.TextDecoder.create("utf-8");return textDecoder.decodeWithStream(msgArray)
}

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

步骤5:配置本地端口和服务端地址,这里为了模拟服务端,使用nc命令在服务器监听9999端口,命令如下:

nc -l -p 9999复制

当然,读者也可以根据自己需要选择其他合适的TCP服务端。

连接上服务端后,客户端发送消息“服务端,你好啊!”

然后服务端回复:“客户端,你好!”,客户端截图如下所示:

服务端截图如下所示:

这样就完成了一个简单的TCP消息发送应用。

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

本文码云源码地址:https://gitee.com/zl3624/harmonyos_network_samples/tree/master/code/udp/UdpDemo

本系列码云源码地址:

https://gitee.com/zl3624/harmonyos_network_samples

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

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

相关文章

Android -- [SelfView] 多动画效果图片播放器

Android – [SelfView] 多动画效果图片播放器 效果&#xff08;录制的有点卡&#xff09; 1. 引用&#xff1a; <com.nepalese.virgolib.widget.image.BaseImageViewandroid:id"id/base_image"android:layout_width"match_parent"android:layout_heigh…

AD画图的使用

一、新建工程 二、绘制原理图 1、原理图搜索方法&#xff1a; https://www.ti.com/lit/ds/symlink/tlv1117.pdf?ts1729143086540&ref_urlhttps%253A%252F%252Fwww.mouser.tw%252F www.alldatasheet.com 2、绘图步骤&#xff1a; 注&#xff1a;管脚四点朝外

2024软考网络工程师笔记 - 第5章.无线通信网

文章目录 移动通信与 5G1️⃣移动通信2️⃣移动通信制式3️⃣5G 应用场景与关键技术 &#x1f551;WLAN 通信技术1️⃣WLAN 通信技术 &#x1f552;WLAN 频谱与信道&#xff08;高频考点&#xff09;1️⃣WLAN 网络分类2️⃣ISM频段3️⃣不重叠信道&#xff08;重点&#xff09…

uniapp 省、市、区、乡镇 数据层级选择插件 Ba-DataPicker

Ba-DataPicker 是一款uniapp数据层级选择弹窗插件。支持省市区乡四级&#xff1b;支持自定义数据。 支持省、市、区、乡镇四级支持自定义数据支持字母检索 截图展示 支持定制、本地包、源码等&#xff0c;有建议和需要&#xff0c;请点击文章结尾“Uniapp插件开发”联系我&am…

人脸识别-特征算法

文章目录 一、LBPH算法1.基本原理2.实现步骤3.代码实现 二、Eigenfaces算法1.特点2.代码实习 三、FisherFaces算法1.算法原理2.算法特点3.代码实现 四、总结 人脸识别特征识别器是数字信息发展中的一种生物特征识别技术&#xff0c;其核心在于通过特定的算法和技术手段&#xf…

代码随想录算法训练营第八天(1)|哈希表理论基础

文档讲解&#xff1a;代码随想录 难度&#xff1a;有一点 哈希表理论基础 哈希表 首先什么是哈希表&#xff0c;哈希表&#xff08;英文名字为Hash table&#xff0c;国内也有一些算法书籍翻译为散列表&#xff0c;大家看到这两个名称知道都是指hash table就可以了&#xff0…

指尖的无声告白,算法里的隐约温柔

公主请阅 1. 三数之和1. 题目说明示例 1示例 2示例 3 1.2 题目分析1.3 代码部分1.3 代码分析 2. 四数之和2.1 题目说明示例 1示例 2 2.2 题目分析2.3 代码部分2.4 代码解析 1. 三数之和 题目传送门 1. 题目说明 给定一个整数数组 nums&#xff0c;判断数组中是否存在三个元素 …

鸿道Intewell操作系统构型介绍之Intewell-C全实时构型

鸿道(Intewell)操作系统主要包括Intewell-C、Intewell-H和Intewell-V三种不同构型产品&#xff1a; Intewell-C Intewell-C是一款工业实时微内核操作系统&#xff0c;由科东软件自主研发&#xff0c;具有超低延迟和最小抖动&#xff0c;保障工业设备可以高效处理时间敏感的现…

python爬虫实战案例——从移动端接口抓取微博评论,采用cookie登陆,数据存入excel表格,超详细(15)

文章目录 1、任务目标2、网页分析3、代码编写3.1 代码分析3.2 完整代码1、任务目标 1、目标网站:微博文章(https://m.weibo.cn/detail/4813628149072458),这是微博某一篇博文,用于本文测试 2、要求:爬取该博文下,所有一级评论和二级评论,以及每条评论的作者,最后保存至E…

熵权法计算评价指标权重——使用Excel VBA实现

[ 熵权法 ] 信息是系统有序程度的一个度量&#xff0c;熵是系统无序程度的一个度量&#xff1b;根据信息熵的定义&#xff0c;对于某项指标&#xff0c;可以用熵值来判断某个指标的离散程度&#xff0c;其信息熵值越小&#xff0c;指标的离散程度越大&#xff0c; 该指标对综合…

java脚手架系列4--测试用例、拦截器

异常处理、拦截器、数据库连接 1 测试用例 单元测试是一个老生常谈的问题&#xff0c;无论是后端对自己的代码质量把的第一道关也好&#xff0c;也是对测试减缓压力。这里就不过多讲述测试用例的重要性&#xff0c;但是有2个框架我们必须了解一下。 1.1 JUnit和mockito 我们…

gitlab保护分支设置

版本&#xff1a;gitlab10.2.2 一旦设置master分支被保护&#xff0c;除了管理员之外的任何用户都无法直接向master提交代码&#xff0c;只要提交代码就会报错 # git push -u origin master Total 0 (delta 0), reused 0 (delta 0) remote: GitLab: You are not allowed to pu…

[LeetCode] 733. 图像渲染

题目描述&#xff1a; 有一幅以 m x n 的二维整数数组表示的图画 image &#xff0c;其中 image[i][j] 表示该图画的像素值大小。你也被给予三个整数 sr , sc 和 color 。你应该从像素 image[sr][sc] 开始对图像进行上色 填充 。 为了完成 上色工作&#xff1a; 从初始像素…

【python】OpenCV—Fun Mirrors

文章目录 1、准备工作2、原理介绍3、代码实现4、效果展示5、参考 1、准备工作 pip install vacm2、原理介绍 在OpenCV中&#xff0c;VCAM 库是一个用于简化创建三维曲面、定义虚拟摄像机、设置参数以及进行投影任务的工具。它特别适用于实现如哈哈镜等图像变形效果。 一、VC…

简易STL实现 | PriorityQueue 的实现

1、priority_queue 的底层是堆&#xff0c;标准库中 直接使用 std::make_heap, std::push_heap, std::pop_heap 来实现 priority_queue 2、std::make_heap、std::push_heap 和 std::pop_heap 这三个函数 用于 处理堆数据结构&#xff08;Heap&#xff09;。堆 是一种特殊的完全…

4、.Net 快速开发框架:DncZeus - 开源项目研究文章

DncZeus 是一个基于 ASP.NET Core 和 Vue.js 的前后端分离的通用后台管理系统框架&#xff0c;其愿景是成为一个易于使用且功能丰富的 .NET Core 通用后台权限管理模板系统基础框架。项目名称 "DncZeus" 由 "Dnc"(.NET Core 的缩写)和 "Zeus"(古…

JavaWeb环境下的Spring Boot在线考试系统开发

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理基于JavaWeb技术的在线考试系统设计与实现…

【学习】word保存图片

word中有想保存的照片 直接右键另存为的话&#xff0c;文件总是不清晰&#xff0c;截屏的话&#xff0c;好像也欠妥。 怎么办? 可以另存为 网页 .html 可以得到&#xff1a; 原图就放到了文件夹里面

Java学习Day47:戏耍黑手道人(项目记录)

1.项目背景 2.技术选择 3.环境搭建 1.创建空项目 创建health_parent父文件用来控制依赖&#xff0c;类型为quickStart 打包方式为&#xff0c;pom&#xff1a;用在父级工程或聚合工程中&#xff0c;用来做jar包的版本控制&#xff0c;必须指明这个聚合工程的打包方式为pom。…

计算机网络-RSTP工作过程与原理

前面我们已经学习了RSTP的一些基础概念以及对于STP的改进之处&#xff0c;因为RSTP兼容STP&#xff0c;所以实际上两者工作原理是一致的&#xff0c;这里只简单过一遍&#xff0c;然后进行一些基础实验即可&#xff0c;大致还是遵循选举根桥、确定端口角色与状态、全网收敛的思…