鸿蒙OS开发实战:【Socket小试MQTT连接】

本篇分享一下 HarmonyOS 中的Socket使用方法

将从2个方面实践:

  1. HarmonyOS 手机应用连接PC端 SocketServer
  2. HarmonyOS 手机应用连接MQTT 服务端

通过循序渐进的方式,全面了解实践HarmonyOS中的Socket用法

学习本章前先熟悉文档开发知识更新库gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md前往。

V2.0白皮书.png

注意:编译时IDE会给出如下警告提示,@ohos.net.socket 这个包没有验证过。

或者+mau123789熟悉文档是v喔!

Currently module for ‘@ohos.net.socket’ is not verified. If you’re importing napi, its verification will be enabled in later SDK version. Please make sure the corresponding .d.ts file is provided and the napis are correctly declared.

官方文档说明

引入Socket包

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

创建TCPSocket,连接远端服务器

//注意,这里声明变量tcp, 建议将类型加上,否则不利于IDE联想API
let tcp: socket.TCPSocket = socket.constructTCPSocketInstance();let bindAddress = {address: '192.168.xx.xx',port: x
};let connectAddress = {address: '192.168.xx.xx',port: x
};//1. 绑定本机地址和端口
tcp.bind(bindAddress, err => {if(err){console.log('1-' + JSON.stringify(err))return}//2. 连接远端服务器tcp.connect({address: connectAddress,timeout: 30000}).then( r => {console.log('2-' +JSON.stringify(r))}).catch((e) => {console.log('3-' + JSON.stringify(e))})})
复制

状态监听

订阅“连接”

tcp.on('connect', () => {});
复制

解除“连接”订阅

tcp.off('connect', () => {});
复制

订阅“消息”

tcp.on('message', () => {//即:可以在这里随时接收到服务端发送过来的消息
});
复制

解除“消息”订阅

tcp.off('message', () => {});
复制

发送消息

let msg: string = '我是HarmonyOS客户端'tcp.send({data: msg}, (error)=>{if(error) {console.log('消息没有发送成功: ' + JSON.stringify(error))} else {console.log('消息发送成功')}
})
复制

实践:实现Socket与ServerSocket通信

创建ServerSocket 服务

这里使用IntelliJ IDEA创建一个Java工程,然后运行在自己电脑上即可使用

如果你实在没法自己实现ServerSocket,可以在网上找一个调试Socket的工具

监听客户端接入

package com.harvey.socketserver;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class Main {public static void main(String[] args) {try {ServerSocket serverSocket = new ServerSocket(6666);//一. 建立客户端监听new Thread(new Runnable() {@Overridepublic void run() {while (true){try {Socket clientSocket = serverSocket.accept();System.out.println("客户端:" + clientSocket.getInetAddress().getLocalHost()+"已连接到服务器");new Server(clientSocket).start();} catch (IOException e) {e.printStackTrace();}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}).start();} catch (IOException e) {e.printStackTrace();}}}
复制

监听客户端消息且自动回复

package com.harvey.socketserver;import java.io.*;
import java.net.Socket;public class Server extends Thread{Socket clientSocket;InputStream is;OutputStream os;String lastReceiverMessage;int LIVE_TIME = 60*1000;public Server(Socket clientSocket){this.clientSocket = clientSocket;try {is = this.clientSocket.getInputStream();os = this.clientSocket.getOutputStream();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void run() {while(LIVE_TIME != 0){if ( this.clientSocket != null ) {if ( this.clientSocket.isClosed() || this.clientSocket.isInputShutdown() || this.clientSocket.isOutputShutdown()) {LIVE_TIME = 0;} else {readMessage();responseMessage();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}LIVE_TIME -= 1000;}} else {LIVE_TIME = 0;}}closeAllChannel();}//释放资源private void closeAllChannel(){try {if(clientSocket != null){clientSocket.close();clientSocket = null;}if(is != null){is.close();is = null;}if(os != null){os.close();os = null;}} catch (IOException e) {e.printStackTrace();}}//读取客户端消息, 注意:这里是为了省事,用的readLine接口//如果消息中有换行符,则会丢失消息private void readMessage(){BufferedReader br = new BufferedReader(new InputStreamReader(is));try {lastReceiverMessage  = br.readLine();System.out.println("已接收到端消息:【" + lastReceiverMessage +"】");} catch (IOException e) {System.err.println("接收消息失败:" + e.getMessage());}}//自动回复客户端消息private void responseMessage(){try {BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));bw.write(System.currentTimeMillis() + "亲爱的客户端,已收到您的来信。"  +  lastReceiverMessage + "\n");bw.flush();System.out.println("回复消息成功");} catch (IOException e) {System.err.println("回复消息失败:" + e.getMessage());}}}
复制

创建客户端应用

布局

为了验证Socket,交互页面包含如下功能:

  1. 连接/断开
  2. 渲染接收到的消息
  3. 渲染已发送的消息
  4. 文字输入
  5. 消息发送

HarmonyOS 核心代码

import socket from '@ohos.net.socket'
import util from '@ohos.util';
import Prompt from '@system.prompt';let tcpSocket: socket.TCPSocket = null@Entry
@Component
struct SocketPage {@State isConnected: boolean = false@State sendMessage: string = ''@State inputMessage: string = ''@State receiveMessage: string = ''@State connectButtonBGColor: Color = Color.Gray@State connectButtonTextColor: Color = Color.White//页面创建时,注册自定义消息,监听来消息和连接状态aboutToAppear(){getContext().eventHub.on('remotemsg', (value)=>{this.receiveMessage = value})getContext().eventHub.on('connectStatus', (value)=>{if(value === 'connect'){this.connectButtonBGColor = Color.Green} else if(value === 'close'){this.connectButtonBGColor = Color.Gray}})}//页面销毁时,解除所有订阅,关闭socketaboutToDisappear() {tcpSocket.off("message")tcpSocket.off("connect")tcpSocket.off("close")tcpSocket.close()tcpSocket = null}build(){Column( {space: 20} ){Row( {space: 20} ){Button('连接').width(60).height(60).fontSize(12).backgroundColor(this.connectButtonBGColor).fontColor(this.connectButtonTextColor).onClick( ()=> {if(this.isConnected){tcpSocket.close()} else {connectServer()}})}Column({space: 30}){Text('发送:' + this.sendMessage).fontSize(20).width('100%')Text('接收:' + this.receiveMessage).fontSize(20).width('100%')}.backgroundColor(Color.Pink).padding( { top: 20, bottom: 20} )Row({space: 10}) {TextArea({placeholder: '输入文字', text: this.inputMessage}).onChange( (value) => {this.inputMessage = value}).fontSize(20).width('75%')Button('发送').fontColor(29).onClick( () => {sendMessage(this.inputMessage)this.sendMessage = this.inputMessagethis.inputMessage = ''}).width('20%')}.width('100%').justifyContent(FlexAlign.Center)}.width('100%').height('100%').alignItems(HorizontalAlign.Start).justifyContent(FlexAlign.Start).padding({top: px2vp(120)})}
}//发送消息
function sendMessage(msg: string){if(tcpSocket){tcpSocket.send({data: msg + '\n'}, (error)=>{if(error) {console.log('消息没有发送成功: ' + JSON.stringify(error))} else {getContext().eventHub.emit('')console.log('消息发送成功')}})} else {Prompt.showToast({message: '还没有连接服务器'})}
}//开始连接服务器
function connectServer(){let bindAddress = {address: '192.168.71.66',port: 1983,family: 1};let connectAddress = {address: '192.168.71.23',port: 6666 //端口号要和ServerSocket 一致};tcpSocket = socket.constructTCPSocketInstance()tcpSocket.on('close', () => {console.log("on close")getContext().eventHub.emit('connectStatus', 'close')});tcpSocket.on('connect', () => {console.log("on connect")getContext().eventHub.emit('connectStatus', 'connect')});tcpSocket.on('message' , ( value: {message: ArrayBuffer, remoteInfo: socket.SocketRemoteInfo} ) => {let view = new Uint8Array(value.message);let textDecoder = util.TextDecoder.create()let str = textDecoder.decodeWithStream(view);getContext().eventHub.emit('remotemsg', str)})tcpSocket.bind(bindAddress, err => {if(err){console.log('1-' + JSON.stringify(err))return}tcpSocket.connect({address: connectAddress,timeout: 30000}).then( r => {console.log('2-' +JSON.stringify(r))}).catch((e) => {console.log('3-' + JSON.stringify(e))})})}
复制

最终效果

实践:实现MQTT连接

准备MQTT Broker

MQTT使用的是 mosquitto,官网下载地址

关于MQTT的入门使用,可参见“MQTT试用”

注意:mosquitto 安装完成后,需要打开匿名设置,并且监听自己电脑的IP和1883端口 mosquitto的配置文件名:mosquitto.conf

  • allow_anonymous true
  • listener 1883 192.168.xxx.xxx

MQTT 5.0 网址

https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.pdf

  1. 熟悉 “3.1 CONNECT – Connection Request”
  2. 熟悉 “3.2 CONNACK – Connect acknowledgement”

场景模拟

客户端使用Socket 连接mosquitto, 并且响应连接ACK

代码实现

请求连接

这部分需要分为两步:1. Socket连接 2. MQTT连接请求(即发送连接请求命令)

a. Socket连接
function connectMQTTServer(){let bindAddress = {address: '192.168.71.66',port: 1983,family: 1};let connectAddress = {address: '192.168.71.23', //MQTT服务器IPport: 1883 //MQTT服务器端口};tcpSocket = socket.constructTCPSocketInstance()listenerStatus()tcpSocket.bind(bindAddress, err => {if(err){console.log('1-' + JSON.stringify(err))return}tcpSocket.connect({address: connectAddress,timeout: 30000}).then( r => {console.log('2-' +JSON.stringify(r))}).catch((e) => {console.log('3-' + JSON.stringify(e))})})
}
复制
b. MQTT连接请求
tcpSocket.on('connect', () => {console.log("on connect")//拼接MQTT请求数据let connectCMD = MQTTConnect.getFixHeader()//发送MQTT连接请求命令tcpSocket.send({data: connectCMD.buffer}, (error)=>{if(error) {console.log('消息没有发送成功: ' + JSON.stringify(error))} else {console.log('消息发送成功')}})getContext().eventHub.emit('connectStatus', 'connect')
});
复制

构建MQTT请求连接数据

代码有点枯燥,都是要对着MQTT 5.0规范文档来实现的

import util from '@ohos.util'export default class MQTTConnect{//3.1.1 CONNECT Fixed Header 【2字节】//3.1.2 CONNECT Variable Header//3.1.2.1 Protocol Name 【6字节】//3.1.2.2 Protocol Version 【1字节】//3.1.2.3 Connect Flags 【1字节】//3.1.2.10 Keep Alive 【2字节】//3.1.2.11 CONNECT Properties//3.1.2.11.1 Property Length 【1字节】//3.1.2.11.2 Session Expiry Interval 【4字节】//3.1.2.11.3 Receive Maximum 【2字节】//3.1.2.11.4 Maximum Packet Size 【4字节】//3.1.2.11.5 Topic Alias Maximum 【2字节】//3.1.2.11.6 Request Response Information 【1字节】//3.1.2.11.7 Request Problem Information 【1字节】//3.1.2.11.8 User Property【UTF-8 String Pair】//3.1.2.11.9 Authentication Method 【UTF-8 String】//3.1.2.11.10 Authentication Data【Binary Data】//3.1.3 CONNECT Payload//3.1.3.1 Client Identifier (ClientID) 【UTF-8 String】//3.1.3.2 Will Properties//3.1.3.2.1 Property Length 【Variable Byte Integer】//3.1.3.2.2 Will Delay Interval 【4字节】//3.1.3.2.3 Payload Format Indicator 【1字节】//3.1.3.2.4 Message Expiry Interval 【4字节】//3.1.3.2.5 Content Type【UTF-8 String】//3.1.3.2.6 Response Topic【UTF-8 String】//3.1.3.2.7 Correlation Data 【Binary Data】//3.1.3.2.8 User Property【UTF-8 String Pair】//3.1.3.3 Will Topic 【UTF-8 String】//3.1.3.4 Will Payload【 Binary Data】//3.1.3.5 User Name【UTF-8 String】//3.1.3.6 Password【 Binary Data】//3.1.4 CONNECT Actionspublic static getFixHeader(): Uint8Array{let remainLength: number = 0//3.1.1 CONNECT Fixed Header - 包类型let abPacketType = new ArrayBuffer(1)const dv_abPacketType = new DataView(abPacketType);dv_abPacketType.setInt8(0, 0x10)//3.1.2.1 Protocol Namelet u8a_protolName = this.utf8String('MQTT')remainLength += u8a_protolName.length//3.1.2.2 Protocol Versionlet version = new Uint8Array([5])remainLength++//3.1.2.3 Connect Flagsconst  UserNameFlag: number = 0x80const  PasswordFlag: number = 0x40const  WillRetain: number = 0x20const  WillQoS0: number = 0x00const  WillQoS1: number = 0x8const  WillQoS2: number = 0x10const  WillQoS3: number = 0x18const  WillFlag: number = 0x4const  CleanStart: number = 0x2let connectFlags: number = 0//可以根据实际对外暴露的接口,在这里进行与运算connectFlags = CleanStartlet u8a_connectFlags = new Uint8Array([connectFlags])remainLength++//3.1.2.10 Keep Aliveconst keepAlive = 60let u8a_keepalive = new Uint8Array([(keepAlive & 0xff00) >> 8 , keepAlive & 0xff])remainLength += 2//3.1.2.11 CONNECT Properties//3.1.2.11.1 Property Lengthlet u8a_propertylength = new Uint8Array([0])remainLength++//3.1.3 CONNECT Payload//3.1.3.1 Client Identifier (ClientID)let u8a_clientidentifier = this.utf8String('Harvey鸿蒙')remainLength += u8a_clientidentifier.length//3.1.1 CONNECT Fixed Header - 包剩余长度let abRemainLength = new ArrayBuffer(1)const dv_remainLength = new DataView(abRemainLength);dv_remainLength.setInt8(0, remainLength)let allIndex: number = 0let allUint8Array = new Uint8Array(2 + remainLength)allUint8Array[allIndex++] = dv_abPacketType.getUint8(0) //包类型allUint8Array[allIndex++] = dv_remainLength.getUint8(0) //包剩余长度u8a_protolName.forEach((value)=>{                       //协议名称allUint8Array[allIndex++] = value})version.forEach((value)=>{                              //协议版本号allUint8Array[allIndex++] = value})u8a_connectFlags.forEach((value)=>{                     //连接标志allUint8Array[allIndex++] = value})u8a_keepalive.forEach((value)=>{                        //长连保活时间allUint8Array[allIndex++] = value})u8a_propertylength.forEach((value)=>{                   //连接属性长度allUint8Array[allIndex++] = value})u8a_clientidentifier.forEach((value)=>{                 //客户端名称allUint8Array[allIndex++] = value})//数值打印let str = [...new Uint8Array(abPacketType),...new Uint8Array(abRemainLength), ...u8a_protolName, ...version, ...u8a_connectFlags, ...u8a_keepalive, ...u8a_propertylength, ...u8a_clientidentifier].map(x => x.toString(16).padStart(2, '0')).join(' ')console.log(str)let allStr: string = ''allUint8Array.forEach((value) => {allStr = allStr.concat(value.toString(16).padStart(2, '0')).concat(' ')})console.log(allStr)return allUint8Array}private static utf8String(content: string): Uint8Array{const encoder = new util.TextEncoder()let u8a_encoder = encoder.encodeInto(content)let encoderLength = u8a_encoder.lengthlet abEncoder = new ArrayBuffer(encoderLength + 2)const dv_encoder = new DataView(abEncoder)dv_encoder.setInt8(0, (encoderLength & 0xff00) >> 8)dv_encoder.setInt8(1, encoderLength & 0x00ff)let index: number = 2u8a_encoder.forEach( (value) => {dv_encoder.setInt8(index++, value)})return new Uint8Array(abEncoder)}}
复制

解析MQTT请求连接ACK数据

DevEco IDE 日志控制台

tcpSocket.on('message' , ( value: {message: ArrayBuffer, remoteInfo: socket.SocketRemoteInfo} ) => {let str = [...new Uint8Array(value.message)].map(x => x.toString(16).padStart(2, '0')).join(' ')console.log(str)let index: number = 0let uint8Array = new Uint8Array(value.message)let cfh = uint8Array[index]index++//3.2.1 CONNACK Fixed Header//解析MQTT ACK数据,转换为日志输出if(cfh == 32){console.log('Fixed Header format:CONNACK('+cfh+')')MQTTConnectACK.parse(index, uint8Array)}getContext().eventHub.emit('remotemsg', str)
})
复制

import MQTTTool from './MQTTTool'export default class MQTTConnectACK{public static parse(index: number, uint8Array: Uint8Array) {let remainLength = uint8Array[index]console.log('Remaining Length:' + remainLength.toString(16))index++if(remainLength == 0){return}let remainIndex: number = 0//3.2.2 CONNACK Variable Header//3.2.2.1 Connect Acknowledge Flagslet caf = uint8Array[index]console.log('Connect Acknowledge Flags:' + caf.toString(16))index++remainIndex++if(remainIndex >= remainLength){return}//3.2.2.2 Connect Reason Codelet crc = uint8Array[index]let des: string = ''if(crc == 0){des = 'Success'} else if(crc == 128){des = 'Unspecified error'} else if(crc == 129){des = 'Malformed Packet'} else if(crc == 130){des = 'Protocol Error'} else if(crc == 131){des = 'Implementation specific error'} else if(crc == 132){des = 'Unsupported Protocol Version'} else if(crc == 133){des = 'Client Identifier not valid'} else if(crc == 134){des = 'Bad User Name or Password'} else if(crc == 135){des = 'Not authorized'} else if(crc == 136){des = 'Server unavailable'} else if(crc == 137){des = 'Server busy'} else if(crc == 138){des = 'Banned'} else if(crc == 140){des = 'Bad authentication method'} else if(crc == 144){des = 'Topic Name invalid'} else if(crc == 149){des = 'Packet too large'} else if(crc == 151){des = 'Quota exceeded'} else if(crc == 153){des = 'Payload format invalid'} else if(crc == 154){des = 'Retain not supported'} else if(crc == 155){des = 'QoS not supported'} else if(crc == 156){des = 'Use another server'} else if(crc == 157){des = 'Server moved'} else if(crc == 158){des = 'Connection rate exceeded'}console.log('Connect Reason Code:' + des)index++remainIndex++if(remainIndex >= remainLength){return}//3.2.2.3 CONNACK Properties//3.2.2.3.1 Property Lengthlet propertyLength = uint8Array[index]console.log('Property Length:' + propertyLength.toString(16))index++remainIndex++if(propertyLength != 0){while (true){//判断类型let nextType = uint8Array[index]index++remainIndex++if(remainIndex >= remainLength){return}if(nextType == 17){ //值为4个字节//3.2.2.3.2 Session Expiry Intervallet costByteNumber = MQTTTool.parseFourByte(uint8Array, index, "Session Expiry Interval:")index += costByteNumberremainIndex += costByteNumberif(remainIndex >= remainLength){return}} else if(nextType == 33){//值为2个字节//3.2.2.3.3 Receive Maximumlet costByteNumber = MQTTTool.parseTwoByte(uint8Array, index, "Receive Maximum:")index += costByteNumberremainIndex += costByteNumberif(remainIndex >= remainLength){return}} else if(nextType == 36){ //值为1个字节//3.2.2.3.4 Maximum QoSlet mq = uint8Array[index]console.log('Maximum QoS:' + mq.toString(16))index++remainIndex++if(remainIndex >= remainLength){return}} else if(nextType == 37) {  //值为1个字节//3.2.2.3.5 Retain Availablelet ra = uint8Array[index]console.log('Retain Available:' + ra.toString(16))index++remainIndex++if(remainIndex >= remainLength){return}} else if(nextType == 39) { //值为4个字节//3.2.2.3.6 Maximum Packet Sizelet costByteNumber = MQTTTool.parseFourByte(uint8Array, index, "Maximum Packet Size:")index += costByteNumberremainIndex += costByteNumberif(remainIndex >= remainLength){return}} else if(nextType == 18) { //UTF-8 String = 2个字节 + 2个字节值的字节//3.2.2.3.7 Assigned Client Identifierlet costByteNumber = MQTTTool.parseUTF8String(uint8Array, index, "Assigned Client Identifier:")index += costByteNumberremainIndex += costByteNumberif(remainIndex >= remainLength){return}} else if(nextType == 34) { // 值为2个字节//3.2.2.3.8 Topic Alias Maximumlet costByteNumber = MQTTTool.parseTwoByte(uint8Array, index, "Topic Alias Maximum:")index += costByteNumberremainIndex += costByteNumberif(remainIndex >= remainLength){return}} else if(nextType == 31) { //UTF-8 String = 2个字节 + 2个字节值的字节//3.2.2.3.9 Reason Stringlet costByteNumber = MQTTTool.parseUTF8String(uint8Array, index, "Reason String:")index += costByteNumberremainIndex += costByteNumberif(remainIndex >= remainLength){return}} else if(nextType == 38) {//UTF-8 String Pair = (2个字节 + 2个字节值的字节)+(2个字节 + 2个字节值的字节)//3.2.2.3.10 User Propertylet costByteNumber = MQTTTool.parseUTF8String(uint8Array, index, "User Property:")index += costByteNumberremainIndex += costByteNumberif(remainIndex >= remainLength){return}} else if(nextType == 40) { //值为1个字节//3.2.2.3.11 Wildcard Subscription Availablelet wsa = uint8Array[index]console.log('Wildcard Subscription Available:' + wsa.toString(16))index++remainIndex++if(remainIndex >= remainLength){return}} else if(nextType == 41) { //值为1个字节//3.2.2.3.12 Subscription Identifiers Availablelet sia = uint8Array[index]console.log('Subscription Identifiers Available:' + sia.toString(16))index++remainIndex++if(remainIndex >= remainLength){return}} else if(nextType == 42) { //值为1个字节//3.2.2.3.13 Shared Subscription Availablelet ssa = uint8Array[index]console.log('Shared Subscription Available:' + ssa.toString(16))index++remainIndex++if(remainIndex >= remainLength){return}} else if(nextType == 19) { //值为2个字节//3.2.2.3.14 Server Keep Alivelet costByteNumber = MQTTTool.parseTwoByte(uint8Array, index, "Server Keep Alive:")index += costByteNumberremainIndex += costByteNumberif(remainIndex >= remainLength){return}} else if(nextType == 26) { //UTF-8 String = 2个字节 + 2个字节值的字节//3.2.2.3.15 Response Informationlet costByteNumber = MQTTTool.parseUTF8String(uint8Array, index, "Response Information:")index += costByteNumberremainIndex += costByteNumberif(remainIndex >= remainLength){return}} else if(nextType == 28) { //UTF-8 String = 2个字节 + 2个字节值的字节//3.2.2.3.16 Server Referencelet costByteNumber = MQTTTool.parseUTF8String(uint8Array, index, "Server Reference:")index += costByteNumberremainIndex += costByteNumberif(remainIndex >= remainLength){return}} else if(nextType == 21) { //UTF-8 String = 2个字节 + 2个字节值的字节//3.2.2.3.17 Authentication Methodlet costByteNumber = MQTTTool.parseUTF8String(uint8Array, index, "Authentication Method:")index += costByteNumberremainIndex += costByteNumberif(remainIndex >= remainLength){return}} else if(nextType == 22) { //Binary Data = 2个字节 + 2个字节值的字节//3.2.2.3.18 Authentication Datalet costByteNumber = MQTTTool.parseBinaryData(uint8Array, index, "Authentication Data:")index += costByteNumberremainIndex += costByteNumberif(remainIndex >= remainLength){return}}}}}}
复制

最终效果

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

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

相关文章

Pytorch入门实战 P4-猴痘图片,精确度提升

目录 一、前言: 二、前期准备: 1、设备查看 2、导入收集到的数据集 3、数据预处理 4、划分数据集(8:2) 5、加载数据集 三、搭建神经网络 四、训练模型 1、设置超参数 2、编写训练函数 3、编写测试函数 4、正式训练 …

C语言例4-30:将一个正整数的各位数字逆序输出

算法分析&#xff1a; 提取某一个正整数的最末一位数字&#xff0c;采用取模10的余数获得&#xff0c;以此类推即可。 代码如下&#xff1a; //将一个正整数的各位数字逆序输出 #include<stdio.h> int main(void) {int i,r;printf("输入一个正整数&#xff1a; \…

代码随想录算法训练营第三十八天 | 509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

代码随想录算法训练营第三十八天 | 509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯 509. 斐波那契数题目解法 70. 爬楼梯题目解法 746. 使用最小花费爬楼梯题目解法 感悟 509. 斐波那契数 题目 解法 使用动态规划 class Solution { public:int fib(int n) {if(n <…

如何准备科学海报

科学会议上的海报展示可以为早期职业研究人员提供宝贵的机会来练习他们的沟通技巧&#xff0c;获得有关他们研究的反馈&#xff0c;并扩大他们的网络。“通过与其他研究人员一对一地讨论我的工作&#xff0c;[我发现]我可以确定哪些工作做得好&#xff0c;哪些需要改进&#xf…

GEE22:基于目视解译的土地利用分类(随机森林监督分类)

采样点信息&#xff1a; 设置一下采样点参数&#xff1a; 代码&#xff1a; //设置研究区位置 var table ee.FeatureCollection("users/cduthes1991/boundry/China_province_2019"); var roi table.filter(ee.Filter.eq(provinces,beijing)); Map.centerObjec…

函数重载和引用

目录 一&#xff1a;函数重载 1.1函数重载的概念 1.2为什么C支持函数重载&#xff0c;而C语言不支持呢&#xff1f; 1.2.1结论 1.2.2分析 二&#xff1a;引用 2.1引用概念 2.2引用特性 ​编辑 2.3常引用 2.4使用场景 一&#xff1a;函数重载 在自然语言中&#x…

浏览器工作原理与实践--块级作用域:var缺陷以及为什么要引入let和const

在前面《07 | 变量提升&#xff1a;JavaScript代码是按顺序执行的吗&#xff1f;》这篇文章中&#xff0c;我们已经讲解了JavaScript中变量提升的相关内容&#xff0c;正是由于JavaScript存在变量提升这种特性&#xff0c;从而导致了很多与直觉不符的代码&#xff0c;这也是Jav…

数字化坚鹏:小熊电器面向数字化转型的大数据顶层设计实践培训

小熊电器面向数字化转型的大数据顶层设计实践培训圆满结束 ——努力打造“数据技术营销”三轮驱动的数字化领先企业 小熊电器股份有限公司由李一峰创立于2006年&#xff0c;是一家专业从事创意小家电研发、设计、生产和销售的实业型企业。2019年8月23日正式在深交所挂牌上市。…

Intel Arc显卡安装Stable Diffusion

StableDiffusion是一种基于深度学习的文本到图像生成模型&#xff0c;于2022年发布。它主要用于根据文本描述生成详细图像&#xff0c;也可应用于其他任务&#xff0c;如内补绘制、外补绘制和在提示词指导下生成图像翻译。通过给定文本提示词&#xff0c;该模型会输出一张匹配提…

gitee多用户配置

一、引言 在工作的时候我们有时候会自己创建项目Demo来实现一些功能&#xff0c;但是又不想把自己的Demo代码放到公司的仓库代码平台&#xff08;gitee&#xff09;中管理&#xff0c;于是就是想自己放到自己的Gitee中管理&#xff0c;于是就需要配置Git多用户。 本文将配置分别…

【Go】五、流程控制

文章目录 1、if2、switch3、for4、for range5、break6、continue7、goto8、return 1、if 条件表达式左右的()是建议省略的if后面一定要有空格&#xff0c;和条件表达式分隔开来{ }一定不能省略if后面可以并列的加入变量的定义 if count : 20;count < 30 {fmt.Println(&quo…

基于springboot实现课程作业管理系统项目【项目源码+论文说明】

基于springboot实现课程作业管理系统演示 摘要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;课程作业管理系统当然也不能排除在外。课程作业管理系统是以实际运用为开发背景…

Transformers —— 以通俗易懂的方式解释-Part 1

公众号:Halo咯咯,欢迎关注~ 本系列主要介绍了为ChatGPT以及许多其他大型语言模型(LLM)提供支持的Transformer神经网络。我们将从基础的Transformer概念开始介绍,尽量避免使用数学和技术细节,使得更多人能够理解这一强大的技术。 Transformers —— 以通俗易懂的方式解释…

数据结构——lesson11排序之快速排序

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

缓存雪崩问题及解决思路

实战篇Redis 2.7 缓存雪崩问题及解决思路 缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机&#xff0c;导致大量请求到达数据库&#xff0c;带来巨大压力。 解决方案&#xff1a; 给不同的Key的TTL添加随机值利用Redis集群提高服务的可用性给缓存业务添加降…

如何制作透明文件夹?

哇&#xff01;是不是很羡慕&#xff1f; 保姆级教程来啦&#xff01; 我们先新建一个文件夹 这么辛苦写文章&#xff0c;可以给我点个关注么~

好用的AI智能便签是哪款?桌面便签哪款比较智能

随着科技的日新月异&#xff0c;我们的生活与工作中涌现出众多便捷的软件工具&#xff0c;它们不仅提升了我们的生活质量&#xff0c;更在工作效率上给予了极大的助力。其中&#xff0c;便签软件以其简单实用的特性&#xff0c;成为了许多人日常不可或缺的好帮手。而在众多便签…

量化交易入门(二十八)什么是布林带,量化中怎么使用

什么叫布林带 布林带&#xff08;Bollinger Bands&#xff09;是一种常用的技术分析指标&#xff0c;由约翰布林&#xff08;John Bollinger&#xff09;于20世纪80年代开发。它由三条线组成&#xff1a;中轨&#xff08;通常为20日移动平均线&#xff09;、上轨&#xff08;中…

【IC前端虚拟项目】write_path子模块DS与RTL编码

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 read_path的代码完成之后,就可以开始整个项目里复杂度最高、bug最多、时序收敛最为困难的模块——write_path的开发了!我自己写过两次这个虚拟项目,每次都是在这里耗时最久,所以大家也可以挑战一下自…

Java虚拟机(JVM)知识点总结

一. Java内存区域 1. JVM的内存区域划分&#xff0c;以及各部分的作用 可分为运行时数据区域和本地内存&#xff0c;按照线程私有和线程共享分类&#xff1a; 线程私有&#xff1a;程序计数器、虚拟机栈、本地方法栈。 线程共享&#xff1a;堆、方法区、直接内存。 JDK1.7…