Mqtt搭建代理伺服器進行通訊-淺析

孤獨的二狗發表於2017-12-29

本文基於Windows系統操作

MQTT簡介:

MQTT(Message Queuing Telemetry Transport,訊息佇列遙測傳輸)是IBM開發的一個即時通訊協議!
複製程式碼

MQTT訊息的主要特點:

使用(publish/subscribe)訊息模式,簡稱p/s模式,即釋出/訂閱!提供一對多的傳送方式!
複製程式碼
MQTT根據QoS定義的等級來傳輸訊息:
  • level 0:最多一次的傳輸
訊息是基於TCP/IP網路傳輸的。沒有迴應,在協議中也沒有定義重傳的語義。訊息可能到達伺服器1次,也可能根本不會到達。
複製程式碼
  • level 1:至少一次的傳輸
伺服器接收到訊息會被確認,通過傳輸一個PUBACK資訊。如果有一個可以辨認的傳輸失敗,無論是通訊連線還是傳送裝置,還是過了一段時間確認資訊沒有收到,傳送方都會將訊息頭的DUP位置1,然後再次傳送訊息。訊息最少一次到達伺服器。SUBSCRIBE和UNSUBSCRIBE都使用level 1 的QoS。
如果客戶端沒有接收到PUBACK資訊(無論是應用定義的超時,還是檢測到失敗然後通訊session重啟),客戶端都會再次傳送PUBLISH資訊,並且將DUP位置1。
當它從客戶端接收到重複的資料,伺服器重新傳送訊息給訂閱者,並且傳送另一個PUBACK訊息。
複製程式碼
  • level 2: 只有一次的傳輸
在QoS level 1上附加的協議流保證了重複的訊息不會傳送到接收的應用。這是最高階別的傳輸,當重複的訊息不被允許的情況下使用。這樣增加了網路流量,但是它通常是可以接受的,因為訊息內容很重要。
QoS level 2在訊息頭有Message ID。
複製程式碼

接下來開始我們的表演:

下載代理伺服器
  • 本文使用mqtt代理伺服器是apache下的apollo代理伺服器

  • 下載地址:http://www.apache.org/dyn/closer.cgi?path=activemq/activemq-apollo/1.7.1/apache-apollo-1.7.1-unix-distro.tar.gz

  • Paste_Image.png

建立代理伺服器
  • 下載完成然後解壓目錄

  • 開啟dos視窗進入到apache-apollo-1.7.1\bin目錄下

  • 執行apollo create testbroker命令建立一個名稱為testbroker的代理伺服器

  • Paste_Image.png

  • 下面就是我們建立的代理伺服器

    Paste_Image.png

啟動代理伺服器
  • 使用dos進入testbroker目錄中的bin目錄下

  • 執行apollo-broker run命令啟動代理伺服器

  • Paste_Image.png

通過HTTP訪問代理伺服器

現在我們可以開啟瀏覽器看下我們的代理伺服器 輸入網址http://127.0.0.1:61680/

  • Paste_Image.png

  • 使用者名稱密碼可到配置檔案中檢視

  • 進入testbroker目錄下的etc目錄

Paste_Image.png

  • users.properties中配置的使用者名稱和密碼

Paste_Image.png

  • 預設有個使用者名稱為admin,密碼為password的使用者

  • 我們也可以自己配置使用者

  • 現在就用預設使用者登陸

  • Paste_Image.png

OK登陸成功

接下來我們編寫Android客戶端

  • 首先準備mqtt jar包
  • 不容易呀,mqtt這個jar真難找
  • https://repo.eclipse.org/content/repositories/paho/org/eclipse/paho/org.eclipse.paho.client.mqttv3/1.0.2/

Paste_Image.png

一定保證客戶端和服務端以及代理伺服器所在的電腦在同一網段下
  • 可以在電腦上生成wifi熱點,手機客戶端連線熱點即可
  • 接下來直接貼Android客戶端程式碼
MqttService.java
package com.example.jingwc.mqtt_demo;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttTopic;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

public class MqttService extends Service {

    /**
     * 代理伺服器ip地址
     */
    public static final String MQTT_BROKER_HOST = "tcp://192.168.1.107:61613";

    /**
     * 客戶端唯一標識
     */
    public static final String MQTT_CLIENT_ID = "android-jingwc";

    /**
     * 訂閱標識
     */
    public static final String MQTT_TOPIC = "jingwc";

    /**
     * 使用者名稱
     */
    public static final String USERNAME = "admin";
    /**
     *  密碼
     */
    public static final String PASSWORD = "password";

    private MqttClient mqttClient;

    public MqttService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    /**
     * 連線mqtt
     */
    public void connect(){
        try {
            // host為主機名,clientid即連線MQTT的客戶端ID,一般以客戶端唯一識別符號表示,
            // MemoryPersistence設定clientid的儲存形式,預設為以記憶體儲存
            mqttClient = new MqttClient(MQTT_BROKER_HOST,MQTT_CLIENT_ID,new MemoryPersistence());
            // 配置引數資訊
            MqttConnectOptions options = new MqttConnectOptions();
            // 設定是否清空session,這裡如果設定為false表示伺服器會保留客戶端的連線記錄,
            // 這裡設定為true表示每次連線到伺服器都以新的身份連線
            options.setCleanSession(true);
            // 設定使用者名稱
            options.setUserName(USERNAME);
            // 設定密碼
            options.setPassword(PASSWORD.toCharArray());
            // 設定超時時間 單位為秒
            options.setConnectionTimeout(10);
            // 設定會話心跳時間 單位為秒 伺服器會每隔1.5*20秒的時間向客戶端傳送個訊息判斷客戶端是否線上,但這個方法並沒有重連的機制
            options.setKeepAliveInterval(20);
            // 連線
            mqttClient.connect(options);

            // 訂閱
            mqttClient.subscribe(MQTT_TOPIC);

            // 設定回撥
            mqttClient.setCallback(new MqttCallback() {
                //連線丟失後,一般在這裡面進行重連
                @Override
                public void connectionLost(Throwable throwable) {
                    Log.d("test","connectionLost");
                }


                //subscribe後得到的訊息會執行到這裡面
                @Override
                public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
                    Log.d("test","messageArrived"+mqttMessage.toString());
                }

                //publish後會執行到這裡
                @Override
                public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
                    Log.d("test","deliveryComplete");
                }
            });

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

    /**
     * 斷開連線
     */
    public void disconnect(){
        if(mqttClient != null){
            if(mqttClient.isConnected()){
                try {
                    mqttClient.disconnect();
                    mqttClient = null;
                } catch (MqttException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    private final Binder binder = new MyBinder();

    class MyBinder extends Binder{

        public MqttService getService(){
            return MqttService.this;
        }
    }
}

複製程式碼
MainActivity.java
package com.example.jingwc.mqtt_demo;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {

    MqttService service = null;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            service = ((MqttService.MyBinder)iBinder).getService();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            service = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button bt_connect = (Button) findViewById(R.id.bt_connect);
        Button bt_disconnect = (Button) findViewById(R.id.bt_disconnect);

        bt_connect.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 連線
                service.connect();
            }
        });

        bt_disconnect.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 斷開連線
                service.disconnect();
            }
        });

        bindService(new Intent(this,MqttService.class),mConnection,BIND_AUTO_CREATE);
    }
}
複製程式碼

服務端程式碼

  • 也可以在寫一個android程式當作服務端
  • 我這裡寫的是java專案
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttTopic;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

public class MqttServer {
	
	// 代理伺服器ip地址
	private static String host = "tcp://192.168.1.107:61613";
	
	private static String userName = "admin";
	private static String password = "password";
	
	private static MqttClient client;
	
	// 主題
	private static MqttTopic topic;
	
	private static MqttMessage message;
	
	// 訂閱標識
	private static String topicStr = "jingwc";
	
	public static void main(String[] args) throws MqttException{
		client = new MqttClient(host,"java-server-jingwc",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(topicStr);
		
		message = new MqttMessage();
		message.setQos(1);
		message.setRetained(true);
		message.setPayload("from server message".getBytes());
		client.connect(options);
		MqttDeliveryToken token = topic.publish(message);
		token.waitForCompletion();
		System.out.println("token:"+token.isComplete());
		
	}

}
複製程式碼

服務端通過代理伺服器傳送客戶端訂閱的訊息圖 ( 個人理解 )

  • Paste_Image.png

Paste_Image.png

相關文章