项目架构
pom
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.47</version>
</dependency>
客户端
@Slf4j
public class MQTTPublishClient {
/**
* tcp://MQTT安装的服务器地址:MQTT定义的端口号
*/
public String HOST;
/**
* 定义MQTT的ID,可以在MQTT服务配置中指定
*/
private String clientId;
private MqttClient client;
@Value("${jsjc.switch.open-mqtt}")
private Boolean openMqtt = true;
@Setter
@Getter
private MqttTopic mqttTopic;
/**
* 账号信息
*/
private String userName = "***";
/**
* 密码
*/
private String password = "****";
/**
* 构造函数
*
* @throws MqttException
*/
public MQTTPublishClient(String host, String serverId) throws MqttException {
if (openMqtt) {
HOST = host;
clientId = serverId;
try {
if (client == null) {
client = new MqttClient(HOST, clientId, new MemoryPersistence());
}
} catch (MqttException e) {
e.printStackTrace();
}
connect();
String[] topic1 = {"", "", "", "", "", ""};
int[] qos = {0, 0, 0, 0, 0, 0};
//订阅主题
client.subscribe(topic1, qos);
}
}
/**
* 用来连接服务器
*/
private void connect() {
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(false);
// 设置超时时间
options.setConnectionTimeout(300);
// 设置会话心跳时间
options.setKeepAliveInterval(300);
//设置自动重连
options.setAutomaticReconnect(true);
options.setUserName(userName);
options.setPassword(password.toCharArray());
try {
options.setSocketFactory(SslUtil.getSocketFactory("./root.pem", "./client.pem", "./client.key", ""));
client.setCallback(new PublishCallback());
client.connect(options);
} catch (Exception e) {
e.printStackTrace();
}
}
//发送消息并获取回执
public void publish(String topic, MqttMessage message) throws MqttPersistenceException, MqttException, InterruptedException {
mqttTopic = client.getTopic(topic);
MqttDeliveryToken token = mqttTopic.publish(message);
token.waitForCompletion();
token.getResponse();
}
}
订阅回调函数
@Slf4j
public class PublishCallback implements MqttCallbackExtended {
@Override
public void connectionLost(Throwable cause) {
// 连接丢失后,一般在这里面进行重连
log.info("[PublishCallback] 连接断开");
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
log.info("deliveryComplete---------" + token.isComplete());
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
try {
//业务层处理逻辑,处理订阅主题的消息体
MyService ervice = SpringUtil.getBean(MyService.class);
log.info("接收消息主题 : " + topic);
String str = message.toString();
log.info("从MQTT收到的消息为:" + str + " ;TOPIC:" + topic);
} catch (Exception e) {
log.error("PublishCallback error:" + e.getMessage());
e.printStackTrace();
}
}
@Override
public void connectComplete(boolean b, String s) {
log.info("connected");
}
}
tip
回调函数中引入业务层类,自动注解@Autowired 会使Spring容器无法加载,需手动引入bean
MyService service = SpringUtil.getBean(MyService.class);
工具类
SslUtil
public class SslUtil {
public static SSLSocketFactory getSocketFactory(final String caCrtFile, final String crtFile, final String keyFile,
final String password) throws Exception {
Security.addProvider(new BouncyCastleProvider());
// load CA certificate
PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));
X509Certificate caCert = (X509Certificate) reader.readObject();
reader.close();
// load client certificate
reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));
X509Certificate cert = (X509Certificate) reader.readObject();
reader.close();
// load client private key
reader = new PEMReader(
new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))),
new PasswordFinder() {
@Override
public char[] getPassword() {
return password.toCharArray();
}
}
);
KeyPair key = (KeyPair) reader.readObject();
reader.close();
// CA certificate is used to authenticate server
KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
caKs.load(null, null);
caKs.setCertificateEntry("ca-certificate", caCert);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(caKs);
// client key and certificates are sent to server so it can authenticate us
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry("certificate", cert);
ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, password.toCharArray());
// finally, create SSL socket factory
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return context.getSocketFactory();
}
}
SpringUtil
@Component
@Slf4j
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
/**
* 获取applicationContext
*
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过name获取 Bean
*
* @param name
* @return
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过class获取Bean
*
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz) {
T bean = getApplicationContext().getBean(clazz);
return bean;
}
/**
* 通过name,以及Clazz返回指定的Bean
* @param name
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}