SpringBoot 集成Mqtt龄减,protobuf服務(wù)端搭建

代碼已開源在GitHub,如果有幫助歡迎star班眯。

Architecture

Mqtt-server-arch.jpg

Server side 構(gòu)成

  • broker (mqtt核心:用于消息的發(fā)送管理) 類似 pub-sub 隊(duì)列
  • Application Server用于處理RestFul的請(qǐng)求希停,轉(zhuǎn)發(fā)為Mqtt消息
    • Publisher 本質(zhì)是Mqtt client,用于發(fā)布server端消息到broker
    • Subscriber 本質(zhì)是Mqtt client署隘,用于從broker訂閱client端消息
  • Client side
    • Publisher用于發(fā)布client端消息到broker
    • Subscriber用于從broker訂閱server端的消息
    • Client 用于發(fā)送RestFul 請(qǐng)求給Application Server觸發(fā)消息pub/sub

總結(jié):從結(jié)構(gòu)上Broker算是Mqtt的本質(zhì)上的Server端宠能,從業(yè)務(wù)上講封裝了Mqtt Client pub/sub的Application server和Broker共同構(gòu)成了業(yè)務(wù)上的Server端

安裝mosquitto及基本使用

安裝

# Install Mosquitto Broker
sudo apt-get update
sudo apt-get install mosquitto

# Install the Clients 
sudo apt-get install mosquitto-clients

開啟、停止查看狀態(tài)

# 查看狀態(tài)
sudo service mosquitto status

# 使用默認(rèn)配置打開mosquitto, 使用-v打開log功能
sudo mosquitto -c /etc/mosquitto/mosquitto.conf -v

# 停止
sudo service mosquitto stop

#開啟
sudo service mosquitto start

使用mosquitto測(cè)試pub/sub

注意 pub和sub的clientid不能相同定踱,相同會(huì)刷屏棍潘。

# 簡單測(cè)試發(fā)布。 -h host -t topic -m message
mosquitto_pub -h localhost -t mqtt-test -m 'hello mqtt'

# 簡單測(cè)試訂閱崖媚。
mosquitto_sub -h localhost -t mqtt-test

# 發(fā)布設(shè)置用戶密碼 -u user -P password 
mosquitto_pub -u admin -P admin -h localhost -t mqtt/loop/message -m 'test mqtt'
mosquitto_sub -u admin -P admin -h localhost -t mqtt/loop/message

# 指定發(fā)布clientid -i (id to use for this client)
mosquitto_sub -u admin -P admin -i shuai-ubuntu-test -h localhost -t mqtt/loop/message
mosquitto_pub -u admin -P admin -i shuai-ubuntu-test-client -h localhost -t mqtt/loop/message -m 'test mqtt client'

查看broker的log

mosquitto的默認(rèn)log 地址是:/var/log/mosquitto/xxx.log

tailf /var/log/mosquitto/mosquitto.log

構(gòu)建Java-Mqtt-Server(Springboot + Mqtt)

requirement依賴

  1. mosquitto broker

    可以使用Eclipse公開的broker,據(jù)說底層也是mosquitto恤浪。地址為iot.eclipse.org

    可以部署安裝mosquitto(本文方案)

  2. springboot (2.1.5.RELEASE)

  3. Eclipse Paho

  4. curl/postman

構(gòu)建springboot項(xiàng)目

1. 使用idea springboot initializer 初始化springboot工程

使用springboot版本2.1.5.RELEASE

2. pom中添加

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-stream</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-mqtt</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.10</version>
    <scope>provided</scope>
</dependency>

3. MQTT Configuration

  • 配置broker地址畅哑,
  • 端口號(hào),
  • 是否使用ssl水由,
  • 用戶名
  • 密碼
public abstract class MQTTConfig {

    protected final String broker = "10.156.2.132";
    protected final int qos = 2;
    protected Boolean hasSSL = false; /* By default SSL is disabled */
    protected Integer port = 1883; /* Default port */
    protected final String userName = "admin";
    protected final String password = "admin";
    protected final String TCP = "tcp://";
    protected final String SSL = "ssl://";

    /**
     * Custom Configuration
     *
     * @param broker
     * @param port
     * @param ssl
     * @param withUserNamePass
     */
    protected abstract void config(String broker, Integer port, Boolean ssl, Boolean withUserNamePass);

    /**
     * Default Configuration
     */
    protected abstract void config();
}

4. Publisher推送者

定義接口

public interface IMQTTPublisher {
    /**
     * Publish message
     *
     * @param topic
     * @param String Message
     */
    public void publishMessage(String topic, String message);

    /**
     * Disconnect MQTT Client
     */
    public void disconnect();
}

定義類


import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class MQTTPublisher extends MQTTConfig implements MqttCallback, IMQTTPublisher {

    private String brokerUrl = null;

    final private String colon = ":";
    final private String clientId = "mqtt_server_pub";

    private MqttClient mqttClient = null;
    private MqttConnectOptions connectionOptions = null;
    private MemoryPersistence persistence = null;

    private static final Logger logger = LoggerFactory.getLogger(MQTTPublisher.class);

    /**
     * Private default constructor
     */
    private MQTTPublisher() {
        this.config();
    }

    /**
     * Private constructor
     */
    private MQTTPublisher(String broker, Integer port, Boolean ssl, Boolean withUserNamePass) {
        this.config(broker, port, ssl, withUserNamePass);
    }

    /**
     * Factory method to get instance of MQTTPublisher
     *
     * @return MQTTPublisher
     */
    public static MQTTPublisher getInstance() {
        return new MQTTPublisher();
    }

    /**
     * Factory method to get instance of MQTTPublisher
     *
     * @param broker
     * @param port
     * @param ssl
     * @param withUserNamePass
     * @return MQTTPublisher
     */
    public static MQTTPublisher getInstance(String broker, Integer port, Boolean ssl, Boolean withUserNamePass) {
        return new MQTTPublisher(broker, port, ssl, withUserNamePass);
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * com.bjitgroup.jasmysp.mqtt.publisher.MQTTPublisherBase#configurePublisher()
     */
    @Override
    protected void config() {

        this.brokerUrl = this.TCP + this.broker + colon + this.port;
        this.persistence = new MemoryPersistence();
        this.connectionOptions = new MqttConnectOptions();
        try {
            this.mqttClient = new MqttClient(brokerUrl, clientId, persistence);
            this.connectionOptions.setCleanSession(true);
            this.mqttClient.connect(this.connectionOptions);
            this.mqttClient.setCallback(this);
        } catch (MqttException me) {
            logger.error("ERROR", me);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * com.bjitgroup.jasmysp.mqtt.publisher.MQTTPublisherBase#configurePublisher(
     * java.lang.String, java.lang.Integer, java.lang.Boolean, java.lang.Boolean)
     */
    @Override
    protected void config(String broker, Integer port, Boolean ssl, Boolean withUserNamePass) {

        String protocal = this.TCP;
        if (true == ssl) {
            protocal = this.SSL;
        }

        this.brokerUrl = protocal + this.broker + colon + port;
        this.persistence = new MemoryPersistence();
        this.connectionOptions = new MqttConnectOptions();

        try {
            this.mqttClient = new MqttClient(brokerUrl, clientId, persistence);
            this.connectionOptions.setCleanSession(true);
            if (true == withUserNamePass) {
                if (password != null) {
                    this.connectionOptions.setPassword(this.password.toCharArray());
                }
                if (userName != null) {
                    this.connectionOptions.setUserName(this.userName);
                }
            }
            this.mqttClient.connect(this.connectionOptions);
            this.mqttClient.setCallback(this);
        } catch (MqttException me) {
            logger.error("ERROR", me);
        }
    }


    /*
     * (non-Javadoc)
     * @see com.monirthought.mqtt.publisher.MQTTPublisherBase#publishMessage(java.lang.String, java.lang.String)
     */
    @Override
    public void publishMessage(String topic, String message) {

        try {
            MqttMessage mqttmessage = new MqttMessage(message.getBytes());
            mqttmessage.setQos(this.qos);
            this.mqttClient.publish(topic, mqttmessage);
        } catch (MqttException me) {
            logger.error("ERROR", me);
        }

    }

    /*
     * (non-Javadoc)
     * @see org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost(java.lang.Throwable)
     */
    @Override
    public void connectionLost(Throwable arg0) {
        logger.info("Connection Lost");

    }

    /*
     * (non-Javadoc)
     * @see org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete(org.eclipse.paho.client.mqttv3.IMqttDeliveryToken)
     */
    @Override
    public void deliveryComplete(IMqttDeliveryToken arg0) {
        logger.info("delivery completed");

    }

    /*
     * (non-Javadoc)
     * @see org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived(java.lang.String, org.eclipse.paho.client.mqttv3.MqttMessage)
     */
    @Override
    public void messageArrived(String arg0, MqttMessage arg1) throws Exception {
        // Leave it blank for Publisher

    }

    /*
     * (non-Javadoc)
     * @see com.monirthought.mqtt.publisher.MQTTPublisherBase#disconnect()
     */
    @Override
    public void disconnect() {
        try {
            this.mqttClient.disconnect();
        } catch (MqttException me) {
            logger.error("ERROR", me);
        }
    }

}

5. Subscriber 訂閱者

定義接口

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public interface IMQTTSubscriber {
    public static final Logger logger = LoggerFactory.getLogger(IMQTTSubscriber.class);

    /**
     * Subscribe message
     *
     * @param topic
     * @param jasonMessage
     */
    public void subscribeMessage(String topic);

    /**
     * Disconnect MQTT Client
     */
    public void disconnect();
}

類定義

import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.sql.Timestamp;

@Component
public class MQTTSubscriber extends MQTTConfig implements MqttCallback, IMQTTSubscriber {

    private String brokerUrl = null;
    final private String colon = ":";
    final private String clientId = "mqtt_server_sub";

    private MqttClient mqttClient = null;
    private MqttConnectOptions connectionOptions = null;
    private MemoryPersistence persistence = null;

    private static final Logger logger = LoggerFactory.getLogger(MQTTSubscriber.class);

    public MQTTSubscriber() {
        this.config();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost(java.lang.
     * Throwable)
     */
    @Override
    public void connectionLost(Throwable cause) {
        logger.info("Connection Lost");

    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived(java.lang.String,
     * org.eclipse.paho.client.mqttv3.MqttMessage)
     */
    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        // Called when a message arrives from the server that matches any
        // subscription made by the client
        String time = new Timestamp(System.currentTimeMillis()).toString();
        System.out.println();
        System.out.println("***********************************************************************");
        System.out.println("Message Arrived at Time: " + time + "  Topic: " + topic + "  Message: "
                + new String(message.getPayload()));
        System.out.println("***********************************************************************");
        System.out.println();
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete(org.eclipse.paho
     * .client.mqttv3.IMqttDeliveryToken)
     */
    @Override
    public void deliveryComplete(IMqttDeliveryToken token) {
        // Leave it blank for subscriber

    }

    /*
     * (non-Javadoc)
     *
     * @see
     * com.monirthought.mqtt.subscriber.MQTTSubscriberBase#subscribeMessage(java.
     * lang.String)
     */
    @Override
    public void subscribeMessage(String topic) {
        try {
            this.mqttClient.subscribe(topic, this.qos);
        } catch (MqttException me) {
            me.printStackTrace();
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see com.monirthought.mqtt.subscriber.MQTTSubscriberBase#disconnect()
     */
    public void disconnect() {
        try {
            this.mqttClient.disconnect();
        } catch (MqttException me) {
            logger.error("ERROR", me);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see com.monirthought.config.MQTTConfig#config(java.lang.String,
     * java.lang.Integer, java.lang.Boolean, java.lang.Boolean)
     */
    @Override
    protected void config(String broker, Integer port, Boolean ssl, Boolean withUserNamePass) {

        String protocal = this.TCP;
        if (true == ssl) {
            protocal = this.SSL;
        }

        this.brokerUrl = protocal + this.broker + colon + port;
        this.persistence = new MemoryPersistence();
        this.connectionOptions = new MqttConnectOptions();

        try {
            this.mqttClient = new MqttClient(brokerUrl, clientId, persistence);
            this.connectionOptions.setCleanSession(true);
            if (true == withUserNamePass) {
                if (password != null) {
                    this.connectionOptions.setPassword(this.password.toCharArray());
                }
                if (userName != null) {
                    this.connectionOptions.setUserName(this.userName);
                }
            }
            this.mqttClient.connect(this.connectionOptions);
            this.mqttClient.setCallback(this);
        } catch (MqttException me) {
            me.printStackTrace();
        }

    }

    /*
     * (non-Javadoc)
     *
     * @see com.monirthought.config.MQTTConfig#config()
     */
    @Override
    protected void config() {

        this.brokerUrl = this.TCP + this.broker + colon + this.port;
        this.persistence = new MemoryPersistence();
        this.connectionOptions = new MqttConnectOptions();
        try {
            this.mqttClient = new MqttClient(brokerUrl, clientId, persistence);
            this.connectionOptions.setCleanSession(true);
            this.mqttClient.connect(this.connectionOptions);
            this.mqttClient.setCallback(this);
        } catch (MqttException me) {
            me.printStackTrace();
        }

    }

}

6. 構(gòu)建 RestFul接口

構(gòu)建Controller

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;

@Slf4j
@RestController
public class DemoRestController {
    public static String TOPIC_LOOP_TEST = "mqtt/loop/message";

        @Autowired
    IMQTTPublisher publisher;

    @Autowired
    IMQTTSubscriber subscriber;
    
    @PostConstruct
    public void init() {
        subscriber.subscribeMessage(TOPIC_LOOP_TEST);    
    }
    
    @RequestMapping(value = "/mqtt/loop/message", method = RequestMethod.POST)
    public String index(@RequestBody String data) {
        publisher.publishMessage(TOPIC_LOOP_TEST, data);
        return "Success";
    }
    
}    

7. 使用curl命令進(jìn)行api調(diào)用測(cè)試

? curl -X POST "http://127.0.0.1:8080/mqtt/loop/message" -d "test"
Success%

# springboot 窗口中可以看到自己sub的回顯
***********************************************************************
Message Arrived at Time: 2019-05-21 16:11:13.675  Topic: mqtt/loop/message  Message: test=
***********************************************************************

也可以使用postman 調(diào)用8080 端口調(diào)試荠呐。

構(gòu)建Java-Mqtt-Server (Springboot + Mqtt +protobuf)

在現(xiàn)有基礎(chǔ)上添加protobuf包裝pub/sub 消息

1. proto文件

將.proto文件放到src/main/proto/

2. 使用maven生成protobuf java代碼

pom中properties中添加

<properties>
    <java.version>1.8</java.version>
    <grpc.version>1.6.1</grpc.version>
    <protobuf.version>3.3.0</protobuf.version>
</properties>

pom dependencies中添加

<dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty</artifactId>
            <version>${grpc.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>${grpc.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>${grpc.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>${protobuf.version}</version>
        </dependency>

pom build中添加,pom plugins中添加

<build>
        <!--protobuf ext-->
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.5.0.Final</version>
            </extension>
        </extensions>

        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.0</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

使用IDE中右側(cè)Maven Projects -> Lifecycle ->compile 生成java對(duì)應(yīng)的protobuf文件

生成的路徑在:target/generated-sources/protobuf/java/對(duì)應(yīng)的包名下

3. 使用proto封裝mqtt message

使用Proto中的newBuilder構(gòu)建builder砂客。使用builder中的set方法設(shè)置proto中的參數(shù)泥张,例如:

KylinProto.Group.Builder builder = KylinProto.Group.newBuilder();
KylinProto.Group group = builder.setThreshold(85.f)
                .setTop(1)
                .setGroup(group_name).build();
publisher.publish(topic, group.toByteArray(), 2, false);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鞠值,隨后出現(xiàn)的幾起案子媚创,更是在濱河造成了極大的恐慌,老刑警劉巖彤恶,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钞钙,死亡現(xiàn)場(chǎng)離奇詭異鳄橘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)芒炼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門瘫怜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人本刽,你說我怎么就攤上這事鲸湃。” “怎么了子寓?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵唤锉,是天一觀的道長。 經(jīng)常有香客問我别瞭,道長窿祥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任蝙寨,我火速辦了婚禮晒衩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘墙歪。我一直安慰自己听系,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布虹菲。 她就那樣靜靜地躺著靠胜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪毕源。 梳的紋絲不亂的頭發(fā)上浪漠,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音霎褐,去河邊找鬼址愿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛冻璃,可吹牛的內(nèi)容都是我干的响谓。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼省艳,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼娘纷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起跋炕,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤赖晶,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后枣购,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嬉探,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡擦耀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涩堤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片眷蜓。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖胎围,靈堂內(nèi)的尸體忽然破棺而出吁系,到底是詐尸還是另有隱情,我是刑警寧澤白魂,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布汽纤,位于F島的核電站,受9級(jí)特大地震影響福荸,放射性物質(zhì)發(fā)生泄漏蕴坪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一敬锐、第九天 我趴在偏房一處隱蔽的房頂上張望背传。 院中可真熱鬧,春花似錦台夺、人聲如沸径玖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梳星。三九已至,卻和暖如春滚朵,著一層夾襖步出監(jiān)牢的瞬間冤灾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工始绍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瞳购,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓亏推,卻偏偏與公主長得像,于是被迫代替她去往敵國和親年堆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吞杭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 記錄一下自己學(xué)習(xí)mqtt的過程,如何從零開始使用Node.js實(shí)現(xiàn)mqtt協(xié)議变丧,實(shí)現(xiàn)一個(gè)web頁面向android...
    eunice_w閱讀 15,879評(píng)論 0 7
  • 使用 WebSocket 客戶端連接 MQTT 服務(wù)器 [TOC] 簡介 近年來隨著 Web 前端的快速發(fā)展芽狗,瀏覽...
    wivwiv閱讀 4,319評(píng)論 0 8
  • 本系列文章以當(dāng)前普遍使用的 MQTT 版本 3.1.1 為例,結(jié)合 wireshark 工具痒蓬,詳細(xì)解析 MQTT ...
    Shawn_xiaoyu閱讀 1,373評(píng)論 2 3
  • 春雨忙潤田 青風(fēng)裁新顏 波光映匆影 愿景思無眠 孫天一 20160501 9:58
    孫然義閱讀 136評(píng)論 0 1
  • 防霧霾糖水童擎,銀耳紅棗雪梨湯是對(duì)肺非常好飲品滴劲,老少男女皆宜。肺喜潤顾复,肺臟在潤的環(huán)境中班挖,功能發(fā)揮的最好,也最強(qiáng)大芯砸。既然...
    謝曉霞閱讀 318評(píng)論 0 1