開源 Arkts 鴻蒙應用 開發(九)通訊--tcp客戶端

??文章的目的為了記錄使用Arkts?進行Harmony?app?開發學習的經歷。本職為嵌入式軟件開發,公司安排開發app,臨時學習,完成app的開發。開發流程和要點有些記憶模糊,趕緊記錄,防止忘記。

?相關鏈接:

開源 Arkts 鴻蒙應用 開發(一)工程文件分析-CSDN博客

開源 Arkts 鴻蒙應用 開發(二)封裝庫.har制作和應用-CSDN博客

開源 Arkts 鴻蒙應用 開發(三)Arkts的介紹-CSDN博客

開源 Arkts 鴻蒙應用 開發(四)布局和常用控件-CSDN博客

開源 Arkts 鴻蒙應用 開發(五)控件組成和復雜控件-CSDN博客

開源 Arkts 鴻蒙應用 開發(六)數據持久--文件和首選項存儲-CSDN博客

開源 Arkts 鴻蒙應用 開發(七)數據持久--sqlite關系數據庫-CSDN博客

開源 Arkts 鴻蒙應用 開發(八)多媒體--相冊和相機-CSDN博客

開源 Arkts 鴻蒙應用 開發(九)通訊--tcp客戶端-CSDN博客

開源 Arkts 鴻蒙應用 開發(十)通訊--Http數據傳輸-CSDN博客

?推薦鏈接:

開源 java android app 開發(一)開發環境的搭建-CSDN博客

開源 java android app 開發(二)工程文件結構-CSDN博客

開源 java android app 開發(三)GUI界面布局和常用組件-CSDN博客

開源 java android app 開發(四)GUI界面重要組件-CSDN博客

開源 java android app 開發(五)文件和數據庫存儲-CSDN博客

開源 java android app 開發(六)多媒體使用-CSDN博客

開源 java android app 開發(七)通訊之Tcp和Http-CSDN博客

開源 java android app 開發(八)通訊之Mqtt和Ble-CSDN博客

開源 java android app 開發(九)后臺之線程和服務-CSDN博客

開源 java android app 開發(十)廣播機制-CSDN博客

開源 java android app 開發(十一)調試、發布-CSDN博客

開源 java android app 開發(十二)封庫.aar-CSDN博客

推薦鏈接:

開源C# .net mvc 開發(一)WEB搭建_c#部署web程序-CSDN博客

開源 C# .net mvc 開發(二)網站快速搭建_c#網站開發-CSDN博客

開源 C# .net mvc 開發(三)WEB內外網訪問(VS發布、IIS配置網站、花生殼外網穿刺訪問)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-CSDN博客

開源 C# .net mvc 開發(四)工程結構、頁面提交以及顯示_c#工程結構-CSDN博客

開源 C# .net mvc 開發(五)常用代碼快速開發_c# mvc開發-CSDN博客

本章內容主要是演示鴻蒙的tcp客戶端的通訊:

實現TCP客戶端功能,可以連接指定IP和端口的服務器。支持發送和接收文本消息,顯示連接狀態和通信歷史。

新建DevEco的工程,選擇Empty Ability,只需要修改module.json5,和Index.ets文件,就可以實現TCP客戶端的APP。

一、設置權限,修改module.json5文件。

文件位置

?

module.json5代碼

{"module": {"name": "entry","type": "entry","description": "$string:module_desc","mainElement": "EntryAbility","deviceTypes": ["phone"],"deliveryWithInstall": true,"installationFree": false,"pages": "$profile:main_pages","abilities": [{"name": "EntryAbility","srcEntry": "./ets/entryability/EntryAbility.ets","description": "$string:EntryAbility_desc","icon": "$media:layered_image","label": "$string:EntryAbility_label","startWindowIcon": "$media:startIcon","startWindowBackground": "$color:start_window_background","exported": true,"skills": [{"entities": ["entity.system.home"],"actions": ["action.system.home"]}]}],"extensionAbilities": [{"name": "EntryBackupAbility","srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets","type": "backup","exported": false,"metadata": [{"name": "ohos.extension.backup","resource": "$profile:backup_config"}],}],"requestPermissions": [{"name": "ohos.permission.INTERNET"}]}
}

二、Index.ets的代碼分析

2.1??初始化

當TcpClientPage組件被加載時,首先會初始化所有@State裝飾的變量(serverIp, serverPort, message, receivedData, isConnected)

創建TCP套接字實例tcpSocket和獲取UI上下文context

aboutToAppear生命周期:

再次創建TCP套接字實例(這里重復創建了,可以優化)

設置TCP連接選項(address, port, family, timeout)

注冊三個事件監聽器:

message:接收服務器數據時觸發

close:連接關閉時觸發

error:發生錯誤時觸發

以下為代碼

// 初始化TCP套接字aboutToAppear() {this.tcpSocket = socket.constructTCPSocketInstance();// 設置TCP連接參數let tcpOptions: socket.TCPConnectOptions = {address: {address: this.serverIp,port: parseInt(this.serverPort),family: 1 // 1表示IPv4},timeout: 5000 // 連接超時時間5秒};// 監聽接收數據this.tcpSocket.on('message', (messageInfo: socket.SocketMessageInfo) => {console.info('Received message');// 從 SocketMessageInfo 中獲取 ArrayBufferconst buffer = messageInfo.message; // message 是 ArrayBuffer 類型const data = this.arrayBufferToString(buffer);this.receivedData += `[接收] ${new Date().toLocaleTimeString()}: ${data}\n`;});// 監聽連接關閉this.tcpSocket.on('close', () => {console.info('Connection closed');this.isConnected = false;promptAction.showToast({ message: '連接已關閉', duration: 2000 });});// 監聽錯誤事件this.tcpSocket.on('error', (err) => {console.error('Socket error: ' + JSON.stringify(err));promptAction.showToast({ message: '發生錯誤: ' + JSON.stringify(err), duration: 3000 });this.isConnected = false;});}

2.2? UI代碼

build()方法構建了以下UI元素:

服務器IP輸入框

端口號輸入框

連接/斷開按鈕(根據連接狀態改變文字和顏色)

消息輸入框

發送按鈕

消息顯示區域(帶滾動條)

以下為代碼

  build() {Column({ space: 10 }) {// 標題Text('TCP客戶端').fontSize(24).fontWeight(FontWeight.Bold).margin({ bottom: 20 })// 服務器地址輸入Row({ space: 10 }) {Text('服務器IP:').fontSize(16).width(80)TextInput({ text: this.serverIp }).width('60%').onChange((value: string) => {this.serverIp = value;})}.width('100%').justifyContent(FlexAlign.Start)// 端口號輸入Row({ space: 10 }) {Text('端口號:').fontSize(16).width(80)TextInput({ text: this.serverPort }).width('60%').onChange((value: string) => {this.serverPort = value;})}.width('100%').justifyContent(FlexAlign.Start)// 連接按鈕Button(this.isConnected ? '斷開連接' : '連接').width('80%').height(40).backgroundColor(this.isConnected ? '#ff4d4f' : '#1890ff').onClick(() => {this.toggleConnection();})// 消息輸入框TextInput({ placeholder: '輸入要發送的消息', text: this.message }).width('90%').height(60).margin({ top: 20 }).onChange((value: string) => {this.message = value;})// 發送按鈕Button('發送').width('80%').height(40).margin({ top: 10 }).onClick(() => {this.sendMessage();})// 消息顯示區域Scroll() {Text(this.receivedData).width('90%').fontSize(14).textAlign(TextAlign.Start).padding(10).backgroundColor('#f5f5f5')}.width('100%').height(200).margin({ top: 20 }).border({ width: 1, color: '#d9d9d9' })}.width('100%').height('100%').padding(20).justifyContent(FlexAlign.Start)}

2.3? 建立連接和發送

調用tcpSocket.connect()建立連接

點擊"發送"按鈕:

觸發sendMessage()方法

以下為代碼

// 連接到服務器private async connect() {try {let tcpOptions: socket.TCPConnectOptions = {address: {address: this.serverIp,port: parseInt(this.serverPort),family: 1},timeout: 5000};await this.tcpSocket.connect(tcpOptions);this.isConnected = true;promptAction.showToast({ message: '連接成功', duration: 2000 });// 開始接收數據this.tcpSocket.getState().then(state => {console.info('Socket state: ' + JSON.stringify(state));});} catch (err) {console.error('Connect failed: ' + JSON.stringify(err));promptAction.showToast({ message: '連接失敗: ' + JSON.stringify(err), duration: 3000 });this.isConnected = false;}}

2.4? 所有代碼

// MainAbility.ets
import socket from '@ohos.net.socket';
import common from '@ohos.app.ability.common';
import promptAction from '@ohos.promptAction';
import { BusinessError } from '@kit.BasicServicesKit';@Entry
@Component
struct TcpClientPage {@State serverIp: string = '192.168.3.146'; // 默認服務器IP@State serverPort: string = '1000'; // 默認端口@State message: string = ''; // 要發送的消息@State receivedData: string = ''; // 接收到的數據@State isConnected: boolean = false; // 連接狀態private tcpSocket: socket.TCPSocket = socket.constructTCPSocketInstance();private context = getContext(this) as common.UIAbilityContext;// 初始化TCP套接字aboutToAppear() {this.tcpSocket = socket.constructTCPSocketInstance();// 設置TCP連接參數let tcpOptions: socket.TCPConnectOptions = {address: {address: this.serverIp,port: parseInt(this.serverPort),family: 1 // 1表示IPv4},timeout: 5000 // 連接超時時間5秒};// 監聽接收數據this.tcpSocket.on('message', (messageInfo: socket.SocketMessageInfo) => {console.info('Received message');// 從 SocketMessageInfo 中獲取 ArrayBufferconst buffer = messageInfo.message; // message 是 ArrayBuffer 類型const data = this.arrayBufferToString(buffer);this.receivedData += `[接收] ${new Date().toLocaleTimeString()}: ${data}\n`;});// 監聽連接關閉this.tcpSocket.on('close', () => {console.info('Connection closed');this.isConnected = false;promptAction.showToast({ message: '連接已關閉', duration: 2000 });});// 監聽錯誤事件this.tcpSocket.on('error', (err) => {console.error('Socket error: ' + JSON.stringify(err));promptAction.showToast({ message: '發生錯誤: ' + JSON.stringify(err), duration: 3000 });this.isConnected = false;});}private arrayBufferToString(buffer: ArrayBuffer): string {const uint8Array = new Uint8Array(buffer);let str = '';for (let i = 0; i < uint8Array.length; i++) {str += String.fromCharCode(uint8Array[i]);}return str;}// 字符串轉ArrayBufferprivate stringToArrayBuffer(str: string): ArrayBuffer {let buf = new ArrayBuffer(str.length);let bufView = new Uint8Array(buf);for (let i = 0; i < str.length; i++) {bufView[i] = str.charCodeAt(i);}return buf;}// 連接/斷開服務器private toggleConnection() {if (this.isConnected) {this.disconnect();} else {this.connect();}}// 連接到服務器private async connect() {try {let tcpOptions: socket.TCPConnectOptions = {address: {address: this.serverIp,port: parseInt(this.serverPort),family: 1},timeout: 5000};await this.tcpSocket.connect(tcpOptions);this.isConnected = true;promptAction.showToast({ message: '連接成功', duration: 2000 });// 開始接收數據this.tcpSocket.getState().then(state => {console.info('Socket state: ' + JSON.stringify(state));});} catch (err) {console.error('Connect failed: ' + JSON.stringify(err));promptAction.showToast({ message: '連接失敗: ' + JSON.stringify(err), duration: 3000 });this.isConnected = false;}}// 斷開連接private disconnect() {try {this.tcpSocket.close();this.isConnected = false;promptAction.showToast({ message: '已斷開連接', duration: 2000 });} catch (err) {console.error('Disconnect failed: ' + JSON.stringify(err));promptAction.showToast({ message: '斷開連接失敗: ' + JSON.stringify(err), duration: 3000 });}}private sendMessage() {if (!this.isConnected) {promptAction.showToast({ message: '請先連接到服務器', duration: 2000 });return;}if (!this.message.trim()) {promptAction.showToast({ message: '消息不能為空', duration: 2000 });return;}try {const buffer = this.stringToArrayBuffer(this.message);this.tcpSocket.send({ data: buffer }).then(() => {this.receivedData += `[發送] ${new Date().toLocaleTimeString()}: ${this.message}\n`;this.message = '';}).catch((err: BusinessError) => {console.error(`Send failed: code=${err.code}, message=${err.message}`);promptAction.showToast({ message: `發送失敗: ${err.message}`, duration: 3000 });});} catch (err) {console.error(`Send error: code=${err.code}, message=${err.message}`);promptAction.showToast({ message: `發送錯誤: ${err.message}`, duration: 3000 });}}build() {Column({ space: 10 }) {// 標題Text('TCP客戶端').fontSize(24).fontWeight(FontWeight.Bold).margin({ bottom: 20 })// 服務器地址輸入Row({ space: 10 }) {Text('服務器IP:').fontSize(16).width(80)TextInput({ text: this.serverIp }).width('60%').onChange((value: string) => {this.serverIp = value;})}.width('100%').justifyContent(FlexAlign.Start)// 端口號輸入Row({ space: 10 }) {Text('端口號:').fontSize(16).width(80)TextInput({ text: this.serverPort }).width('60%').onChange((value: string) => {this.serverPort = value;})}.width('100%').justifyContent(FlexAlign.Start)// 連接按鈕Button(this.isConnected ? '斷開連接' : '連接').width('80%').height(40).backgroundColor(this.isConnected ? '#ff4d4f' : '#1890ff').onClick(() => {this.toggleConnection();})// 消息輸入框TextInput({ placeholder: '輸入要發送的消息', text: this.message }).width('90%').height(60).margin({ top: 20 }).onChange((value: string) => {this.message = value;})// 發送按鈕Button('發送').width('80%').height(40).margin({ top: 10 }).onClick(() => {this.sendMessage();})// 消息顯示區域Scroll() {Text(this.receivedData).width('90%').fontSize(14).textAlign(TextAlign.Start).padding(10).backgroundColor('#f5f5f5')}.width('100%').height(200).margin({ top: 20 }).border({ width: 1, color: '#d9d9d9' })}.width('100%').height('100%').padding(20).justifyContent(FlexAlign.Start)}
}

三、效果演示

3.1? 首先搭建服務器端,這里使用了網絡調試助手,設置ip和端口號,打開。

3.2? APP的效果和設置

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/93098.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/93098.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/93098.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Go的defer和recover

在 Go 語言中&#xff0c;defer 和 recover 是兩個緊密相關的關鍵字&#xff0c;主要用于錯誤處理和資源清理。它們通常一起使用&#xff0c;特別是在處理panic&#xff08;運行時崩潰&#xff09;時&#xff0c;確保程序不會直接崩潰&#xff0c;而是能夠優雅地恢復并繼續執行…

Spring Boot 配置文件常用配置屬性詳解(application.properties / application.yml)

前言 Spring Boot 的一大優勢就是通過簡單的配置文件即可快速定制應用行為&#xff0c;而無需編寫大量 XML 配置或 Java 代碼。Spring Boot 使用 application.properties 或 application.yml 作為核心配置文件&#xff0c;支持豐富的配置屬性。 本文將詳細介紹 Spring Boot 常用…

uni-appDay02

1.首頁-通用輪播組件 輪播圖組件需要再首頁和分類頁使用&#xff0c;封裝成通用組件 準備組件自動導入組件 <script setup lang"ts"> import XtxSwiper from /components/XtxSwiper.vue import CustomNavbar from ./components/CustomNavbar.vue </scrip…

FastAPI入門:請求體、查詢參數和字符串校驗、路徑參數和數值校驗

請求體 FastAPI 使用請求體從客戶端&#xff08;例如瀏覽器&#xff09;向 API 發送數據。請求體是客戶端發送給 API 的數據。響應體是 API 發送給客戶端的數據。 使用 Pydantic 模型聲明請求體&#xff0c;能充分利用它的功能和優點 from fastapi import FastAPI from pydanti…

Docker的docker-compose類比Spring的ApplicationContext

總一句話是&#xff1a;Docker Compose&#xff1a;集中化管理多個容器及其依賴的資源環境&#xff1b;ApplicationContext&#xff1a;集中化管理 多個Bean 及其運行所需的資源和依賴關系。 1. 整體概念 Docker Compose&#xff1a;用于定義和運行多容器 Docker 應用程序&…

Reason-before-Retrieve(CVPR 2025)

研究方向&#xff1a;Image Captioning論文全名&#xff1a;《Reason-before-Retrieve: One-Stage Reflective Chain-of-Thoughts for Training-Free Zero-Shot Composed Image Retrieval》1. 論文介紹組合圖像檢索&#xff08;CIR&#xff09;旨在檢索與參考圖像密切相似的目標…

Idefics2:構建視覺-語言模型時,什么是重要的

溫馨提示&#xff1a; 本篇文章已同步至"AI專題精講" Idefics2&#xff1a;構建視覺-語言模型時&#xff0c;什么是重要的 摘要 隨著large language models和vision transformers的進步&#xff0c;視覺-語言模型&#xff08;VLMs&#xff09;受到了越來越多的關注…

再談fpga開發(fpga調試方法)

【 聲明&#xff1a;版權所有&#xff0c;歡迎轉載&#xff0c;請勿用于商業用途。 聯系信箱&#xff1a;feixiaoxing 163.com】我們之前在學校學習c、c的時候&#xff0c;其實學校漏掉了很重要的一個教學環節&#xff0c;那就是調試、測試。很多時候我們代碼寫出來了&#xff…

C語言中的數據結構--棧和隊列(1)

前言本屆開始我們將對數據結構中棧的內容進行講解,那么廢話不多說,我們正式進入今天的學習棧棧是一種很特殊的線性表&#xff0c;它只能在固定的一端進行插入和刪除操作&#xff0c;進行數據的插入和刪除的一端叫做棧頂&#xff0c;另外一端叫做棧底&#xff0c;棧中的元素遵守…

字符串是數據結構還是數據類型?

比較糾結的一個問題&#xff0c;以下是在網上查到后總結的&#xff0c;不知道對不對&#xff0c;歡迎討論。這是個觸及計算機科學核心概念的精妙問題&#xff01;字符串既可以被視為一種數據類型&#xff0c;也可以被視為一種數據結構&#xff0c;這取決于你觀察的視角和討論的…

Cline與Cursor深度實戰指南:AI編程助手的革命性應用

引言 在AI編程工具快速發展的今天&#xff0c;Cline和Cursor作為兩款備受矚目的AI編程助手&#xff0c;正在重新定義開發者的工作方式。作為一名深度使用這兩款工具的開發者&#xff0c;我在過去一年的實踐中積累了豐富的經驗和獨到的見解。本文將從技術角度深入分析Cline和Cur…

根本是什么

根本是什么 根本沒有了&#xff0c;枝葉還在么&#xff1f; 沒有了內涵&#xff0c;外延還有么&#xff1f; 丟棄了根本&#xff0c;再嗨也是無意義&#xff0c;無根據空虛之樂罷了。 人之所行所言所思所想所念皆欲念、歷程感懷&#xff0c;情思。所謂得失過往&#xff0c;時空…

springboot基于Java的人力資源管理系統設計與實現

管理員&#xff1a;登錄&#xff0c;個人中心&#xff0c;部門管理&#xff0c;員工管理&#xff0c;培訓信息管理&#xff0c;員工獎勵管理&#xff0c;員工懲罰管理員工考核管理&#xff0c;調薪信息管理&#xff0c;員工調動管理&#xff0c;員工工資管理員工&#xff1a;注…

金字塔降低采樣

文章目錄image_scale.hppimage_scale.cppmainimage_scale.hpp #ifndef IMAGE_SCALE_HPP #define IMAGE_SCALE_HPP#include <vector> #include <cstdint> #include <utility> // for std::pair #include <algorithm> #include <string> enum cl…

Filament引擎(四)——光照渲染Froxelizer實現分析

Froxelizer主要是用于filament光照效果的實現&#xff0c;生成光照渲染時所需的必要信息&#xff0c;幫助渲染過程中明確哪些區域受哪些光源所影響&#xff0c;是Filament中保證光照效果渲染效率的核心所在。這部分的源碼&#xff0c;可以結合filament官方文檔中Light Path部分…

2025 環法對決,VELO Angel Glide 坐墊輕裝上陣

2025環法第16賽段的風禿山之巔&#xff0c;當最后一縷夕陽沉入云層&#xff0c;山風裹挾著礫石的氣息掠過賽道&#xff0c;一場足以載入史冊的激戰正酣。帕雷-潘特的肌肉在汗水里賁張&#xff0c;鏈條與齒輪的咬合聲混著粗重喘息&#xff0c;在171.5公里賽程的最后3公里陡坡上&…

Linux程序->進度條

進度條最終效果&#xff1a; 目錄 進度條最終效果&#xff1a; 一&#xff1a;兩個須知 1&#xff1a;緩沖區 ①&#xff1a;C語言自帶緩沖區 ②&#xff1a;緩沖區的刷新策略 2&#xff1a;回車和換行的區別 二&#xff1a;倒計時程序 三&#xff1a;入門板進度條的實…

Python爬蟲實戰:研究tldextract庫相關技術構建新聞網站域名分析爬蟲系統

1. 引言 網絡爬蟲作為一種自動獲取互聯網信息的技術,在數據挖掘、信息檢索、輿情分析等領域有著廣泛的應用。Python 因其豐富的庫和簡潔的語法,成為了開發爬蟲的首選語言。tldextract 是 Python 中一個強大的域名解析庫,能夠準確地從 URL 中提取頂級域名、二級域名等關鍵信…

【算法-華為機試-火星基地改造】

基地改造題目描述目標輸入輸出代碼實現題目描述 在2XXX年&#xff0c;人們發現了一塊火星地區&#xff0c;這里看起來很適合建設新家園。但問題是&#xff0c;我們不能一次性將這片地區的空氣變得適合人類居住&#xff0c;得分步驟來。 把這片火星地區想象成一個巨大的棋盤。棋…

C++入門自學Day1-- C語言的宏函數和C++內聯函數

一、函數調用開銷函數調用會涉及&#xff1a;參數壓棧&#xff08;或寄存器傳參&#xff09;跳轉到函數體返回值處理棧幀銷毀這個過程對小函數來說可能非常浪費&#xff0c;因此&#xff0c;宏函數和內聯函數的目的就是避免“函數調用的開銷”&#xff0c;通過代碼展開&#xf…