MQTT協(xié)議學(xué)習(xí)與在Java(Android通用)中的使用

寫在前面

最近有需求要了解一下各個(gè)推送的協(xié)議,目前了解到實(shí)現(xiàn)推送的三個(gè)主要方式:MQTT、XMPP和Google Cloud Message(GCM)。第三種方式暫不研究,前兩種都要看一看,本篇討論一下MQTT協(xié)議吧。本文使用阿里云Ubuntu云服務(wù)器安裝代理服務(wù)器,使用eclipse paho實(shí)現(xiàn)的MqttClient編寫代碼。文中的所使用的賬戶名和密碼在本文發(fā)布后將會(huì)更改,請(qǐng)各位自行搭建環(huán)境。本文包括以下內(nèi)容:

  • MQTT簡(jiǎn)介
  • MQTT優(yōu)勢(shì)
  • MQTT開發(fā)環(huán)境搭建
  • 使用PAHO實(shí)現(xiàn)MQTT推送

MQTT簡(jiǎn)介 & MQTT優(yōu)勢(shì)

MQTT全稱是Message Queuing Telemetry Transport,MQTT是IBM開發(fā)的基于TCP/IP協(xié)議的輕量級(jí)通訊協(xié)議。MQTT是一個(gè)客戶端服務(wù)端架構(gòu)的發(fā)布-訂閱(publish-subscribe)的消息傳輸協(xié)議。它的設(shè)計(jì)思想是開放、簡(jiǎn)單、輕量、易于實(shí)現(xiàn)。這些特點(diǎn)使它適用于受限環(huán)境。例如,但不僅限于:

  • 網(wǎng)絡(luò)代價(jià)昂貴,帶寬低、不可靠
  • 在嵌入式設(shè)備中運(yùn)行,處理器和內(nèi)存資源有限

作為一個(gè)物聯(lián)網(wǎng)專業(yè)的畢業(yè)生,看了以上的描述已經(jīng)心動(dòng)了,很適合作為傳感器節(jié)點(diǎn)之間的通訊協(xié)議哇!哦,忘了,我現(xiàn)在是個(gè)Androider……MQTT控制報(bào)文頭部?jī)H有2字節(jié)的長度,降低了網(wǎng)絡(luò)傳輸所需要的流量。MQTT支持三種不同級(jí)別的服務(wù)質(zhì)量(Quality of Service,QoS)為不同場(chǎng)景提供消息可靠性:

  • 級(jí)別0:盡力而為。消息發(fā)送者會(huì)想盡辦法發(fā)送消息,但是遇到意外并不會(huì)重試。
  • 級(jí)別1:至少一次。消息接受者如果沒有知會(huì)或者知會(huì)本身丟失,消息發(fā)送者會(huì)在此發(fā)送以保證消息接收者至少會(huì)收到一次,當(dāng)然可能造成重復(fù)消息。
  • 級(jí)別2:恰好一次。保證這種語義肯定會(huì)減少并發(fā)或者增加延時(shí),不過丟失或者重復(fù)消息是不可接受的時(shí)候,級(jí)別2是最合適的。

如果各位讀完了這些仍然覺得不過癮,沒有戳中各位的痛點(diǎn),可以去讀一下MQTT的協(xié)議規(guī)范,這里中英文版本都有挑自己愛看的讀一下就好。

MQTT開發(fā)環(huán)境搭建

首先需要一個(gè)代理服務(wù)器,這里mqtt代理服務(wù)器使用的是apache的apollo,apollo支持STOMP,AMQP,MQTT,Openwire,SSL和WebSockets。下載戳這

點(diǎn)這個(gè)就行

下載到本地之后,將之上傳到服務(wù)器上:

$ scp 文件名 $username@$ip:~

解壓tar.gz:

$ tar zxvf apache-apollo-1.7.1-unix-distro.tar.gz

進(jìn)入解壓后的bin目錄下執(zhí)行apollo create testbroker命令創(chuàng)建一個(gè)名稱為testbroker的代理服務(wù)器。

$ cd apache-apollo-1.7.1-unix-distro.tar.gz/bin/
$ ./apollo create testbroker

輸入ls命令就可以看到文件夾下多了一個(gè)testbroker的文件夾

testbroker

進(jìn)入testbroker的bin文件夾下,執(zhí)行apollo-broker run 啟動(dòng)代理服務(wù)器。進(jìn)入testbroker文件下的etc文件夾,可以看到名為users.properties的文件,可以看到在最后配置了用戶名和密碼:

用戶名&密碼

該文件夾下的apollo.xml中配置了端口和ip,不過這里就不管了。代理服務(wù)器配置完畢,接下來就是下載paho實(shí)現(xiàn)的mqtt client的jar包了。
下載地址

下載這個(gè)

使用PAHO實(shí)現(xiàn)MQTT推送

這里利用Idea編寫Java程序?qū)崿F(xiàn),對(duì)于Android程序來說只需要稍加修改就可直接使用。首先新建一個(gè)Java項(xiàng)目,接著將上面下載的jar包作為依賴導(dǎo)入。首先編寫服務(wù)端:

import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

public class MqttServer {
    /**
     * 代理服務(wù)器ip地址
     */
    public static final String MQTT_BROKER_HOST = "tcp://xiasuhuei321.com:61613";

    /**
     * 訂閱標(biāo)識(shí)
     */
    public static final String MQTT_TOPIC = "test";

    private static String userName = "admin";
    private static String password = "password";

    /**
     * 客戶端唯一標(biāo)識(shí)
     */
    public static final String MQTT_CLIENT_ID = "android_server_xiasuhuei321";
    private static MqttTopic topic;
    private static MqttClient client;

    public static void main(String... args) {
        // 推送消息
        MqttMessage message = new MqttMessage();
        try {
            client = new MqttClient(MQTT_BROKER_HOST, MQTT_CLIENT_ID, new MemoryPersistence());
            MqttConnectOptions options = new MqttConnectOptions();
            options.setCleanSession(true);
            options.setUserName(userName);
            options.setPassword(password.toCharArray());
            options.setConnectionTimeout(10);
            options.setKeepAliveInterval(20);

            topic = client.getTopic(MQTT_TOPIC);

            message.setQos(1);
            message.setRetained(false);
            message.setPayload("message from server".getBytes());
            client.connect(options);

            while (true) {
                MqttDeliveryToken token = topic.publish(message);
                token.waitForCompletion();
                System.out.println("已經(jīng)發(fā)送");
                Thread.sleep(10000);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

這里的邏輯非常簡(jiǎn)單,創(chuàng)建一個(gè)MqttClient,每十秒發(fā)送一次消息,訂閱了相應(yīng)topic的客戶端將會(huì)收到這個(gè)消息。接下來編寫客戶端:

import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

public class MyMqttClient {
    /**
     * 代理服務(wù)器ip地址
     */
    public static final String MQTT_BROKER_HOST = "tcp://xiasuhuei321.com:61613";

    /**
     * 客戶端唯一標(biāo)識(shí)
     */
    public static final String MQTT_CLIENT_ID = "android_xiasuhuei321";

    /**
     * 訂閱標(biāo)識(shí)
     */
    public static final String MQTT_TOPIC = "xiasuhuei321";

    /**
     *
     */
    public static final String USERNAME = "admin";
    /**
     * 密碼
     */
    public static final String PASSWORD = "password";

    private volatile static MqttClient mqttClient;
    private static MqttConnectOptions options;

    public static void main(String... args) {
        try {
            // host為主機(jī)名,clientid即連接MQTT的客戶端ID,一般以客戶端唯一標(biāo)識(shí)符表示,
            // MemoryPersistence設(shè)置clientid的保存形式,默認(rèn)為以內(nèi)存保存
            // 設(shè)備id不要太騷氣!!!!!!!
            mqttClient = new MqttClient(MQTT_BROKER_HOST, MQTT_CLIENT_ID, new MemoryPersistence());
            // 配置參數(shù)信息
            options = new MqttConnectOptions();
            // 設(shè)置是否清空session,這里如果設(shè)置為false表示服務(wù)器會(huì)保留客戶端的連接記錄,
            // 這里設(shè)置為true表示每次連接到服務(wù)器都以新的身份連接
            options.setCleanSession(true);
            // 設(shè)置用戶名
            options.setUserName(USERNAME);
            // 設(shè)置密碼
            options.setPassword(PASSWORD.toCharArray());
            // 設(shè)置超時(shí)時(shí)間 單位為秒
            options.setConnectionTimeout(10);
            // 設(shè)置會(huì)話心跳時(shí)間 單位為秒 服務(wù)器會(huì)每隔1.5*20秒的時(shí)間向客戶端發(fā)送個(gè)消息判斷客戶端是否在線,但這個(gè)方法并沒有重連的機(jī)制
            options.setKeepAliveInterval(20);
            // 連接
            mqttClient.connect(options);
            // 訂閱
            mqttClient.subscribe("test");
            // 設(shè)置回調(diào)
            mqttClient.setCallback(new MqttCallback() {
                @Override
                public void connectionLost(Throwable throwable) {
                    System.out.println("connectionLost");
                }

                @Override
                public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
                    System.out.println("Topic: " + s + " Message: " + mqttMessage.toString());
                }

                @Override
                public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {

                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

接下來啟動(dòng)服務(wù)端和客戶端看一下效果

服務(wù)端
客戶端

思考

到這也差不多了,說實(shí)話,在Android中難的從來都不是實(shí)現(xiàn)推送,而是如何保證接收推送的服務(wù)存活。在Android對(duì)后臺(tái)服務(wù)限制越來越大的現(xiàn)在,自己實(shí)現(xiàn)推送的意義可能并不是非常大。但是對(duì)于一些特殊的應(yīng)用場(chǎng)景下,比如用戶打開應(yīng)用進(jìn)行的一些操作需要用到長連接,自己實(shí)現(xiàn)推送可能會(huì)更加可靠一些(聽朋友說三方推送有時(shí)會(huì)莫名其妙收不到推送)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評(píng)論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,786評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,986評(píng)論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,204評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,964評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,354評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,554評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,106評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,918評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,093評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,342評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,755評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,009評(píng)論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,839評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,107評(píng)論 2 375

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,804評(píng)論 18 139
  • 關(guān)鍵詞 JMS、ActiveMQ(ActivityMQ)、Apollo、MQTT、Android 摘要 由于項(xiàng)目開...
    KtYY閱讀 4,614評(píng)論 2 21
  • 最近項(xiàng)目需要做到網(wǎng)絡(luò)環(huán)境復(fù)雜 網(wǎng)絡(luò)環(huán)境差的情況下 實(shí)時(shí)刷新終端(凈化器)狀態(tài),客戶端數(shù)據(jù),相比較于htt...
    Mr_不靠譜_先森閱讀 2,224評(píng)論 0 4
  • 有那么一個(gè)人, 無數(shù)次讓我紅了眼眶, 我還在一臉笑著原諒; 有那么一個(gè)人, 丟棄了曾經(jīng)對(duì)我的諾言, 我卻依舊緊鎖他...
    紫玲閱讀 209評(píng)論 0 0