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

news2025/1/24 22:38:18

本篇分享一下 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() {
                    @Override
                    public 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();
        }
    }

    @Override
    public 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
        }
    })

  }

  //页面销毁时,解除所有订阅,关闭socket
  aboutToDisappear() {
     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.inputMessage
              this.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服务器IP
    port: 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 Actions


   public 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 Name

      let u8a_protolName = this.utf8String('MQTT')
      remainLength += u8a_protolName.length

      //3.1.2.2 Protocol Version
      let version = new Uint8Array([5])
      remainLength++

      //3.1.2.3 Connect Flags
      const  UserNameFlag: number = 0x80
      const  PasswordFlag: number = 0x40
      const  WillRetain: number = 0x20
      const  WillQoS0: number = 0x00
      const  WillQoS1: number = 0x8
      const  WillQoS2: number = 0x10
      const  WillQoS3: number = 0x18
      const  WillFlag: number = 0x4
      const  CleanStart: number = 0x2

      let connectFlags: number = 0
      //可以根据实际对外暴露的接口,在这里进行与运算
      connectFlags = CleanStart
      let u8a_connectFlags = new Uint8Array([connectFlags])
      remainLength++

      //3.1.2.10 Keep Alive
      const keepAlive = 60

      let u8a_keepalive = new Uint8Array([(keepAlive & 0xff00) >> 8 , keepAlive & 0xff])
      remainLength += 2

      //3.1.2.11 CONNECT Properties
      //3.1.2.11.1 Property Length
      let 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 = 0
      let 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.length

      let 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 = 2
      u8a_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 = 0

  let 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 Flags
      let caf = uint8Array[index]
      console.log('Connect Acknowledge Flags:' + caf.toString(16))
      index++
      remainIndex++

      if(remainIndex >= remainLength){
        return
      }

      //3.2.2.2 Connect Reason Code
      let 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 Length
      let 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 Interval
            let costByteNumber = MQTTTool.parseFourByte(uint8Array, index, "Session Expiry Interval:")
            index += costByteNumber
            remainIndex += costByteNumber

            if(remainIndex >= remainLength){
              return
            }

          } else if(nextType == 33){//值为2个字节

            //3.2.2.3.3 Receive Maximum
            let costByteNumber = MQTTTool.parseTwoByte(uint8Array, index, "Receive Maximum:")
            index += costByteNumber
            remainIndex += costByteNumber

            if(remainIndex >= remainLength){
              return
            }

          } else if(nextType == 36){ //值为1个字节

            //3.2.2.3.4 Maximum QoS
            let 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 Available
            let 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 Size
            let costByteNumber = MQTTTool.parseFourByte(uint8Array, index, "Maximum Packet Size:")
            index += costByteNumber
            remainIndex += costByteNumber

            if(remainIndex >= remainLength){
              return
            }

          } else if(nextType == 18) { //UTF-8 String = 2个字节 + 2个字节值的字节

            //3.2.2.3.7 Assigned Client Identifier
            let costByteNumber = MQTTTool.parseUTF8String(uint8Array, index, "Assigned Client Identifier:")
            index += costByteNumber
            remainIndex += costByteNumber

            if(remainIndex >= remainLength){
              return
            }

          } else if(nextType == 34) { // 值为2个字节
            //3.2.2.3.8 Topic Alias Maximum
            let costByteNumber = MQTTTool.parseTwoByte(uint8Array, index, "Topic Alias Maximum:")
            index += costByteNumber
            remainIndex += costByteNumber

            if(remainIndex >= remainLength){
              return
            }

          } else if(nextType == 31) { //UTF-8 String = 2个字节 + 2个字节值的字节
            //3.2.2.3.9 Reason String
            let costByteNumber = MQTTTool.parseUTF8String(uint8Array, index, "Reason String:")
            index += costByteNumber
            remainIndex += costByteNumber

            if(remainIndex >= remainLength){
              return
            }

          } else if(nextType == 38) {//UTF-8 String Pair = (2个字节 + 2个字节值的字节)+(2个字节 + 2个字节值的字节)
            //3.2.2.3.10 User Property
            let costByteNumber = MQTTTool.parseUTF8String(uint8Array, index, "User Property:")
            index += costByteNumber
            remainIndex += costByteNumber

            if(remainIndex >= remainLength){
              return
            }

          } else if(nextType == 40) { //值为1个字节
            //3.2.2.3.11 Wildcard Subscription Available
            let 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 Available
            let 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 Available
            let 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 Alive
            let costByteNumber = MQTTTool.parseTwoByte(uint8Array, index, "Server Keep Alive:")
            index += costByteNumber
            remainIndex += costByteNumber

            if(remainIndex >= remainLength){
              return
            }

          } else if(nextType == 26) { //UTF-8 String = 2个字节 + 2个字节值的字节
            //3.2.2.3.15 Response Information
            let costByteNumber = MQTTTool.parseUTF8String(uint8Array, index, "Response Information:")
            index += costByteNumber
            remainIndex += costByteNumber

            if(remainIndex >= remainLength){
              return
            }

          } else if(nextType == 28) { //UTF-8 String = 2个字节 + 2个字节值的字节
            //3.2.2.3.16 Server Reference
            let costByteNumber = MQTTTool.parseUTF8String(uint8Array, index, "Server Reference:")
            index += costByteNumber
            remainIndex += costByteNumber

            if(remainIndex >= remainLength){
              return
            }

          } else if(nextType == 21) { //UTF-8 String = 2个字节 + 2个字节值的字节
            //3.2.2.3.17 Authentication Method
            let costByteNumber = MQTTTool.parseUTF8String(uint8Array, index, "Authentication Method:")
            index += costByteNumber
            remainIndex += costByteNumber

            if(remainIndex >= remainLength){
              return
            }

          } else if(nextType == 22) { //Binary Data = 2个字节 + 2个字节值的字节
            //3.2.2.3.18 Authentication Data
            let costByteNumber = MQTTTool.parseBinaryData(uint8Array, index, "Authentication Data:")
            index += costByteNumber
            remainIndex += costByteNumber

            if(remainIndex >= remainLength){
              return
            }

          }
        }

    }
  }

}
复制

最终效果

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

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

相关文章

3-zookeeper之ZAB协议

Zookeeper ZAB协议 概述 ZAB(Zookeeper Automic Broadcast)是一套专门为Zookeeper设计的用于进行原子广播和崩溃恢复的协议ZAB协议主要包含了两个功能 原子广播:保证数据一致性崩溃恢复:保证集群的高可用 ZAB协议本身是基于2PC算法来进行的设计&#…

『VUE』05. 条件渲染(详细图文注释)

目录 新建IfDemo.vuev-if 和 v-elsev-if | v-else-if | v-elsev-showv-show 与 v-if 的区别,性能分析总结 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 在前文的项目基础上继续~ 新建IfDemo.vue IfDemo.vue内容如下 <scri…

前端-深入探讨网络面试题

第一关 请求-文件、数据、连接 文件类的请求&#xff1a;加载HTMl、CSS 数据&#xff1a; ajax请求&#xff08;基于HTTP&#xff0c;HTTP基于TCP&#xff09;&#xff0c;如何建立连接的&#xff08;三次握手&#xff0c;为什么不是两次或者四次&#xff09;&#xff0c;sock…

用MATLAB编写一个简易的贪吃蛇游戏

编写一个贪吃蛇游戏的MATLAB代码需要涉及到游戏逻辑、图形用户界面(GUI)的创建、事件监听&#xff08;如键盘操作&#xff09;和游戏状态更新等。以下是一个简化的贪吃蛇游戏MATLAB代码示例&#xff0c;展示了基本的游戏框架和逻辑。 此代码创建了一个游戏窗口&#xf…

解决GNU Radio+USRP实现OFDM收发在接收端QPSK星座图映射无“抖动”问题

文章目录 前言一、遇到的问题二、解决方案三、重新编译安装四、验证五、资源自取 前言 本文记录在 GNU RadioUSRP 实现 OFDM 收发时&#xff0c;在接收端 QPSK 星座图映射无“抖动”问题的解决方法&#xff0c; 一、遇到的问题 我遇到的问题是&#xff0c;现在搭建的 OFDM 模…

Qt实现Kermit协议

1 概述 Kermit文件运输协议提供了一条从大型计算机下载文件到微机的途径。它已被用于进行公用数据传输。 其特性如下: Kermit文件运输协议是一个半双工的通信协议。它支持7位ASCII字符。数据以可多达96字节长度的可变长度的分组形式传输。对每个被传送分组需要一个确认。Kerm…

Spark SQL— Catalyst 优化器

Spark SQL— Catalyst 优化器 1. 目的 本文的目标是描述Spark SQL 优化框架以及它如何允许开发人员用很少的代码行表达复杂的查询转换。我们还将描述Spark SQL如何通过大幅提高其查询优化能力来提高查询的执行时间。在本教程中&#xff0c;我们还将介绍什么是优化、为什么使用…

先进电机技术 —— 何为轮毂电机?

一、轮毂电机 轮毂电机&#xff08;Hub Motor&#xff09;是一种将电动机集成到车轮内部&#xff0c;直接驱动车轮转动的电动车驱动技术。这种设计省去了传统的传动轴、差速器、半轴等机械传动部件&#xff0c;使得动力传输更为直接、高效。 轮毂电机的优点&#xff1a; 1. 结…

elementui el-input输入框类型为textarea时,将输入的数据保存换行和空格,并展示换行和空格

el-input输入框类型为textarea时&#xff0c;如果不做数据处理&#xff0c;是不会保存换行和空格的说输入了换行&#xff0c;但是保存数据后不会进行换行&#xff0c;需要保存输入的换行。 1、效果图 输入状态&#xff1a; 显示时&#xff1a; 2、实现代码 2.1、html部分&am…

【CKA模拟题】综合案例演示如何创建pv和pvc

Useful Resources: Persistent Volumes & Claim 题干 For this question, please set this context (In exam, diff cluster name) kubectl config use-context kubernetes-adminkubernetesCreate a PersistentVolume (PV) and a PersistentVolumeClaim (PVC) using an e…

排序---数组和集合

1、数组排序 Arrays.sort(int[] a)这种形式是对一个数组的所有元素进行排序&#xff0c;并且是按照从小到大的排序。 public static void main(String[] args) {Integer []arr {1,2,3,4,5,6};//升序Arrays.sort(arr);for (int x:arr){System.out.print(x " ");}Sys…

【Web APIs】正则表达式

目录 1.正则表达式 2.正则表达式语法 3.元字符 3.1边界符 3.2量词 3.3字符类 4.修饰符 1.正则表达式 正则表达式&#xff08;Regular Expression&#xff09;是用于匹配字符串中字符组合的模式&#xff0c;在 JavaScript中&#xff0c;正则表达式也是对象。通常用来查…

数据分析之POWER Piovt的KPI设置

内容总结&#xff1a; 1.两个表格关联不上&#xff1a;需要添加辅助列&#xff0c;建立关联 2.添加辅助列后还关联不上&#xff1a;将虚线变为实线 3.根据需求要增加一些度量值 4.设置KPI后&#xff0c;绝对值选1后设定百分比 5.在透视表里面加入KPI状态 导入所关联的数据后建立…

取证之内存取证工具Volatility学习

一、简介 Volatility是一款开源的内存取证分析工具&#xff0c;支持Windows&#xff0c;Linux&#xff0c;MaC&#xff0c;Android等多类型操作系统系统的内存取证方式。该工具是由python开发的&#xff0c;目前支持python2、python3环境。 二、安装 1、下载地址 GitHub - …

Matlab之求直角坐标系下两直线的交点坐标

目的&#xff1a;在直角坐标系下&#xff0c;求两个直线的交点坐标 一、函数的参数说明 输入参数&#xff1a; PointA&#xff1a;直线A上的点坐标&#xff1b; AngleA&#xff1a;直线A的倾斜角&#xff0c;单位度&#xff1b; PointB&#xff1a;直线B上的点坐标&#xf…

【日常记录】【JS】@babel/core @babel/polyfill @babel/preset-env core-js 介绍

文章目录 1、介绍1.1 babel/core1.2 babel/polyfill1.3 babel/preset-env 2、基本使用3、参考链接 1、介绍 1.1 babel/core babel/core 是 Babel 编译器的核心模块&#xff0c;它是 Babel 工具链的核心组件之一。Babel 是一个 JavaScript 编译器&#xff0c;主要用于将 ECMASc…

数据结构——链表(单链表)

大家好&#xff0c;又是我&#xff08;小锋&#xff09;&#xff0c;今天给大家带了一个比较有挑战的章节&#xff08;链表&#xff09;&#xff0c;但是不用担心&#xff0c;小锋会陪大家一起度过。 顺序表的思考与问题 1. 中间/头部的插入删除&#xff0c;时间复杂度为O(N) …

rhcsa复习4

文件权限 文件的权限针对三类对象进行定义 owner 属主&#xff0c;缩写 u group 属组&#xff0c;缩写 g other 其他&#xff0c;缩写 o 每个文件针对每类访问者定义了三种主要权限 r &#xff1a; Read 读 - 文本文件 cat tac more less head tail paste d ls -l 列…

数字范围按位与

题目链接 数字范围按位与 题目描述 注意点 0 < left < right < 2^31 - 1包含 left 、right 端点 解答思路 返回区间内所有数字按位与的结果&#xff0c;所以区间内所有数字在某一位的值相同&#xff0c;则结果该位数字为该值&#xff0c;否则该位数字为0&#xf…

【漏洞复现】XETUX 系统 dynamiccontent 接口处存在远程代码执行漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…