MQTT——使用EMQ服务器进行通信

部署安装

  1. 从官网中下载对应版本的emq服务器,对于linux下的部署安装在官方文档中有着资料。
  2. 下载完成后需进行安装。以下是安装启动步骤(此为在windows下的操作)
    2.1 emq代理服务器下的安装
    MQTT——使用EMQ服务器进行通信_第1张图片
    2.2 emq服务器的启动/关闭、及查看
    MQTT——使用EMQ服务器进行通信_第2张图片
    2.3 控制台
    控制台地址: http://127.0.0.1:18083,默认用户: admin,密码:public
    MQTT——使用EMQ服务器进行通信_第3张图片

搭建MQTT客户端(java)

1. 下载MQTT客户端库
MQTT客户端库:https://www.eclipse.org/paho/clients/java/
2.配置MQTT属性
定义mqtt配置属性类:

@Component
@Data
public class Wx2MqttConfig {
    @Value("${mqtt.host}")
    private String host;

    @Value("${mqtt.port}")
    private String port;

    @Value("${mqtt.username}")
    private String username;

    @Value("${mqtt.clientId}")
    private String clientId;

    @Value("${mqtt.password}")
    private String password;
    }

3. 定义主题类

public class Topic {
    //用来分路由处理的:
    public static final String DEVICE_ACK_BIND = "device/+/ack/bind";
    public static final String DEVICE_ACK_UNBIND = "device/+/ack/unbind";
    public static final String DEVICE_ACK_GET = "device/+/ack/get";
    public static final String DEVICE_ACK_SET = "device/+/ack/set";
    public static final String DEVICE_NOTIFY = "device/+/notify";

    //用来publish的(绑定/解绑 & 设置/获取)
    public static final String SERVER_BIND_PREFIX = "server/bind/";
    public static final String SERVER_UNBIND_PREFIX = "server/unbind/";
    public static final String SERVER_SET_PREFIX = "server/set/";
    public static final String SERVER_GET_PREFIX = "server/get/";

    public static final int[] QOS = {2,2,2,2,2}; //定义消息级别
    public static final String[] TOPICS = {
            DEVICE_ACK_BIND,
            DEVICE_ACK_UNBIND,
            DEVICE_ACK_GET,
            DEVICE_ACK_SET,
            DEVICE_NOTIFY
    };

}

4. 实现MqttCallback,实现发布/订阅方法

@Service
public class Wx2MqttService implements MqttCallback {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private MqttClient client;

    @Autowired
    private Wx2MqttConfig wx2MqttConfig;
  
    @PostConstruct
    public void init(){
        logger.info("初始化Wx2MqttService");
        //TODO 分出支持tls格式的config。
        try {
            client = new MqttClient(wx2MqttConfig.getHost() + ":" + wx2MqttConfig.getPort(), wx2MqttConfig.getClientId(), new MemoryPersistence());
            reConnect();
        } catch (MqttException e) {
            e.printStackTrace();
            logger.error(e.toString());
        }
    }

    public void reConnect() {

        try {
            connect();
            subscribe();
            client.setCallback(this);
        } catch (MqttException e) {
            e.printStackTrace();
        }

    }

    private void connect() throws MqttException{
        //阻塞在此直到连上或超时没连上
        logger.info("阻塞在此直到连上或超时没连上");
        client.connect(getConnectOptions());
        logger.info("阻塞完毕:Connected=" + client.isConnected());

    }

    private MqttConnectOptions connectOptions = null;

    private MqttConnectOptions getConnectOptions() {

        if (connectOptions == null) {
            connectOptions = new MqttConnectOptions();
            connectOptions.setCleanSession(false);
            connectOptions.setAutomaticReconnect(true);
            connectOptions.setUserName(wx2MqttConfig.getUsername());
            connectOptions.setPassword(wx2MqttConfig.getPassword().toCharArray());
            // 设置超时时间 TODO 这两个参数设入properties中
            connectOptions.setConnectionTimeout(10);
            // 设置会话心跳时间
            connectOptions.setKeepAliveInterval(20);
        }
        return connectOptions;

    }

    //订阅消息(默认主题)
    private void subscribe() throws MqttException {
        logger.info("初始化订阅消息");
        client.subscribe(Topic.TOPICS,Topic.QOS);
    }

    //订阅特定主题
    public void subscribe(String topic) throws MqttException {
        logger.info("订阅"+topic+"消息");
        this.subscribe(topic,0);

    }

    //订阅特定主题,附加消息级别
    public void subscribe(String topic, int qos) throws MqttException {
        logger.info("订阅"+topic+"消息,消息级别为:"+qos);
        client.subscribe(topic,qos);
    }


    //发布消息
    public boolean publish(String topic,String message){
        MqttMessage mqttMessage = new MqttMessage();
        mqttMessage.setQos(2);
        mqttMessage.setRetained(true);
        mqttMessage.setPayload(message.getBytes());
        return this.publish(topic,mqttMessage);
    }


    public boolean publish(String topic, MqttMessage mqttMessage) {
        logger.info("准备publish的topic:" + topic);
        logger.info("准备publish的mqttMsg:" + new String(mqttMessage.getPayload()));
        if (client == null || !client.isConnected()) {
            reConnect();
            logger.error("Wx2MqttService未连接上,无法publish");
            return false;
        }
        try {
            MqttTopic mqttTopic = client.getTopic(topic);
            MqttDeliveryToken token = mqttTopic.publish(mqttMessage);
            token.waitForCompletion();
        } catch (MqttException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }


    // 连接丢失后,一般在这里面进行重连
    @Override
    public void connectionLost(Throwable throwable) {
        // TODO - 多次重连不行时发短信等方式通知维护人员(标-的TODO当前并不优先考虑实现)
        logger.info("连接断开");
        logger.info("连接断开,正在重连");
        reConnect();
    }


    //publish后会执行到这里
    @Override
    public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
        logger.info("publish后的deliveryComplete:" + iMqttDeliveryToken.isComplete() + "");
    }

    //TODO subscribe后得到的消息会执行到这里面
    @Override
    public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {

        logger.info("\n 【接收到mqtt消息】:[topic=[{}],qos=[{}] mqttMessage=[{}]", topic,mqttMessage.getQos(), mqttMessage);
        if (StringUtils.isEmpty(topic)) {
            return;
        }
        String json = new String(mqttMessage.getPayload());
        //根据主题及订阅到的数据进行判断,选择对应的操作。。。
    }


    public WeixinService getWxService(){
        return wxService;
    }

}

这里用了@PostConstruct,目的在于启动时即能打开客户端与emq服务器的通信,同时也能将客户端进行订阅主题和相应的消息级别。

备注

  1. 如果需要测试的话,可以下载mqttfx进行MQTT协议调试。下载链接:http://www.jensd.de/apps/mqttfx/1.1.0/
  2. mqtt协议基于主题(topic)进行发布/订阅,以实现开发者云平台与硬件的通信。其主要在于两边都连接上同一个EMQ代理服务器,EMQ服务器根据MQTT协议中的要求来控制数据的流动。而这里需要注意的概念是,A客户端中的发布主题即是B客户端的订阅主题,相反也是如此。MQTT协议的通信,有点儿类似于socket通信(即是tcp协议通信),它们的类似在于通信是双端的,不同在于mqtt可以定义消息级别来保证消息的传递。

你可能感兴趣的:(物联网)