Detailed use of front-end mqtt (including mqtt server deployment, front-end vue3 using mqtt connection, topic subscription, publishing, etc.)

1. Brief description

​ MQTT (Message Queue Telemetry Transport Protocol) is a communication protocol based on the publish/subscribe (publish/subscribe) model, which is built on the TCP/IP protocol. The biggest advantage of MQTT is that it can provide real-time and reliable messaging services for connecting remote devices with very little code and limited bandwidth. The application scenarios of the MQTT protocol include the Internet of Things, mobile applications, Internet of Vehicles, smart homes, instant chat, etc.

2. Characteristics

  • Use publish/subscribe messaging pattern.

  • Message transmission with payload content masked.

  • Provide network connectivity using TCP/IP.

  • There are three message publishing quality of services:

    1. "At most once", message publishing completely relies on the underlying TCP/IP network. Message loss or duplication can occur. This level can be used in situations where, for environmental sensor data, it doesn't matter if a read record is lost because a second one will be sent soon. This method is mainly used for ordinary APP push. If your smart device is not connected to the Internet when the message is pushed, and the push was not received in the past, it will not be received if it is connected to the Internet again.

    2. "At least once" ensures that the message arrives, but message duplication may occur.

    3. "Only once" ensures that the message arrives once. This level can be used in some billing systems with stricter requirements. In billing systems, duplicate or missing messages can lead to incorrect results. This highest-quality message publishing service can also be used for push notifications on instant messaging apps, ensuring that users receive it only once.

  • Small transfer with little overhead (fixed length header is 2 bytes) and protocol switching minimized to reduce network traffic.

  • A mechanism to notify relevant parties of abnormal client interruptions using the Last Will and Testament features.

    Last Will: The last words mechanism is used to notify other devices under the same topic that the device sending the last words has been disconnected.

    Testament: Will mechanism, similar in function to Last Will.

3. Subscriptions, topics, and sessions in the MQTT protocol

1. Subscription

Subscriptions include topic filters and maximum quality of service (QoS). Subscriptions are associated with a session. A session can contain multiple subscriptions. Each subscription in each session has a different topic filter.

2. Session

After each client establishes a connection with the server, it is a session, and there is stateful interaction between the client and the server. A session exists between a network and may span multiple consecutive network connections between a client and a server.

3. Topic Name

Connect to a label for an application message that matches the server's subscription. The server sends the message to every client subscribed to the matching tag.

4. Topic Filter

A wildcard filter for topic names, used in subscription expressions to indicate multiple topics matched by the subscription.

5. Payload

The specific content received by message subscribers.

4. Methods in MQTT protocol

  • Connect. Waiting for a connection to be established with the server.
  • Disconnect. Wait for the MQTT client to finish what it has done and disconnect the TCP/IP session from the server.
  • Subscribe. Wait for the subscription to be completed.
  • UnSubscribe. Wait for the server to cancel the client's subscription to one or more topics.
  • Publish. The MQTT client sends a message request and returns to the application thread after the sending is completed.

5. Use mqtt on the front end

1. Deployment of mqtt server

Use EMQX CLOUD to deploy mqtt services

EMQX Cloud: Fully managed MQTT messaging cloud service

Deployment tutorial: https://www.bilibili.com/video/BV1Bx4y1K7hN/

There is a free monthly quota for using the Serverless version

Insert image description here

2. Install js mqtt package

npm i mqtt

3. Front-end code

The complete code has been uploaded to github
https://github.com/void00013/8.mqtt_vue3_test/tree/main
. Please download the dependencies and run it yourself.

// Home.vue
<template>
  <div class="home-container">
    <el-card shadow="always" style="margin-bottom:30px;">
      <div class="emq-title">
        Configuration
      </div>
      <el-form ref="configForm" hide-required-asterisk size="small" label-position="top" :model="connection">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item prop="host" label="Host">
              <el-row :gutter="10">
                <el-col :span="7">
                  <el-select v-model="connection.protocol" @change="handleProtocolChange">
                    <el-option label="ws://" value="ws"></el-option>
                    <el-option label="wss://" value="wss"></el-option>
                  </el-select>
                </el-col>
                <el-col :span="17">
                  <el-input v-model="connection.host"></el-input>
                </el-col>
              </el-row>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item prop="port" label="Port">
              <el-input v-model.number="connection.port" type="number" placeholder="8083/8084"></el-input>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item prop="endpoint" label="Mountpoint">
              <el-input v-model="connection.endpoint" placeholder="/mqtt"></el-input>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item prop="clientId" label="Client ID">
              <el-input v-model="connection.clientId"> </el-input>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item prop="username" label="Username">
              <el-input v-model="connection.username"></el-input>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item prop="password" label="Password">
              <el-input v-model="connection.password"></el-input>
            </el-form-item>
          </el-col>

          <el-col :span="24">
            <el-button type="success" size="small" class="conn-btn" style="margin-right: 20px;" :disabled="client.connected" @click="createConnection" :loading="connecting">
              {
   
   { client.connected ? 'Connected' : 'Connect' }}
            </el-button>

            <el-button v-if="client.connected" type="danger" size="small" class="conn-btn" @click="destroyConnection">
              Disconnect
            </el-button>
          </el-col>
        </el-row>
      </el-form>
    </el-card>
    <el-card shadow="always" style="margin-bottom:30px;">
      <div class="emq-title">
        Subscribe
      </div>
      <el-form ref="subscription" hide-required-asterisk size="small" label-position="top" :model="subscription">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item prop="topic" label="Topic">
              <el-input v-model="subscription.topic"></el-input>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item prop="qos" label="QoS">
              <el-select v-model="subscription.qos">
                <el-option v-for="qos in qosList" :key="qos" :label="qos" :value="qos"></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-button :disabled="!client.connected" type="success" size="small" class="subscribe-btn" @click="doSubscribe">
              {
   
   { subscribeSuccess ? 'Subscribed' : 'Subscribe' }}
            </el-button>
            <el-button :disabled="!client.connected" type="success" size="small" class="subscribe-btn" style="margin-left:20px" @click="doUnSubscribe" v-if="subscribeSuccess">
              Unsubscribe
            </el-button>
          </el-col>
        </el-row>
      </el-form>
    </el-card>
    <el-card shadow="always" style="margin-bottom:30px;">
      <div class="emq-title">
        Publish
      </div>
      <el-form ref="publish" hide-required-asterisk size="small" label-position="top" :model="publish">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item prop="topic" label="Topic">
              <el-input v-model="publish.topic"></el-input>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item prop="payload" label="Payload">
              <el-input v-model="publish.payload"></el-input>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item prop="qos" label="QoS">
              <el-select v-model="publish.qos">
                <el-option v-for="qos in qosList" :key="qos" :label="qos" :value="qos"></el-option>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <el-col :span="24">
        <el-button :disabled="!client.connected" type="success" size="small" class="publish-btn" @click="doPublish">
          Publish
        </el-button>
      </el-col>
    </el-card>
    <el-card shadow="always" style="margin-bottom:30px;">
      <div class="emq-title">
        Receive
      </div>
      <el-col :span="24">
        <el-input type="textarea" :rows="3" style="margin-bottom: 15px" v-model="receiveNews" readOnly></el-input>
      </el-col>
    </el-card>
  </div>
</template>

<script>
import mqtt from 'mqtt'

export default {
  name: 'Home',

  data() {
    return {
      connection: {
        protocol: 'wss',
        host: '你的mqtt服务器地址',
        // server less服务器只有两种协议:mqtts: 8883; wss: 8084
        port: 8084,
        endpoint: '/mqtt',
        clean: true,
        connectTimeout: 30 * 1000, // ms
        reconnectPeriod: 4000, // ms
        clientId: 'emqx_vue_' + Math.random().toString(16).substring(2, 8),
        // auth
        username: 'void',
        password: '123',
      },
      subscription: {
        topic: 'topic/mqttx',
        qos: 0,
      },
      publish: {
        topic: 'topic/browser',
        qos: 0,
        payload: '{ "msg": "Hello, I am browser." }',
      },
      receiveNews: '',
      qosList: [0, 1, 2],
      client: {
        connected: false,
      },
      subscribeSuccess: false,
      connecting: false,
      retryTimes: 0,
    }
  },
  methods: {
    initData() {
      this.client = {
        connected: false,
      }
      this.retryTimes = 0
      this.connecting = false
      this.subscribeSuccess = false
    },
    handleOnReConnect() {
      this.retryTimes += 1
      if (this.retryTimes > 5) {
        try {
          this.client.end()
          this.initData()
          this.$message.error('Connection maxReconnectTimes limit, stop retry')
        } catch (error) {
          this.$message.error(error.toString())
        }
      }
    },
    createConnection() {
      try {
        this.connecting = true
        const { protocol, host, port, endpoint, ...options } = this.connection
        const connectUrl = `${protocol}://${host}:${port}${endpoint}`
        this.client = mqtt.connect(connectUrl, options)
        if (this.client.on) {
          this.client.on('connect', () => {
            this.connecting = false
            console.log('Connection succeeded!')
          })
          this.client.on('reconnect', this.handleOnReConnect)
          this.client.on('error', (error) => {
            console.log('Connection failed', error)
          })
          this.client.on('message', (topic, message) => {
            this.receiveNews = this.receiveNews.concat(message)
            console.log(`Received message ${message} from topic ${topic}`)
          })
        }
      } catch (error) {
        this.connecting = false
        console.log('mqtt.connect error', error)
      }
    },
    // subscribe topic
    doSubscribe() {
      const { topic, qos } = this.subscription
      this.client.subscribe(topic, { qos }, (error, res) => {
        if (error) {
          console.log('Subscribe to topics error', error)
          return
        }
        this.subscribeSuccess = true
        console.log('Subscribe to topics res', res)
      })
    },
    // unsubscribe topic
    doUnSubscribe() {
      const { topic } = this.subscription
      this.client.unsubscribe(topic, (error) => {
        if (error) {
          console.log('Unsubscribe error', error)
        }
      })
    },
    // publish message
    doPublish() {
      const { topic, qos, payload } = this.publish
      this.client.publish(topic, payload, { qos }, (error) => {
        if (error) {
          console.log('Publish error', error)
        }
      })
    },
    // disconnect
    destroyConnection() {
      if (this.client.connected) {
        try {
          this.client.end(false, () => {
            this.initData()
            console.log('Successfully disconnected!')
          })
        } catch (error) {
          console.log('Disconnect failed', error.toString())
        }
      }
    },
    handleProtocolChange(value) {
      this.connection.port = value === 'wss' ? '8084' : '8083'
    },
  },
}
</script>

<style lang="scss">
@import url('../assets/style/home.scss');

.home-container {
  max-width: 1100px;
  margin: 0 auto;

  .conn-btn {
    color: #fff;
    background-color: #00b173;
    font-size: 14px;
  }

  .publish-btn {
    margin-bottom: 20px;
    float: right;
  }

  .el-button--success {
    background-color: #34c388 !important;
    border-color: #34c388 !important;
    font-size: 14px !important;
  }

  .el-button--danger {
    background-color: #f5222d !important;
    border-color: #f5222d !important;
  }

  .el-form-item {
    &.is-error {
      .el-input__inner,
      .el-textarea__inner {
        box-shadow: 0 0 0 2px rgba(245, 34, 45, 0.2);
      }
    }
    &.is-success {
      .el-input__inner,
      .el-textarea__inner {
        border-color: #34c388 !important;
      }
    }
  }
}
</style>
// home.scss
body {
  background-color: #f0f2f5;
  padding: 0;
  margin: 0;
  color: rgba(0, 0, 0, 0.65);
  font-size: 14px;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Hiragino Sans GB, Microsoft YaHei,
    Helvetica Neue, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
}

.emq-title {
  font-size: 16px;
  color: #333333;
  font-weight: bolder;
  margin-bottom: 15px;

  &.h3 {
    font-size: 14px;
  }

  &[size='small'] {
    font-size: 14px;
  }

  .sub-title {
    font-weight: normal;
    display: block;
    font-size: 12px;
    color: #8f9297;
    margin-top: 12px;
  }

  &.required-title {
    &:before {
      content: '*';
      color: #f5222d;
      margin-right: 4px;
    }
  }
}

.el-select {
  width: 100%;
}

.subscribe-btn {
  margin-top: 42px !important;
}

.publish-btn {
  margin-bottom: 30px;
}

4. EMQX official example

The official provides code examples in various languages ​​such as vue, react, java, python, etc. If necessary, please check
https://github.com/emqx/MQTT-Client-Examples/tree/master

Guess you like

Origin blog.csdn.net/weixin_47365243/article/details/132119874