emq官网插件地址 http://emqtt.com/docs/v2/plugins.html#
官方文档 https://docs.emqx.io/broker/v3/cn/
由于emqttd是用Erlang语言编写的,所以,在Linux下安装时,需要先安装Erlang
yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-devel
下载地址: http://emqtt.com/downloads
程序包下载后,可直接解压启动运行,例如 Mac 平台:
unzip emqttd-macosx-v2.0.zip && cd emqttd
禁用匿名认证
修改etc目录下的emq.conf文件, vim emq.conf 找到 mqtt.all_anonymous配置项将其置为false
建库建表
配置数据库
etc/plugins/emq_auth_mysql.conf 配置 ‘super_query’, ‘auth_query’, ‘password_hash’:
EMQ 消息服务器默认访问控制,在 etc/emq.conf 中设置:
## ACL nomatch
mqtt.acl_nomatch = allow
## Default ACL File
mqtt.acl_file = etc/acl.conf
# 启动emqttd
./bin/emqttd start
# 检查运行状态
./bin/emqttd_ctl status
# 停止emqttd
./bin/emqttd stop
/sbin/iptables -I INPUT -p tcp --dport 18083 -j ACCEPT
/sbin/iptables -I INPUT -p tcp --dport 1883 -j ACCEPT
/sbin/iptables -I INPUT -p tcp --dport 8083 -j ACCEPT
web页面管理端插件设置emq_dashboard.conf
emq_auth_mysql: MySQL 认证/访问控制插件?
MySQL 认证/访问控制插件,基于 MySQL 库表认证鉴权: https://github.com/emqtt/emq-auth-mysql
MQTT 用户表
CREATE TABLE `mqtt_user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
`salt` varchar(35) DEFAULT NULL,
`is_superuser` tinyint(1) DEFAULT 0,
`created` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `mqtt_username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
注解
MySQL 插件可使用系统自有的用户表,通过 ‘authquery’ 配置查询语句。
MQTT 访问控制表
CREATE TABLE `mqtt_acl` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`allow` int(1) DEFAULT NULL COMMENT '0: deny, 1: allow',
`ipaddr` varchar(60) DEFAULT NULL COMMENT 'IpAddress',
`username` varchar(100) DEFAULT NULL COMMENT 'Username',
`clientid` varchar(100) DEFAULT NULL COMMENT 'ClientId',
`access` int(2) NOT NULL COMMENT '1: subscribe, 2: publish, 3: pubsub',
`topic` varchar(100) NOT NULL DEFAULT '' COMMENT 'Topic Filter',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `mqtt_acl` (`id`, `allow`, `ipaddr`, `username`, `clientid`, `access`, `topic`)
VALUES
(1,1,NULL,'$all',NULL,2,'#'),
(2,0,NULL,'$all',NULL,1,'$SYS/#'),
(3,0,NULL,'$all',NULL,1,'eq #'),
(5,1,'127.0.0.1',NULL,NULL,2,'$SYS/#'),
(6,1,'127.0.0.1',NULL,NULL,2,'#'),
(7,1,NULL,'dashboard',NULL,1,'$SYS/#');
配置 MySQL 认证鉴权插件
etc/plugins/emq_auth_mysql.conf:
## Mysql Server
auth.mysql.server = 127.0.0.1:3306
## Mysql Pool Size
auth.mysql.pool = 8
## Mysql Username
## auth.mysql.username =
## Mysql Password
## auth.mysql.password =
## Mysql Database
auth.mysql.database = mqtt
## Variables: %u = username, %c = clientid
## Authentication Query: select password only
auth.mysql.auth_query = select password from mqtt_user where username = '%u' limit 1
## Password hash: plain, md5, sha, sha256, pbkdf2
auth.mysql.password_hash = sha256
## %% Superuser Query
auth.mysql.super_query = select is_superuser from mqtt_user where username = '%u' limit 1
## ACL Query Command
auth.mysql.acl_query = select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'
加载 MySQL 认证鉴权插件
./bin/emqttd_ctl plugins load emq_auth_mysql
emq_auth_username - 用户名密码认证插件?
EMQ 2.0-rc.2 版本将用户名认证模块改为独立插件: https://github.com/emqtt/emq_auth_username
用户名认证配置
etc/plugins/emq_auth_username.conf:
##auth.user.$N.username = admin
##auth.user.$N.password = public
## Examples:
##auth.user.1.username = admin
##auth.user.1.password = public
##auth.user.2.username = feng@emqtt.io
##auth.user.2.password = public
两种方式添加用户:
直接在 etc/plugins/emq_auth_username.conf 中明文配置默认用户例如:
auth.username.test = public
通过 ‘./bin/emqttd_ctl’ 管理命令行添加用户:
$ ./bin/emqttd_ctl users add <Username> <Password>
加载用户名认证插件
./bin/emqttd_ctl plugins load emq_auth_username
emq_web_hook Web Hook 插件
在web管理页面emq_web_hook配置一个可以访问的地址(java测试代码如下)
打断点观察(有客户端连接时会请求到管理端配置的地址)
import com.aisino.domain.SystemConfig;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@javax.servlet.annotation.WebServlet("/zjx.do")
public class WebServlet extends HttpServlet {
private static final Logger logger = LogManager.getLogger(WebServlet.class);
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding(SystemConfig.CHARSET);
response.setCharacterEncoding(SystemConfig.CHARSET);
response.setContentType("application/json");
ServletInputStream inputStream = request.getInputStream();
String requestXML = IOUtils.toString(inputStream,SystemConfig.CHARSET);
JSONObject jsonObject = JSON.parseObject(requestXML);
String active = jsonObject.getString("action");
String client_id = jsonObject.getString("client_id");
logger.info(active + client_id);
logger.info(requestXML);
IOUtils.closeQuietly(inputStream);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// req.setCharacterEncoding(SystemConfig.CHARSET);
request.setCharacterEncoding(SystemConfig.CHARSET);
response.setCharacterEncoding(SystemConfig.CHARSET);
response.setContentType("application/json");
// 定义返回的Json
// String reponse = StringUtils.EMPTY;
String responseXML = StringUtils.EMPTY;
final PrintWriter out = response.getWriter();
try {
// 业务处理方法
logger.info(responseXML);
} catch (Exception e) {
logger.error("--线程(ID:" + Thread.currentThread().getId() + ")系统异常:{}", e);
} finally {
// 向请求端发回反馈信息
out.print(responseXML);
out.flush();
out.close();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}
我这里有优化过的emq包(链接:https://pan.baidu.com/s/1jAze5JFeeQ7ltAVBfQOB-A 提取码:baph ),
直接解压,修改emqttd/etc/emq.conf 里参数:node.name就可以使用
管理端地址http://127.0.0.1:18083(默认用户名密码admin public)
emq提供的接口需要的可以使用,其他的自己学习。
服务端代码:
public class Server {
public static final String HOST = "tcp://192.168.103.1:1883";
public static final String TOPIC = "THIS/IS/TOPIC/ID/0";
private static final String clientid = "ID-0";
private MqttClient client;
private MqttTopic topic;
private String userName = "admin";
private String passWord = "public";
private MqttMessage message;
private String getMessage() throws Exception{
String reqJson =
"{"+
"'GHR':'zhangsan',"+
"'SHR':'lisi',"+
"'}";
return reqJson;
}
public Server() throws MqttException {
client = new MqttClient(HOST, clientid, new MemoryPersistence());
connect();
}
private void connect() {
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(false);
options.setUserName(userName);
options.setPassword(passWord.toCharArray());
options.setConnectionTimeout(200);
options.setKeepAliveInterval(20);
try {
client.setCallback(new PushCallback());
client.connect(options);
topic = client.getTopic(TOPIC);
} catch (Exception e) {
e.printStackTrace();
}
}
public void publish(MqttTopic topic , MqttMessage message) throws MqttPersistenceException,
MqttException {
MqttDeliveryToken token = topic.publish(message);
token.waitForCompletion();
System.out.println("message is published completely! "
+ token.isComplete());
}
public static void main(String[] args) throws Exception {
Server server = new Server();
server.message = new MqttMessage();
server.message.setQos(1);
server.message.setRetained(true);
server.message.setPayload(server.getMessage().getBytes());
server.publish(server.topic , server.message);
System.out.println(server.message.isRetained() + "------ratained");
}
}
客户端代码
public class Client {
public static final String TOPIC = "THIS/IS/TOPIC/ID/0";
private static final String clientid = SystemConfig.CLIENTID;
private MqttClient client;
private MqttConnectOptions options;
private String userName = "admin";
private String passWord = "public";
private void start() {
try {
client = new MqttClient(HOST, clientid, new MemoryPersistence());
options = new MqttConnectOptions();
options.setCleanSession(true);
options.setUserName(userName);
options.setPassword(passWord.toCharArray());
options.setConnectionTimeout(10000000);
options.setKeepAliveInterval(60000000);
client.setCallback(new PushCallback());
MqttTopic topic = client.getTopic(TOPIC);
client.connect(options);
int[] Qos = {1};
String[] topic1 = {TOPIC};
client.subscribe(topic1, Qos);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws MqttException {
new Thread(
new Runnable() {
@Override
public void run() {
JOptionPane.showMessageDialog( null,"D1设备启动.......");
}
}).start();
Client client = new Client();
client.start();
}
}
回调函数:
public class PushCallback implements MqttCallback {
int i= 0;
private static final Logger logger = LogManager.getLogger(PushCallback.class);
private final HandlerRequest handlerRequest = new HandlerRequest();
public void connectionLost(Throwable cause) {
logger.info("--线程(ID:" + Thread.currentThread().getId() + "),连接丢失了");
System.out.println("连接缺失"+cause);
}
public void deliveryComplete(IMqttDeliveryToken token) {
try {
System.out.println("deliveryComplete---------" + token.isComplete()+"mqtt"+token.getMessage());
} catch (MqttException e) {
e.printStackTrace();
}
}
public void messageArrived(String topic, MqttMessage message) throws Exception {
if(topic.endsWith("disconnected")){
logger.info("--线程(ID:" + Thread.currentThread().getId()+").设备离线,离线原因为:"+message.toString());
}else if(topic.endsWith("connected")){
logger.info("--线程(ID:" + Thread.currentThread().getId()+"). 设备上线,连接信息:"+message.toString());
}
System.out.println("订阅到的主题 : " + topic);
System.out.println("发送等级 : " + message.getQos());
System.out.println("发送的消息 : " + new String(message.getPayload()));
System.out.println("源消息"+message);
}
}
我用的依赖:
<!-- MQTT client -->
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.1.0</version>
</dependency>
效果:
服务端
客户端: