鴻蒙網絡編程系列54-倉頡版實現Smtp郵件發送客戶端

1. SMTP郵件發送客戶端

在本系列的第4篇文章《鴻蒙網絡編程系列4-實現SMTP郵件發送客戶端》中,基于ArkTS語言在API9環境下使用TCPSocket對象演示了SMTP客戶端的實現,并且通過騰訊郵件服務器執行了實際的郵件發送。不過,在2024年末,騰訊發了一個通知,從2024年11月20日開始,停用以明文非加密方式登錄的第三方郵件客戶端,必需啟用SSL/TLS加密方式。不過,除了騰訊郵件發送服務器,還有很多其他郵件服務器支持使用明文登錄,其中比較知名的有搜狐郵箱,可以通過如下的方式啟用:

保存的時候,搜狐郵箱會自動生成獨立密碼,將來可以使用這個密碼執行登錄。

本文將使用倉頡語言在API17環境下實現SMTP郵件發送客戶端,具體的郵件發送將通過搜狐郵箱實現,關于SMTP協議的相關基礎知識,可以參考本系列第4篇文章的第一部分,這里不再贅述。

2. 郵件發送客戶端示例演示

本示例運行后的頁面如圖所示:

輸入SMTP服務器地址和端口(這里輸入的是搜狐郵箱發送服務器的地址),再輸入郵箱用戶名和登錄密碼,此時就可以單擊“登錄”按鈕執行登錄了,如圖所示:

登錄成功后,輸入收件人、發件人郵箱地址以及郵件的標題和內容,再單擊下面的“發送郵件”按鈕,既可以執行郵件發送,過程如下所示:

發送成功后,登錄收件人的郵箱,就可以查看發送的郵件了,郵件內容如下所示:

3. 郵件發送客戶端示例編寫

下面詳細介紹創建該示例的步驟(確保DevEco Studio已安裝倉頡插件)。

步驟1:創建[Cangjie]Empty Ability項目。

步驟2:在module.json5配置文件加上對權限的聲明:

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

這里添加了訪問互聯網的權限。

步驟3:在build-profile.json5配置文件加上倉頡編譯架構:

"cangjieOptions": {"path": "./src/main/cangjie/cjpm.toml","abiFilters": ["arm64-v8a", "x86_64"]}

步驟4:在index.cj文件里添加如下的代碼:

package ohos_app_cangjie_entryimport ohos.base.*
import ohos.component.*
import ohos.state_manage.*
import ohos.state_macro_manage.*
import std.collection.HashMap
import std.convert.*
import std.net.*
import std.socket.*
import encoding.base64.toBase64String@Entry
@Component
class EntryView {@Statevar title: String = 'SMTP郵件發送客戶端示例';//連接、通訊歷史記錄@Statevar msgHistory: String = ''//服務器是否響應(發送數據到客戶端)var isServerResponse: Bool = false//服務端地址,smtp.sohu.com的ip地址為116.130.217.16@Statevar serverAddr: String = "116.130.217.16"//服務端端口,smtp.sohu.com的端口為25,不同的smtp服務器端口可能不一樣@Statevar serverPort: UInt16 = 25//用戶名@Statevar userName: String = "youmail@sohu.com"//密碼,對于搜狐郵箱,這里是獨立密碼@Statevar passwd: String = "youpassword"//收件人郵箱列表(如果多個使用逗號分隔)@Statevar rcptList: String = "*****@sohu.com,****@qq.com"//發件人郵箱@Statevar mailFrom: String = "youmail@sohu.com"//郵件標題@Statevar mailTitle: String = "測試郵件標題"//郵件內容@Statevar mailContent: String = "這是來自鴻蒙的問候!"//是否正在登錄@Statevar isLogin: Bool = false//是否可以發送郵件@Statevar canSend: Bool = false//TCP客戶端var tcpClient: ?TcpSocket = Nonelet scroller: Scroller = Scroller()func build() {Row {Column {Text(title).fontSize(14).fontWeight(FontWeight.Bold).width(100.percent).textAlign(TextAlign.Center).padding(10)Flex(FlexParams(justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center)) {Text("SMTP服務器地址:").fontSize(14)TextInput(text: serverAddr).onChange({value => serverAddr = value}).width(100).fontSize(11).flexGrow(1)Text(":").fontSize(14)TextInput(text: serverPort.toString()).onChange({value => serverPort = UInt16.parse(value)}).setType(InputType.Number).width(80).fontSize(11)}.width(100.percent).padding(5)Flex(FlexParams(justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center)) {Text("郵箱用戶名:").fontSize(14).width(100).flexGrow(0)TextInput(text: userName).onChange({value => userName = value}).width(110).fontSize(12).flexGrow(1)}.width(100.percent).padding(5)Flex(FlexParams(justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center)) {Text("登錄密碼:").fontSize(14).width(100).flexGrow(0)TextInput(text: passwd).onChange({value => passwd = value}).setType(InputType.Password).width(110).fontSize(12).flexGrow(1)Button("登錄").onClick {evt => login()}.enabled(!isLogin && userName != "" && passwd != "").width(70).fontSize(14)}.width(100.percent).padding(5)Flex(FlexParams(justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center)) {Text("收件人郵箱:").fontSize(14).width(100).flexGrow(0)TextArea(placeholder: "多個收件人使用逗號分隔", text: rcptList).onChange({value => rcptList = value}).width(110).fontSize(12).flexGrow(1)}.width(100.percent).padding(5)Flex(FlexParams(justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center)) {Text("發件人郵箱:").fontSize(14).width(100).flexGrow(0)TextInput(text: mailFrom).onChange({value => mailFrom = value}).width(110).fontSize(12).flexGrow(1)}.width(100.percent).padding(5)Flex(FlexParams(justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center)) {Text("郵件標題:").fontSize(14).width(100).flexGrow(0)TextInput(text: mailTitle).onChange({value => mailTitle = value}).width(110).fontSize(12).flexGrow(1)}.width(100.percent).padding(5)Flex(FlexParams(direction: FlexDirection.Column, justifyContent: FlexAlign.Start,alignItems: ItemAlign.Center)) {Text("郵件內容:").fontSize(14).width(100.percent)TextArea(placeholder: "請輸入要發送的郵件內容", text: mailContent).onChange({value => mailContent = value}).width(100.percent).height(80).fontSize(12)Row() {Button("發送郵件").onClick {evt => sendMail()}.enabled(canSend).width(100).fontSize(14)}.width(100.percent).justifyContent(FlexAlign.End)Scroll(scroller) {Text(msgHistory).textAlign(TextAlign.Start).padding(10).width(100.percent).backgroundColor(0xeeeeee)}.align(Alignment.Top).backgroundColor(0xeeeeee).height(200).flexGrow(1).scrollable(ScrollDirection.Vertical).scrollBar(BarState.On).scrollBarWidth(20)}.width(100.percent).padding(5).flexGrow(1).height(300)}.width(100.percent).height(100.percent)}.height(100.percent)}//發送命令到服務器func sendCmd2ServerWithCRLF(cmd: String) {let fullCmd: String = cmd + "\r\n"tcpClient?.write(fullCmd.toArray())msgHistory += "C:${cmd}\r\n"}//從服務器讀取消息func readMsgFromServer() {let buffer = Array<UInt8>(1024, item: 0)//從socket讀取數據var readCount = tcpClient?.read(buffer)//把接收到的數據轉換為字符串let content = String.fromUtf8(buffer[0..readCount.getOrThrow()])msgHistory += "S:${content}"return content}//登錄func login() {tcpClient = TcpSocket(serverAddr, serverPort)isLogin = true//啟動一個線程執行登錄spawn {try {tcpClient?.connect()msgHistory += "C:連接成功!\r\n"} catch (err: Exception) {msgHistory += "C:連接失敗${err.message}!\r\n"isLogin = falsereturn}try {sendCmd2ServerWithCRLF("ehlo anyname")var content = readMsgFromServer()sendCmd2ServerWithCRLF("auth login")content = readMsgFromServer()sendCmd2ServerWithCRLF(toBase64String(userName.toArray()))content = readMsgFromServer()sendCmd2ServerWithCRLF(toBase64String(passwd.toArray()))content = readMsgFromServer()canSend = true} catch (exp: Exception) {msgHistory += "從Socket讀取數據錯誤:${exp}\r\n"}isLogin = false}}func sendMail() {//啟動一個線程執行發送spawn {try {sendCmd2ServerWithCRLF("mail from:<${mailFrom}>")var content = readMsgFromServer()for (rcpt in rcptList.split(",")) {sendCmd2ServerWithCRLF("rcpt to:<${rcpt}>")content = readMsgFromServer()}//準備發送郵件內容sendCmd2ServerWithCRLF("data")content = readMsgFromServer()let mailBody = "Subject: ${mailTitle} \r\nFrom: ${mailFrom}\r\n\r\n${mailContent}\r\n."sendCmd2ServerWithCRLF(mailBody)content = readMsgFromServer()sendCmd2ServerWithCRLF("quit")content = readMsgFromServer()} catch (exp: Exception) {msgHistory += "從套接字讀取數據錯誤:${exp}\r\n"}}}
}

步驟5:編譯運行,可以使用模擬器或者真機。

步驟6:按照本文第2部分“郵件發送客戶端示例演示”操作即可。

4. 代碼分析

本文的核心代碼主要是兩個函數,第一個是發送命令到服務器的函數sendCmd2ServerWithCRLF,該函數在發送命令給服務器時,會在命令后面添加回車換行符號,然后調用tcpClient的write函數執行實際的發送。第二個是從服務器讀取消息的函數readMsgFromServer,該函數會從套接字讀取數據并寫入到緩沖區buffer中,然后把數據轉換為字符串。

需要特別注意的是,為了簡化開發,第二個函數假設可以一次性讀取服務器的完整回復,并且服務器的回復不超過1024字節,這個假設一般是成立的,不過,在一些特殊情況下,比如網絡不太好,或者網絡數據“粘包”,可能會出現接收問題。這時候,可以通過更復雜的代碼來解決,這里就不展開了,可以參考本系列相關的“TCP粘包”文章。

(本文作者原創,除非明確授權禁止轉載)

本文源碼地址:
https://gitee.com/zl3624/harmonyos_network_samples/tree/master/code/tcp/SmtpClient4Cj

本系列源碼地址:
https://gitee.com/zl3624/harmonyos_network_samples

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

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

相關文章

【慧游魯博】【12】UI美化·圖標選擇與變換·動態交互·格式定義

文章目錄 圖標設計迭代過程初始版本問題分析優化措施 游覽畫卷美化原因當前效果展示美化步驟(1) 代碼修改結構優化CSS&#xff08;優化樣式&#xff09; (2) 圖標選擇&#xff08;4種方案&#xff09;(3) 交互優化 版本一版本二1. 修改HTML結構2. 新增CSS樣式色彩控制技術性能優…

IMU介紹

IMU(Inertial Measurement Unit,慣性測量單元)是一種基于慣性原理的傳感器,通過測量物體的加速度和角速度來獲取運動狀態信息。以下從技術原理、核心組件、應用場景及關鍵指標等方面展開詳細解析: 一、IMU的技術原理與核心組件 1. 工作原理 慣性力學基礎:利用牛頓第二定…

MOS管和比較器

目錄 前言一、前置器件復習使用1.比較器工作特性2.光電二極管3.紅外出水水龍頭4.溫控風扇工作原理 二、MOS管1.前置1.1 增強型MOS管1.2 耗盡型MOS管1.3 四種1.4 比較 2.基本結構3.導通條件4.開關電路的設計方法5.寄生電容問題6.寄生二極管不能忽略7.Nmos管做電源開關的注意事項…

從代碼學習深度強化學習 - Double DQN PyTorch版

文章目錄 前言理論篇:為什么需要 Double DQN?代碼實現篇:構建一個 Double DQN 智能體2.1 項目設置與輔助函數2.2 環境 (Environment)2.3 DQN 的核心組件2.3.1 Replay Buffer (經驗回放池)2.3.2 Q-Network (Q網絡)2.3.3 The Double DQN Agent (Double DQN 智能體)訓練與結果3…

四非鼠鼠計算機專業的保研分享

四非鼠鼠的計算機專業保研分享 1.前言 鼠鼠的本科學校是一所不怎么出名的四非院校&#xff0c;專業是計算機科學與技術。在寫下這篇文章時&#xff0c;鼠鼠并不是為了炫耀什么&#xff0c;而是想把自己在保研路上的一些踩坑經歷分享出來&#xff0c;尤其是寫給那些和我一樣&a…

【C++詳解】STL-vector使用底層剖析和實現

文章目錄 vector介紹vector和string的區別補充知識initializer_listemplace_back結構化綁定 vector的使用構造析構遍歷修改insertfind流插入/流提取vector\<vector>(楊輝三角) vector模擬實現淺品STL源碼構造函數拷貝構造多參數構造迭代器區間構造n個val初始化swapoperat…

MySql升級安裝、socket 及密碼重置

升級 項目需要使用Mysql8.0, 查看自己的ubuntu22.04上mysql版本為5.7&#xff0c; 使用以下命令自動升級到8.0版本。 sudo apt install Mysqlsock錯誤&#xff1a; Can’t connect to local MySQL server through socket 運行mysql -u -p 報以下錯誤&#xff1a; ERROR 200…

Python網絡爬蟲技術:從入門到實戰

在當今數字化時代&#xff0c;網絡爬蟲技術已經成為數據挖掘和信息收集的重要工具。通過網絡爬蟲&#xff0c;我們可以高效地從互聯網上獲取大量有價值的數據&#xff0c;用于數據分析、市場研究、學術研究等多種場景。本文將帶你從零開始&#xff0c;了解Python網絡爬蟲的基本…

偏微分方程初值問題求解

題目 問題 2. (a) u t + 3 u x ? 2 u y = x ; u t + x u x + y u y = x ; u_t + 3u_x - 2u_y = x; \quad u_t + xu_x + yu_y = x; ut?+3ux??2uy?=x;ut?+xux?+yuy?=x; u t + x u x ? y u y = x ; u t + y u x + x u y = x ; u_t + xu_x - yu_y = x; \quad u_t + yu_…

【專業梳理】PMP知識體系,以SIPOC流程圖為核心的質量工具擴展

??1. SIPOC流程圖:質量管理的起點?? SIPOC(Supplier-Input-Process-Output-Customer)是六西格瑪和流程管理中的核心工具,用于定義和優化跨職能流程。在PMBOK中,它與質量管理知識領域(尤其是質量規劃、質量保證)緊密關聯: ??質量規劃??:通過SIPOC明確流程邊界…

OpenCV指定pid和vid通過MSMF打開攝像頭

在基于OpenCV的項目中&#xff0c;實際開發過程會面臨設備上存在多個攝像頭&#xff0c;需要指定攝像頭的pid和vid打開攝像頭。在OpenCV通過MSMF打開攝像頭時&#xff0c;需要傳入攝像頭的index&#xff0c;因此需要在打開該攝像頭前需要找出攝像頭的index&#xff0c;下面給出…

STM32F103ZET6系統啟動過程

STM32F103ZET6系統啟動過程 一、概述 STM32F103ZET6啟動過程指硬件選擇啟動模式后,執行固件程序之前的一系列動作。對于系統存儲器模式,系統執行Bootloader程序升級狀態,檢測數據進行串口升級;對于內部Flash模式,系統執行啟動文件,設置堆棧大小,配置系統時鐘,最終調用…

[Data Pipeline] Kafka消息 | Redis緩存 | Docker部署(Lambda架構)

第七章&#xff1a;Kafka消息系統&#xff08;實時流處理&#xff09; 歡迎回到數據探索之旅&#xff01; 在前六章中&#xff0c;我們構建了強大的**批量處理流水線**。 通過Airflow DAG&#xff08;批量任務編排&#xff09;協調Spark作業&#xff08;數據處理&#xff09;…

jquery 賦值時不觸發change事件解決——仙盟創夢IDE

一、傳統方法jquey change $(#village_id).trigger(change);$("#village_id").val(99);$("#village_id").change(); 不生效 二、傳統方法jquey $(#village_id).trigger(change); 四、傳統方法jquey <input type"text" /> <button…

Android | 簽名安全

檢驗和簽名 校驗開發者在數據傳送時采用的一種校正數據的一種方式&#xff0c; 常見的校驗有:簽名校驗(最常見)、dexcrc校驗、apk完整性校驗、路徑文件校驗等。 通過對 Apk 進行簽名&#xff0c;開發者可以證明對 Apk 的所有權和控制權&#xff0c;可用于安裝和更新其應用。…

Android14 耳機按鍵拍照

在相機拍照預覽界面 通過耳機按鍵實現拍照功能 耳機按鍵定義 frameworks/base/core/java/android/view/KeyEvent.java public static final int KEYCODE_HEADSETHOOK 79;相機界面 拍照邏輯 DreamCamera2\src\com\android\camera\PhotoModule.java Override public bool…

【AI作畫】第2章comfy ui的一般輸入節點,文本框的類型和輸入形式

目錄 CLIP文本編碼器 條件輸出和文本輸出 轉換某一變量為輸入 展示作品集 在默認的工作流之外&#xff0c;我們如何自己添加節點呢&#xff1f; 一般我們用到的sampler采樣器在“鼠標右鍵——添加節點——采樣——K采樣器” 我們用的clip文本編碼器在“鼠標右鍵——添加節…

vue3仿高德地圖官網路況預測時間選擇器

<template><div class"time-axis-container"><div class"time-axis" ref"axisRef"><!-- 刻度線 - 共25個刻度(0-24) --><divv-for"hour in 25":key"hour - 1"class"tick-mark":class&…

ZArchiver:高效解壓縮,輕松管理文件

在數字時代&#xff0c;文件的壓縮與解壓已成為我們日常操作中不可或缺的一部分。無論是接收朋友分享的大文件&#xff0c;還是下載網絡資源&#xff0c;壓縮包的處理都極為常見。ZArchiver正是一款為安卓用戶精心打造的解壓縮軟件&#xff0c;它以強大的功能、簡潔的界面和高效…

1432.改變一個整數能得到的最大差值

貪心思想&#xff0c;為了得到最大差&#xff0c;想辦法變成一個最大的數和一個最小的數。 這里有規則&#xff0c;從最高位開始&#xff0c; 變成最大&#xff0c;如果<9&#xff0c;則將該數位代表的數都變成9&#xff0c;如果該數位已經是9了&#xff0c;則將下一個數位…