├── .idea └── uiDesigner.xml ├── README.md ├── image └── icon.jpg ├── iot_push.iml ├── iot_push_client ├── iot_push_client.iml ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── lxr │ │ └── iot │ │ ├── auto │ │ ├── MqttListener.java │ │ ├── MqttMessageListener.java │ │ └── MqttProducerConfigure.java │ │ └── bootstrap │ │ ├── AbsMqttProducer.java │ │ ├── AbstractBootstrapClient.java │ │ ├── Bean │ │ ├── SendMqttMessage.java │ │ └── SubMessage.java │ │ ├── BootstrapClient.java │ │ ├── MqttApi.java │ │ ├── MqttProducer.java │ │ ├── Producer.java │ │ ├── cache │ │ └── Cache.java │ │ ├── channel │ │ └── MqttHandlerServiceService.java │ │ ├── handler │ │ └── DefaultMqttHandler.java │ │ └── scan │ │ ├── SacnScheduled.java │ │ └── ScanRunnable.java │ └── resources │ └── META-INF │ └── spring.factories ├── iot_push_client_starter_test ├── iot_push_client_starter.iml ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── lxr │ │ └── iot │ │ ├── App.java │ │ ├── SubListener.java │ │ ├── example │ │ └── MqttMain.java │ │ └── spring │ │ └── springTest.java │ └── resources │ ├── application.yml │ └── securesocket.jks ├── iot_push_common ├── iot_push_common.iml ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── lxr │ └── iot │ ├── enums │ ├── ConfirmStatus.java │ ├── ProtocolEnum.java │ ├── QosStatus.java │ ├── SessionStatus.java │ └── SubStatus.java │ ├── exception │ ├── ConnectionException.java │ └── NoFindHandlerException.java │ ├── ip │ └── IpUtils.java │ ├── mqtt │ ├── ClientMqttHandlerService.java │ ├── MqttHander.java │ ├── MqttHandlerIntf.java │ └── ServerMqttHandlerService.java │ ├── pool │ ├── DefaultThreadFactory.java │ ├── ExecutorQueue.java │ ├── Scheduled.java │ └── StandardThreadExecutor.java │ ├── properties │ ├── ConnectOptions.java │ └── InitBean.java │ ├── ssl │ ├── SecureSocketKeyStore.java │ ├── SecureSocketSslContextFactory.java │ ├── SecureSokcetTrustManagerFactory.java │ ├── StreamReader.java │ └── X509CertTool.java │ ├── util │ ├── ByteBufUtil.java │ ├── IdWorker.java │ ├── MessageId.java │ ├── RemotingUtil.java │ └── SpringBeanUtils.java │ └── zookeeper │ ├── ZkStateListener.java │ ├── ZkUtils.java │ └── testZk.java ├── iot_push_server ├── iot_push_server.iml ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── lxr │ │ └── iot │ │ ├── auto │ │ ├── InitServer.java │ │ └── ServerAutoConfigure.java │ │ └── bootstrap │ │ ├── AbstractBootstrapServer.java │ │ ├── BaseApi.java │ │ ├── BaseAuthService.java │ │ ├── BootstrapServer.java │ │ ├── ChannelService.java │ │ ├── NettyBootstrapServer.java │ │ ├── ScheduledPool.java │ │ ├── bean │ │ ├── MqttChannel.java │ │ ├── RetainMessage.java │ │ ├── SendMqttMessage.java │ │ ├── SessionMessage.java │ │ └── WillMeaasge.java │ │ ├── channel │ │ ├── AbstractChannelService.java │ │ ├── ClientSessionService.java │ │ ├── MqttChannelService.java │ │ ├── MqttHandlerService.java │ │ ├── PublishApiSevice.java │ │ ├── WillService.java │ │ └── cache │ │ │ └── CacheMap.java │ │ ├── coder │ │ ├── ByteBufToWebSocketFrameEncoder.java │ │ └── WebSocketFrameToByteBufDecoder.java │ │ ├── handler │ │ └── DefaultMqttHandler.java │ │ └── queue │ │ ├── DisruptorMessageStarter.java │ │ ├── MessageEvent.java │ │ ├── MessageHandler.java │ │ ├── MessageStarter.java │ │ └── MessageTransfer.java │ └── resources │ └── META-INF │ └── spring.factories ├── iot_push_server_starter_test ├── iot_push_server_starter.iml ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── lxr │ │ └── iot │ │ ├── DefaultAutoService.java │ │ └── ServerApplication.java │ └── resources │ ├── application.yml │ └── securesocket.jks ├── iot_push_test ├── iot_push_test.iml ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── lxr │ │ └── iot │ │ ├── client │ │ ├── MyListener.java │ │ └── main.java │ │ └── paho │ │ ├── MqttClientProducerTest.java │ │ ├── comsumer2 │ │ └── MqttClientConsumerTest.java │ │ └── consumer │ │ └── MqttClientConsumerTest1.java │ └── resources │ ├── application.yml │ └── securesocket.jks └── pom.xml /README.md: -------------------------------------------------------------------------------- 1 | # 此项目暂停维护 2 | 3 | [请使用新项目](https://github.com/quickmsg/smqtt) 4 | 5 | # iot_push 6 | 基于netty+mqtt 3.1.1协议开发的物联网消息推送框架 7 | 8 | ## 项目目录 9 | * [mqtt简介](#1) 10 | * [功能](#2) 11 | * [如何使用](#3) 12 | ## 更新日志 13 | 基于netty4.1-final+springboot实现的 Mqtt 3.1.1 物联网标准推送协议 14 | ## mqtt简介 15 | MQTT 协议是 IBM 开发的即时通讯协议,相对于 IM 的实际上的准标准协议 XMPP 来说,MQTT 更小,更快,更轻量。MQTT 适合于任何计算能力有限,工作在低带宽、不可靠的网络中的设备,包括手机,传感器等等。 16 | ## 功能 17 | 18 | **## 服务端 example(iot_push_server_starter_test)** 19 | 20 | #### 已实现: 21 | * 发布订阅功能 22 | * 遗言通知 23 | * 会话session数据 24 | * 发布保留消息 25 | * 主题过滤(/test 会接受到 /test/yy 的主题消息) 26 | * 实现标准的 qos0 qos1 qos2消息确认机制 27 | * ssl加密 28 | * 支持ws协议 29 | * 集成spring容器 30 | 31 | 32 | #### 如何使用 33 | * 安装lombok插件 34 | * 下载源码 35 | * springboot 36 | * jdk8 37 | * 导入IDE 38 | * 配置yml 或者properties 文件 [yml](https://github.com/1ssqq1lxr/iot_push/blob/master/iot_push_server_starter_test/src/main/resources/application.yml) 39 | * 简单测试:运行包 test 下的 测试 文件,即可开启测试客户端。 40 | * 压力测试:推荐使用jmeter 的mqtt插件 [插件](https://github.com/tuanhiep/mqtt-jmeter) 41 | 42 | **## 客户端 example(iot_push_client_starter_test)** 43 | 44 | * 基于springboot 配置方式[yml](https://github.com/1ssqq1lxr/iot_push/blob/master/iot_push_client_starter_test/src/main/resources/application.yml) 45 | 46 | * 配置实现 MqttListener 类并添加MqttMessageListener指定订阅的topic跟服务质量 47 | 48 | * @Autowired Procuder producer 即可使用; 49 | 50 | * 编码 [java](https://github.com/1ssqq1lxr/iot_push/blob/master/iot_push_client_starter_test/src/main/java/com/lxr/iot/example/MqttMain.java) 51 | 52 | qq群号: 789331252 53 | ### 关注公众号,输入 `物联网` 扫码加入交流群 54 | ![image](image/icon.jpg) 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /image/icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ssqq1lxr/iot_push/f65d6ef642c7fb8da329c01957d2adab7765fdde/image/icon.jpg -------------------------------------------------------------------------------- /iot_push.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /iot_push_client/iot_push_client.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /iot_push_client/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | iot_push 5 | com.lxr.iot 6 | 1.0-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | iot_push_client 11 | jar 12 | 13 | iot_push_client 14 | http://maven.apache.org 15 | 16 | 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | com.lxr.iot 24 | iot_push_common 25 | 1.0-SNAPSHOT 26 | 27 | 28 | junit 29 | junit 30 | 3.8.1 31 | test 32 | 33 | 34 | 35 | 36 | 37 | org.apache.maven.plugins 38 | maven-compiler-plugin 39 | 3.3 40 | 41 | 1.8 42 | 1.8 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/auto/MqttListener.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.auto; 2 | 3 | /** 4 | * call scan 5 | * @author lxr 6 | * @create 2018-01-04 18:42 7 | **/ 8 | public interface MqttListener{ 9 | 10 | void callBack(String topic,String msg); 11 | 12 | void callThrowable(Throwable e); 13 | } 14 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/auto/MqttMessageListener.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.auto; 2 | 3 | import io.netty.handler.codec.mqtt.MqttQoS; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * Created by rh on 2017/11/17. 9 | * 消费者配置注解类 10 | */ 11 | @Target(ElementType.TYPE) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Documented 14 | public @interface MqttMessageListener { 15 | 16 | String[] topic() ; 17 | 18 | MqttQoS qos() default MqttQoS.AT_MOST_ONCE; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/auto/MqttProducerConfigure.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.auto; 2 | 3 | import com.lxr.iot.bootstrap.MqttProducer; 4 | import com.lxr.iot.bootstrap.Producer; 5 | import com.lxr.iot.bootstrap.Bean.SubMessage; 6 | import com.lxr.iot.properties.ConnectOptions; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.springframework.aop.support.AopUtils; 9 | import org.springframework.beans.BeansException; 10 | import org.springframework.beans.factory.DisposableBean; 11 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 12 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 13 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 14 | import org.springframework.context.ApplicationContext; 15 | import org.springframework.context.ApplicationContextAware; 16 | import org.springframework.context.ConfigurableApplicationContext; 17 | import org.springframework.context.annotation.Bean; 18 | import org.springframework.context.annotation.Configuration; 19 | import org.springframework.core.env.Environment; 20 | 21 | import java.util.Arrays; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.Optional; 25 | import java.util.stream.Collectors; 26 | 27 | /** 28 | * 自动配置类 29 | * 30 | * @author lxr 31 | * @create 2018-01-04 20:50 32 | **/ 33 | @Configuration 34 | @ConditionalOnClass({MqttProducer.class}) 35 | @EnableConfigurationProperties({ConnectOptions.class}) 36 | public class MqttProducerConfigure implements ApplicationContextAware,DisposableBean { 37 | 38 | private static final int _BLACKLOG = 1024; 39 | 40 | private static final int CPU =Runtime.getRuntime().availableProcessors(); 41 | 42 | private static final int SEDU_DAY =10; 43 | 44 | private static final int TIMEOUT =120; 45 | 46 | private static final int BUF_SIZE=10*1024*1024; 47 | 48 | private ConfigurableApplicationContext applicationContext; 49 | 50 | 51 | @Bean 52 | @ConditionalOnMissingBean() 53 | public Producer initServer(ConnectOptions connectOptions, Environment env){ 54 | MqttProducer mqttProducer = new MqttProducer(); 55 | Map beansWithAnnotation = this.applicationContext.getBeansWithAnnotation(MqttMessageListener.class); 56 | checkArgs(connectOptions); 57 | Optional.of(beansWithAnnotation).ifPresent((Map mqttListener) -> { 58 | beansWithAnnotation.forEach((name, bean) -> { 59 | Class clazz = AopUtils.getTargetClass(bean); 60 | if (!MqttListener.class.isAssignableFrom(bean.getClass())) { 61 | throw new IllegalStateException(clazz + " is not instance of " + MqttListener.class.getName()); 62 | } 63 | MqttMessageListener annotation = clazz.getAnnotation(MqttMessageListener.class); 64 | String[] topics = annotation.topic(); 65 | MqttListener listener = (MqttListener) bean; 66 | mqttProducer.setMqttListener(listener); 67 | mqttProducer.connect(connectOptions); 68 | if(StringUtils.isNoneBlank(topics)){ 69 | SubMessage[] SubMessages = new SubMessage[topics.length]; 70 | List collect = Arrays.stream(topics) 71 | .map(topic -> SubMessage.builder() 72 | .qos(annotation.qos()) 73 | .topic(topic) 74 | .build()).collect(Collectors.toList()); 75 | mqttProducer.sub(collect.toArray(SubMessages)); 76 | } 77 | }); 78 | }); 79 | return mqttProducer; 80 | } 81 | private void checkArgs(ConnectOptions connectOptions) { 82 | if(connectOptions.getServerIp()==null) 83 | throw new RuntimeException("ip地址为空"); 84 | if(connectOptions.getPort()<1) 85 | throw new RuntimeException("端口号为空"); 86 | if(connectOptions.getBacklog()<1) 87 | connectOptions.setBacklog(_BLACKLOG); 88 | if(connectOptions.getBossThread()<1) 89 | connectOptions.setBossThread(CPU); 90 | if (connectOptions.getConnectTime()<1) 91 | connectOptions.setConnectTime(10); 92 | if (connectOptions.getHeart()<1) 93 | connectOptions.setConnectTime(120); 94 | if(connectOptions.getMinPeriod()<1) 95 | connectOptions.setMinPeriod(10); 96 | if(connectOptions.getRevbuf()<1) 97 | connectOptions.setRevbuf(BUF_SIZE); 98 | if(connectOptions.getSndbuf()<1) 99 | connectOptions.setSndbuf(BUF_SIZE); 100 | ConnectOptions.MqttOpntions mqtt=connectOptions.getMqtt(); 101 | if(mqtt!=null){ 102 | if(mqtt.getClientIdentifier()==null) 103 | throw new RuntimeException("设备号为空"); 104 | if(mqtt.getKeepAliveTime()<1) 105 | mqtt.setKeepAliveTime(100); 106 | if (mqtt.isHasUserName()&&mqtt.getUserName()==null) 107 | throw new RuntimeException("未设置用户"); 108 | if (mqtt.isHasPassword()&&mqtt.getPassword()==null) 109 | throw new RuntimeException("未设置密码"); 110 | if(!mqtt.isHasWillFlag()){ 111 | mqtt.setHasWillRetain(false); 112 | mqtt.setWillQos(0); 113 | mqtt.setWillMessage(null); 114 | mqtt.setWillTopic(null); 115 | } 116 | } 117 | } 118 | 119 | @Override 120 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 121 | this.applicationContext = (ConfigurableApplicationContext) applicationContext; 122 | } 123 | 124 | @Override 125 | public void destroy() throws Exception { 126 | Producer bean = applicationContext.getBean(Producer.class); 127 | if(bean!=null){ 128 | bean.close(); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/bootstrap/AbstractBootstrapClient.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap; 2 | 3 | import com.lxr.iot.mqtt.MqttHander; 4 | import com.lxr.iot.properties.ConnectOptions; 5 | import com.lxr.iot.ssl.SecureSokcetTrustManagerFactory; 6 | import io.netty.channel.ChannelPipeline; 7 | import io.netty.handler.codec.mqtt.MqttDecoder; 8 | import io.netty.handler.codec.mqtt.MqttEncoder; 9 | import io.netty.handler.ssl.SslHandler; 10 | import io.netty.handler.timeout.IdleStateHandler; 11 | 12 | import javax.net.ssl.SSLContext; 13 | import javax.net.ssl.SSLEngine; 14 | 15 | /** 16 | * 抽象类 17 | * 18 | * @author lxr 19 | * @create 2017-12-21 15:56 20 | **/ 21 | public abstract class AbstractBootstrapClient implements BootstrapClient { 22 | 23 | 24 | private SSLContext CLIENT_CONTEXT; 25 | 26 | private String PROTOCOL = "TLS"; 27 | 28 | /** 29 | * @param channelPipeline channelPipeline 30 | * @param clientBean 客户端配置参数 31 | */ 32 | protected void initHandler(ChannelPipeline channelPipeline, ConnectOptions clientBean, MqttHander mqttHander){ 33 | if(clientBean.isSsl()){ 34 | initSsl(); 35 | SSLEngine engine = 36 | CLIENT_CONTEXT.createSSLEngine(); 37 | engine.setUseClientMode(true); 38 | channelPipeline.addLast("ssl", new SslHandler(engine)); 39 | } 40 | channelPipeline.addLast("decoder", new MqttDecoder()); 41 | channelPipeline.addLast("encoder", MqttEncoder.INSTANCE); 42 | channelPipeline.addLast(new IdleStateHandler(clientBean.getHeart(),0,0)); 43 | channelPipeline.addLast(mqttHander); 44 | 45 | } 46 | 47 | private void initSsl(){ 48 | SSLContext clientContext; 49 | try { 50 | clientContext = SSLContext.getInstance(PROTOCOL); 51 | clientContext.init(null, SecureSokcetTrustManagerFactory.getTrustManagers(), null); 52 | } catch (Exception e) { 53 | throw new Error( 54 | "Failed to initialize the client-side SSLContext", e); 55 | } 56 | CLIENT_CONTEXT = clientContext; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/bootstrap/Bean/SendMqttMessage.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.Bean; 2 | 3 | import com.lxr.iot.enums.ConfirmStatus; 4 | import com.lxr.iot.enums.QosStatus; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | 8 | /** 9 | * 消息 10 | * 11 | * @author lxr 12 | * @create 2018-01-04 19:36 13 | **/ 14 | @Data 15 | @Builder 16 | public class SendMqttMessage { 17 | 18 | private String Topic; 19 | 20 | private byte[] payload; 21 | 22 | private int qos; 23 | 24 | private boolean retained; 25 | 26 | private boolean dup; 27 | 28 | private int messageId; 29 | 30 | 31 | private long timestamp; 32 | 33 | private volatile ConfirmStatus confirmStatus; 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/bootstrap/Bean/SubMessage.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.Bean; 2 | 3 | import io.netty.handler.codec.mqtt.MqttQoS; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | /** 8 | * 订阅消息 9 | * 10 | * @author lxr 11 | * @create 2018-01-04 19:42 12 | **/ 13 | @Builder 14 | @Data 15 | public class SubMessage { 16 | 17 | private String topic; 18 | 19 | private MqttQoS qos; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/bootstrap/BootstrapClient.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap; 2 | 3 | 4 | import io.netty.channel.Channel; 5 | 6 | /** 7 | * 启动类接口 8 | * 9 | * @author lxr 10 | * @create 2017-11-18 14:05 11 | **/ 12 | public interface BootstrapClient { 13 | 14 | 15 | void shutdown(); 16 | 17 | void initEventPool(); 18 | 19 | Channel start(); 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/bootstrap/MqttApi.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap; 2 | 3 | import com.lxr.iot.bootstrap.Bean.SendMqttMessage; 4 | import com.lxr.iot.util.MessageId; 5 | import io.netty.buffer.Unpooled; 6 | import io.netty.channel.Channel; 7 | import io.netty.handler.codec.mqtt.*; 8 | import io.netty.util.AttributeKey; 9 | import lombok.extern.slf4j.Slf4j; 10 | 11 | import java.util.List; 12 | import java.util.concurrent.Executors; 13 | import java.util.concurrent.ScheduledExecutorService; 14 | import java.util.concurrent.ScheduledFuture; 15 | 16 | /** 17 | * 操作api 处理主动发送请求 18 | * 19 | * @author lxr 20 | * @create 2018-01-10 9:36 21 | **/ 22 | @Slf4j 23 | public class MqttApi { 24 | 25 | 26 | 27 | 28 | protected ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); 29 | 30 | 31 | protected void pubMessage(Channel channel, SendMqttMessage mqttMessage){ 32 | log.info("成功发送消息:"+new String(mqttMessage.getPayload())); 33 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH,mqttMessage.isDup(), MqttQoS.valueOf(mqttMessage.getQos()),mqttMessage.isRetained(),0); 34 | MqttPublishVariableHeader mqttPublishVariableHeader = new MqttPublishVariableHeader(mqttMessage.getTopic(),mqttMessage.getMessageId()); 35 | MqttPublishMessage mqttPublishMessage = new MqttPublishMessage(mqttFixedHeader,mqttPublishVariableHeader, Unpooled.wrappedBuffer(mqttMessage.getPayload())); 36 | channel.writeAndFlush(mqttPublishMessage); 37 | } 38 | 39 | protected void subMessage(Channel channel, List mqttTopicSubscriptions, int messageId){ 40 | MqttSubscribePayload mqttSubscribePayload = new MqttSubscribePayload(mqttTopicSubscriptions); 41 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.SUBSCRIBE,false, MqttQoS.AT_LEAST_ONCE,false,0); 42 | MqttMessageIdVariableHeader mqttMessageIdVariableHeader =MqttMessageIdVariableHeader.from(messageId); 43 | MqttSubscribeMessage mqttSubscribeMessage = new MqttSubscribeMessage(mqttFixedHeader,mqttMessageIdVariableHeader,mqttSubscribePayload); 44 | channel.writeAndFlush(mqttSubscribeMessage); 45 | 46 | } 47 | 48 | protected void sendAck(MqttMessageType type,boolean isDup,Channel channel, int messageId){ 49 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(type,isDup, MqttQoS.AT_LEAST_ONCE,false,0x02); 50 | MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(messageId); 51 | MqttPubAckMessage mqttPubAckMessage = new MqttPubAckMessage(mqttFixedHeader,from); 52 | channel.writeAndFlush(mqttPubAckMessage); 53 | } 54 | 55 | protected void pubRecMessage(Channel channel,int messageId) { 56 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBREC,false, MqttQoS.AT_LEAST_ONCE,false,0x02); 57 | MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(messageId); 58 | MqttMessage mqttPubAckMessage = new MqttMessage(mqttFixedHeader,from); 59 | channel.writeAndFlush(mqttPubAckMessage); 60 | } 61 | 62 | protected void unSubMessage(Channel channel,List topic,int messageId){ 63 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE,false, MqttQoS.AT_LEAST_ONCE,false,0x02); 64 | MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(messageId); 65 | MqttUnsubscribePayload MqttUnsubscribeMessage = new MqttUnsubscribePayload(topic); 66 | MqttUnsubscribeMessage mqttUnsubscribeMessage = new MqttUnsubscribeMessage(mqttFixedHeader,variableHeader,MqttUnsubscribeMessage); 67 | channel.writeAndFlush(mqttUnsubscribeMessage); 68 | } 69 | 70 | protected void sendDisConnect(Channel channel){ 71 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.DISCONNECT,false, MqttQoS.AT_LEAST_ONCE,false,0x02); 72 | MqttMessage mqttMessage = new MqttMessage(mqttFixedHeader); 73 | channel.writeAndFlush(mqttMessage); 74 | } 75 | 76 | protected AttributeKey> getKey(String id){ 77 | return AttributeKey.valueOf(id); 78 | } 79 | 80 | 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/bootstrap/MqttProducer.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap; 2 | 3 | import com.lxr.iot.bootstrap.Bean.SendMqttMessage; 4 | import com.lxr.iot.bootstrap.Bean.SubMessage; 5 | import com.lxr.iot.enums.ConfirmStatus; 6 | import com.lxr.iot.properties.ConnectOptions; 7 | import com.lxr.iot.util.MessageId; 8 | import io.netty.handler.codec.mqtt.MqttTopicSubscription; 9 | import lombok.extern.slf4j.Slf4j; 10 | 11 | import java.io.UnsupportedEncodingException; 12 | import java.util.LinkedList; 13 | import java.util.List; 14 | import java.util.Optional; 15 | import java.util.stream.Collectors; 16 | 17 | 18 | /** 19 | * mqtt api操作类 20 | * 21 | * @author lxr 22 | * @create 2018-01-04 15:10 23 | **/ 24 | @Slf4j 25 | public class MqttProducer extends AbsMqttProducer{ 26 | 27 | 28 | public Producer connect(ConnectOptions connectOptions){ 29 | connectTo(connectOptions); 30 | return this; 31 | } 32 | 33 | 34 | @Override 35 | public void pub(String topic,String message,int qos){ 36 | pub(topic,message,false,qos); 37 | } 38 | 39 | @Override 40 | public void pub(String topic, String message, boolean retained) { 41 | pub(topic,message,retained,0); 42 | } 43 | @Override 44 | public void pub(String topic,String message){ 45 | pub(topic,message,false,0); 46 | } 47 | 48 | 49 | @Override 50 | public void pub(String topic, String message, boolean retained, int qos) { 51 | Optional.ofNullable(buildMqttMessage(topic, message, retained, qos, false, true)).ifPresent(sendMqttMessage -> { 52 | pubMessage(channel, sendMqttMessage); 53 | }); 54 | } 55 | 56 | private SendMqttMessage buildMqttMessage(String topic, String message, boolean retained, int qos, boolean dup, boolean time) { 57 | int messageId=0; 58 | if(qos!=0){ 59 | messageId=MessageId.messageId(); 60 | } 61 | try { 62 | return SendMqttMessage.builder().messageId(messageId) 63 | .Topic(topic) 64 | .dup(dup) 65 | .retained(retained) 66 | .qos(qos) 67 | .confirmStatus(ConfirmStatus.PUB) 68 | .timestamp(System.currentTimeMillis()) 69 | .payload(message.getBytes("Utf-8")).build(); 70 | } catch (UnsupportedEncodingException e) { 71 | e.printStackTrace(); 72 | } 73 | return null; 74 | } 75 | 76 | @Override 77 | public void sub(SubMessage... subMessages){ 78 | Optional.ofNullable(getSubTopics(subMessages)).ifPresent(mqttTopicSubscriptions -> { 79 | int messageId = MessageId.messageId(); 80 | subMessage(channel, mqttTopicSubscriptions,messageId); 81 | topics.addAll(mqttTopicSubscriptions); 82 | }); 83 | } 84 | 85 | @Override 86 | public void unsub(List topics) { 87 | Optional.ofNullable(topics).ifPresent(strings -> { 88 | int messageId = MessageId.messageId(); 89 | super.unsub(strings,messageId); 90 | }); 91 | } 92 | 93 | @Override 94 | public void unsub(){ 95 | unsub(toList()); 96 | } 97 | 98 | 99 | 100 | private List getSubTopics(SubMessage[]subMessages ) { 101 | return Optional.ofNullable(subMessages) 102 | .map(subMessages1 -> { 103 | List mqttTopicSubscriptions = new LinkedList<>(); 104 | for(SubMessage sb :subMessages1){ 105 | MqttTopicSubscription mqttTopicSubscription = new MqttTopicSubscription(sb.getTopic(),sb.getQos()); 106 | mqttTopicSubscriptions.add(mqttTopicSubscription); 107 | } 108 | return mqttTopicSubscriptions; 109 | }).orElse(null); 110 | } 111 | 112 | private List toList(){ 113 | return Optional.ofNullable(topics). 114 | map(mqttTopicSubscriptions -> 115 | mqttTopicSubscriptions.stream(). 116 | map(mqttTopicSubscription -> mqttTopicSubscription.topicName()).collect(Collectors.toList())) 117 | .orElse(null); 118 | } 119 | 120 | 121 | private List getTopics(SubMessage[] subMessages) { 122 | return Optional.ofNullable(subMessages) 123 | .map(subMessages1 -> { 124 | List mqttTopicSubscriptions = new LinkedList<>(); 125 | for(SubMessage sb :subMessages1){ 126 | mqttTopicSubscriptions.add(sb.getTopic()); 127 | } 128 | return mqttTopicSubscriptions; 129 | }).orElse(null); 130 | } 131 | 132 | 133 | 134 | 135 | } 136 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/bootstrap/Producer.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap; 2 | 3 | import com.lxr.iot.auto.MqttListener; 4 | import com.lxr.iot.bootstrap.Bean.SubMessage; 5 | import com.lxr.iot.properties.ConnectOptions; 6 | import io.netty.channel.Channel; 7 | import io.netty.handler.codec.mqtt.MqttConnAckMessage; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 生产者 13 | * 14 | * @author lxr 15 | * @create 2018-01-04 17:17 16 | **/ 17 | public interface Producer { 18 | 19 | Channel getChannel(); 20 | 21 | Producer connect(ConnectOptions connectOptions); 22 | 23 | void close(); 24 | 25 | void setMqttListener(MqttListener mqttListener); 26 | 27 | void pub(String topic,String message,boolean retained,int qos); 28 | 29 | void pub(String topic,String message); 30 | 31 | void pub(String topic,String message,int qos); 32 | 33 | void pub(String topic,String message,boolean retained); 34 | 35 | void sub(SubMessage... subMessages); 36 | 37 | void unsub(List topics); 38 | 39 | void unsub(); 40 | 41 | void disConnect(); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/bootstrap/cache/Cache.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.cache; 2 | 3 | import com.lxr.iot.bootstrap.Bean.SendMqttMessage; 4 | 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | /** 8 | * 缓存 9 | * 10 | * @author lxr 11 | * @create 2018-01-04 20:15 12 | **/ 13 | public class Cache { 14 | 15 | private static ConcurrentHashMap message = new ConcurrentHashMap<>(); 16 | 17 | 18 | public static boolean put(Integer messageId,SendMqttMessage mqttMessage){ 19 | 20 | return message.put(messageId,mqttMessage)==null; 21 | 22 | } 23 | 24 | public static SendMqttMessage get(Integer messageId){ 25 | 26 | return message.get(messageId); 27 | 28 | } 29 | 30 | 31 | public static SendMqttMessage del(Integer messageId){ 32 | return message.remove(messageId); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/bootstrap/channel/MqttHandlerServiceService.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.channel; 2 | 3 | import com.lxr.iot.bootstrap.cache.Cache; 4 | import com.lxr.iot.enums.ConfirmStatus; 5 | import com.lxr.iot.mqtt.ClientMqttHandlerService; 6 | import io.netty.channel.Channel; 7 | import io.netty.handler.codec.mqtt.*; 8 | import io.netty.handler.timeout.IdleStateEvent; 9 | import io.netty.util.AttributeKey; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | import java.util.Optional; 13 | import java.util.concurrent.ScheduledFuture; 14 | 15 | /** 16 | * 客户端channelService 处理收到消息信息 17 | * 18 | * @author lxr 19 | * @create 2018-01-02 20:48 20 | **/ 21 | @Slf4j 22 | public class MqttHandlerServiceService extends ClientMqttHandlerService{ 23 | 24 | @Override 25 | public void close(Channel channel) {} 26 | 27 | @Override 28 | public void puback(Channel channel, MqttMessage mqttMessage) { 29 | MqttMessageIdVariableHeader messageIdVariableHeader = (MqttMessageIdVariableHeader) mqttMessage.variableHeader(); 30 | int messageId = messageIdVariableHeader.messageId(); 31 | Optional.ofNullable(Cache.del(messageId)).ifPresent(sendMqttMessage -> { 32 | sendMqttMessage.setConfirmStatus(ConfirmStatus.COMPLETE); 33 | }); 34 | } 35 | 36 | @Override 37 | public void pubrec(Channel channel, MqttMessage mqttMessage ) { 38 | MqttMessageIdVariableHeader messageIdVariableHeader = (MqttMessageIdVariableHeader) mqttMessage.variableHeader(); 39 | int messageId = messageIdVariableHeader.messageId(); 40 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBREL,false, MqttQoS.AT_LEAST_ONCE,false,0x02); 41 | MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(messageId); 42 | MqttMessage mqttPubAckMessage = new MqttMessage(mqttFixedHeader,from); 43 | Optional.ofNullable(Cache.get(messageId)).ifPresent(sendMqttMessage -> { 44 | sendMqttMessage.setTimestamp(System.currentTimeMillis()); 45 | sendMqttMessage.setConfirmStatus(ConfirmStatus.PUBREL); 46 | }); 47 | channel.writeAndFlush(mqttPubAckMessage); 48 | } 49 | 50 | @Override 51 | public void pubrel(Channel channel, MqttMessage mqttMessage ) { 52 | MqttMessageIdVariableHeader messageIdVariableHeader = (MqttMessageIdVariableHeader) mqttMessage.variableHeader(); 53 | int messageId = messageIdVariableHeader.messageId(); 54 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBCOMP,false, MqttQoS.AT_MOST_ONCE,false,0x02); 55 | MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(messageId); 56 | MqttMessage mqttPubAckMessage = new MqttMessage(mqttFixedHeader,from); 57 | Optional.ofNullable(Cache.del(messageId)).ifPresent(sendMqttMessage -> { 58 | sendMqttMessage.setConfirmStatus(ConfirmStatus.COMPLETE); 59 | }); 60 | channel.writeAndFlush(mqttPubAckMessage); 61 | } 62 | 63 | @Override 64 | public void pubcomp(Channel channel,MqttMessage mqttMessage ) { 65 | MqttMessageIdVariableHeader messageIdVariableHeader = (MqttMessageIdVariableHeader) mqttMessage.variableHeader(); 66 | int messageId = messageIdVariableHeader.messageId(); 67 | Optional.ofNullable(Cache.del(messageId)).ifPresent(sendMqttMessage -> { 68 | sendMqttMessage.setConfirmStatus(ConfirmStatus.COMPLETE); 69 | }); 70 | } 71 | 72 | @Override 73 | public void heart(Channel channel, IdleStateEvent evt) { 74 | MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PINGREQ, false, MqttQoS.AT_MOST_ONCE, false, 0); 75 | MqttMessage mqttMessage = new MqttMessage(fixedHeader); 76 | log.info("发送心跳"); 77 | channel.writeAndFlush(mqttMessage); 78 | } 79 | 80 | public void suback(Channel channel,MqttSubAckMessage mqttMessage) { 81 | ScheduledFuture scheduledFuture = channel.attr(getKey(Integer.toString(mqttMessage.variableHeader().messageId()))).get(); 82 | if(scheduledFuture!=null){ 83 | scheduledFuture.cancel(true); 84 | } 85 | } 86 | //qos1 send 87 | @Override 88 | public void pubBackMessage(Channel channel, int messageId) { 89 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBACK,false, MqttQoS.AT_LEAST_ONCE,false,0x02); 90 | MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(messageId); 91 | MqttPubAckMessage mqttPubAckMessage = new MqttPubAckMessage(mqttFixedHeader,from); 92 | channel.writeAndFlush(mqttPubAckMessage); 93 | } 94 | 95 | @Override 96 | public void unsubBack(Channel channel, MqttMessage mqttMessage) { 97 | int messageId; 98 | if(mqttMessage instanceof MqttUnsubAckMessage){ 99 | MqttUnsubAckMessage mqttUnsubAckMessage = (MqttUnsubAckMessage)mqttMessage; 100 | messageId= mqttUnsubAckMessage.variableHeader().messageId(); 101 | } 102 | else { 103 | MqttMessageIdVariableHeader o =(MqttMessageIdVariableHeader) mqttMessage.variableHeader(); 104 | messageId= o.messageId(); 105 | } 106 | if(messageId>0){ 107 | ScheduledFuture scheduledFuture = channel.attr(getKey(Integer.toString(messageId))).get(); 108 | if(!scheduledFuture.isCancelled()){ 109 | scheduledFuture.cancel(true); 110 | } 111 | } 112 | } 113 | 114 | private AttributeKey> getKey(String id){ 115 | return AttributeKey.valueOf(id); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/bootstrap/handler/DefaultMqttHandler.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.handler; 2 | 3 | import com.lxr.iot.auto.MqttListener; 4 | import com.lxr.iot.bootstrap.MqttProducer; 5 | import com.lxr.iot.bootstrap.Producer; 6 | import com.lxr.iot.mqtt.ClientMqttHandlerService; 7 | import com.lxr.iot.mqtt.MqttHander; 8 | import com.lxr.iot.properties.ConnectOptions; 9 | import com.lxr.iot.util.ByteBufUtil; 10 | import io.netty.buffer.ByteBuf; 11 | import io.netty.channel.Channel; 12 | import io.netty.channel.ChannelHandler; 13 | import io.netty.channel.ChannelHandlerContext; 14 | import io.netty.handler.codec.mqtt.*; 15 | import lombok.extern.slf4j.Slf4j; 16 | 17 | /** 18 | * 默认 mqtthandler处理 19 | * 20 | * @author lxr 21 | * @create 2017-11-20 13:58 22 | **/ 23 | 24 | @ChannelHandler.Sharable 25 | @Slf4j 26 | public class DefaultMqttHandler extends MqttHander { 27 | 28 | 29 | private ClientMqttHandlerService mqttHandlerApi; 30 | 31 | private MqttProducer mqttProducer; 32 | 33 | private MqttListener mqttListener; 34 | 35 | private ConnectOptions connectOptions; 36 | 37 | public DefaultMqttHandler(ConnectOptions connectOptions, ClientMqttHandlerService mqttHandlerApi, Producer producer, MqttListener mqttListener) { 38 | super(mqttHandlerApi); 39 | this.mqttProducer =(MqttProducer) producer; 40 | this.mqttListener = mqttListener; 41 | this.mqttHandlerApi=mqttHandlerApi; 42 | this.connectOptions=connectOptions; 43 | } 44 | 45 | 46 | 47 | @Override 48 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 49 | Channel channel = ctx.channel(); 50 | ConnectOptions.MqttOpntions mqtt = connectOptions.getMqtt(); 51 | log.info("【DefaultMqttHandler:channelActive】"+ctx.channel().localAddress().toString()+"启动成功"); 52 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.CONNECT,false, MqttQoS.AT_LEAST_ONCE,false,10); 53 | MqttConnectVariableHeader mqttConnectVariableHeader = new MqttConnectVariableHeader(MqttVersion.MQTT_3_1_1.protocolName(),MqttVersion.MQTT_3_1_1.protocolLevel(),mqtt.isHasUserName(),mqtt.isHasPassword(),mqtt.isHasWillRetain(),mqtt.getWillQos(),mqtt.isHasWillFlag(),mqtt.isHasCleanSession(),mqtt.getKeepAliveTime()); 54 | MqttConnectPayload mqttConnectPayload = new MqttConnectPayload(mqtt.getClientIdentifier(),mqtt.getWillTopic(),mqtt.getWillMessage(),mqtt.getUserName(),mqtt.getPassword()); 55 | MqttConnectMessage mqttSubscribeMessage = new MqttConnectMessage(mqttFixedHeader,mqttConnectVariableHeader,mqttConnectPayload); 56 | channel.writeAndFlush(mqttSubscribeMessage); 57 | } 58 | 59 | 60 | 61 | @Override 62 | public void doMessage(ChannelHandlerContext channelHandlerContext, MqttMessage mqttMessage) { 63 | MqttFixedHeader mqttFixedHeader = mqttMessage.fixedHeader(); 64 | switch (mqttFixedHeader.messageType()){ 65 | case UNSUBACK: 66 | mqttHandlerApi.unsubBack(channelHandlerContext.channel(),mqttMessage); 67 | break; 68 | case CONNACK: 69 | mqttProducer.connectBack((MqttConnAckMessage) mqttMessage); 70 | break; 71 | case PUBLISH: 72 | publish(channelHandlerContext.channel(),(MqttPublishMessage)mqttMessage); 73 | break; 74 | case PUBACK: // qos 1回复确认 75 | mqttHandlerApi.puback(channelHandlerContext.channel(),mqttMessage); 76 | break; 77 | case PUBREC: // 78 | mqttHandlerApi.pubrec(channelHandlerContext.channel(),mqttMessage); 79 | break; 80 | case PUBREL: // 81 | mqttHandlerApi.pubrel(channelHandlerContext.channel(),mqttMessage); 82 | break; 83 | case PUBCOMP: // 84 | mqttHandlerApi.pubcomp(channelHandlerContext.channel(),mqttMessage); 85 | break; 86 | case SUBACK: 87 | mqttHandlerApi.suback(channelHandlerContext.channel(),(MqttSubAckMessage)mqttMessage); 88 | break; 89 | default: 90 | break; 91 | } 92 | } 93 | 94 | private void publish(Channel channel,MqttPublishMessage mqttMessage) { 95 | MqttFixedHeader mqttFixedHeader = mqttMessage.fixedHeader(); 96 | MqttPublishVariableHeader mqttPublishVariableHeader = mqttMessage.variableHeader(); 97 | ByteBuf payload = mqttMessage.payload(); 98 | byte[] bytes = ByteBufUtil.copyByteBuf(payload); // 99 | if(mqttListener!=null){ 100 | mqttListener.callBack(mqttPublishVariableHeader.topicName(),new String(bytes)); 101 | } 102 | switch (mqttFixedHeader.qosLevel()){ 103 | case AT_MOST_ONCE: 104 | break; 105 | case AT_LEAST_ONCE: 106 | mqttHandlerApi.pubBackMessage(channel,mqttPublishVariableHeader.messageId()); 107 | break; 108 | case EXACTLY_ONCE: 109 | mqttProducer.pubRecMessage(channel,mqttPublishVariableHeader.messageId()); 110 | break; 111 | } 112 | 113 | } 114 | 115 | @Override 116 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 117 | // mqttProducer.getNettyBootstrapClient().doubleConnect(); 118 | if(mqttListener!=null){ 119 | mqttListener.callThrowable(cause); 120 | } 121 | } 122 | 123 | 124 | @Override 125 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 126 | mqttProducer.getNettyBootstrapClient().doubleConnect(); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/bootstrap/scan/SacnScheduled.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.scan; 2 | 3 | import com.lxr.iot.bootstrap.Bean.SendMqttMessage; 4 | import com.lxr.iot.bootstrap.Producer; 5 | import com.lxr.iot.pool.Scheduled; 6 | import io.netty.handler.codec.mqtt.MqttMessageType; 7 | import lombok.Data; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | import java.util.concurrent.Executors; 11 | import java.util.concurrent.ScheduledExecutorService; 12 | import java.util.concurrent.ScheduledFuture; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | /** 16 | * 扫描消息确认 17 | * 18 | * @author lxr 19 | * @create 2018-01-08 19:22 20 | **/ 21 | @Data 22 | @Slf4j 23 | public class SacnScheduled extends ScanRunnable { 24 | 25 | private Producer producer; 26 | 27 | private ScheduledFuture submit; 28 | 29 | private int seconds; 30 | 31 | public SacnScheduled(Producer producer,int seconds) { 32 | this.producer=producer; 33 | this.seconds=seconds; 34 | } 35 | 36 | public void start(){ 37 | Scheduled scheduled = new ScheduledPool(); 38 | this.submit = scheduled.submit(this); 39 | } 40 | 41 | public void close(){ 42 | if(submit!=null && !submit.isCancelled()){ 43 | submit.cancel(true); 44 | } 45 | } 46 | 47 | 48 | @Override 49 | public void doInfo(SendMqttMessage poll) { 50 | if(producer.getChannel().isActive()){ 51 | if(checkTime(poll)){ 52 | poll.setTimestamp(System.currentTimeMillis()); 53 | switch (poll.getConfirmStatus()){ 54 | case PUB: 55 | poll.setDup(true); 56 | pubMessage(producer.getChannel(),poll); 57 | break; 58 | case PUBREC: 59 | sendAck(MqttMessageType.PUBREC,true,producer.getChannel(),poll.getMessageId()); 60 | break; 61 | case PUBREL: 62 | sendAck(MqttMessageType.PUBREL,true,producer.getChannel(),poll.getMessageId()); 63 | break; 64 | } 65 | 66 | } 67 | } 68 | else 69 | { 70 | log.info("channel is not alived"); 71 | submit.cancel(true); 72 | } 73 | } 74 | 75 | private boolean checkTime(SendMqttMessage poll) { 76 | return System.currentTimeMillis()-poll.getTimestamp()>=seconds*1000; 77 | } 78 | 79 | private class ScheduledPool implements Scheduled { 80 | private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); 81 | public ScheduledFuture submit(Runnable runnable){ 82 | return scheduledExecutorService.scheduleAtFixedRate(runnable,2,2, TimeUnit.SECONDS); 83 | } 84 | 85 | 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /iot_push_client/src/main/java/com/lxr/iot/bootstrap/scan/ScanRunnable.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.scan; 2 | 3 | import com.lxr.iot.bootstrap.Bean.SendMqttMessage; 4 | import com.lxr.iot.bootstrap.MqttApi; 5 | import com.lxr.iot.enums.ConfirmStatus; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | import java.util.concurrent.ConcurrentLinkedQueue; 11 | 12 | /** 13 | * 扫描未确认信息 14 | * 15 | * @author lxr 16 | * @create 2018-01-06 16:50 17 | **/ 18 | 19 | @Slf4j 20 | public abstract class ScanRunnable extends MqttApi implements Runnable { 21 | 22 | 23 | 24 | private ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); 25 | 26 | 27 | public boolean addQueue(SendMqttMessage t){ 28 | return queue.add(t); 29 | } 30 | 31 | public boolean addQueues(List ts){ 32 | return queue.addAll(ts); 33 | } 34 | 35 | 36 | 37 | @Override 38 | public void run() { 39 | if(!queue.isEmpty()) { 40 | SendMqttMessage poll; 41 | List list = new LinkedList<>(); 42 | for (; (poll = queue.poll()) != null; ) { 43 | if (poll.getConfirmStatus() != ConfirmStatus.COMPLETE) { 44 | list.add(poll); 45 | doInfo(poll); 46 | } 47 | break; 48 | } 49 | addQueues(list); 50 | } 51 | } 52 | public abstract void doInfo( SendMqttMessage poll); 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /iot_push_client/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.lxr.iot.auto.MqttProducerConfigure -------------------------------------------------------------------------------- /iot_push_client_starter_test/iot_push_client_starter.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /iot_push_client_starter_test/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | iot_push 5 | com.lxr.iot 6 | 1.0-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | iot_push_client_starter 11 | jar 12 | 13 | iot_push_client_starter 14 | http://maven.apache.org 15 | 16 | 17 | UTF-8 18 | 19 | 20 | 21 | 22 | junit 23 | junit 24 | 3.8.1 25 | test 26 | 27 | 28 | com.lxr.iot 29 | iot_push_client 30 | 1.0-SNAPSHOT 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-compiler-plugin 38 | 3.3 39 | 40 | 1.8 41 | 1.8 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /iot_push_client_starter_test/src/main/java/com/lxr/iot/App.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | import org.springframework.context.ConfigurableApplicationContext; 5 | 6 | import static org.springframework.boot.SpringApplication.run; 7 | 8 | /** 9 | * Hello world! 10 | * 11 | */ 12 | @SpringBootApplication 13 | public class App 14 | { 15 | public static void main( String[] args ) 16 | { 17 | ConfigurableApplicationContext run = run(App.class, args); 18 | } 19 | } -------------------------------------------------------------------------------- /iot_push_client_starter_test/src/main/java/com/lxr/iot/SubListener.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot; 2 | 3 | import com.lxr.iot.auto.MqttListener; 4 | import com.lxr.iot.auto.MqttMessageListener; 5 | import io.netty.handler.codec.mqtt.MqttQoS; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | * @author lxr 11 | * @create 2018-01-12 15:14 12 | **/ 13 | @Slf4j 14 | @Service 15 | @MqttMessageListener(qos = MqttQoS.AT_LEAST_ONCE,topic = {"/test","/haha"}) 16 | public class SubListener implements MqttListener { 17 | 18 | @Override 19 | public void callBack(String topic, String msg) { 20 | log.info("============================="+topic+msg); 21 | } 22 | 23 | 24 | 25 | @Override 26 | public void callThrowable(Throwable e) { 27 | log.info("exception",e); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /iot_push_client_starter_test/src/main/java/com/lxr/iot/example/MqttMain.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.example; 2 | 3 | import com.lxr.iot.auto.MqttListener; 4 | import com.lxr.iot.bootstrap.MqttProducer; 5 | import com.lxr.iot.bootstrap.Producer; 6 | import com.lxr.iot.properties.ConnectOptions; 7 | 8 | /** 9 | * 测试 10 | * 11 | * @author lxr 12 | * @create 2018-01-10 10:09 13 | **/ 14 | public class MqttMain { 15 | 16 | public static void main(String[] strings){ 17 | Producer producer = new MqttProducer(); 18 | ConnectOptions connectOptions = new ConnectOptions(); 19 | connectOptions.setBacklog(1024); 20 | connectOptions.setConnectTime(1000l); 21 | connectOptions.setSsl(false); 22 | connectOptions.setJksStorePassword("mu$tch8ng3"); 23 | connectOptions.setJksFile("/securesocket.jks"); 24 | connectOptions.setJksCertificatePassword("inc0rrect"); 25 | connectOptions.setServerIp("192.168.109.1"); 26 | connectOptions.setPort(1884); 27 | connectOptions.setBossThread(1); 28 | connectOptions.setWorkThread(8); 29 | connectOptions.setMinPeriod(10); 30 | connectOptions.setRevbuf(1024); 31 | connectOptions.setSndbuf(1024); 32 | connectOptions.setTcpNodelay(true); 33 | connectOptions.setKeepalive(true); 34 | ConnectOptions.MqttOpntions mqttOpntions = new ConnectOptions.MqttOpntions(); 35 | mqttOpntions.setHasCleanSession(true); 36 | mqttOpntions.setHasWillFlag(true); 37 | mqttOpntions.setClientIdentifier("111"); 38 | mqttOpntions.setWillTopic("/lose/device"); 39 | mqttOpntions.setWillQos(1); 40 | mqttOpntions.setHasWillRetain(false); 41 | mqttOpntions.setWillMessage("123123"); 42 | mqttOpntions.setHasPassword(false); 43 | mqttOpntions.setHasPassword(false); 44 | connectOptions.setMqtt(mqttOpntions); 45 | producer.setMqttListener(new MqttListener() { 46 | @Override 47 | public void callBack(String topic, String msg) { 48 | System.out.print("========================================"+topic+msg); 49 | } 50 | @Override 51 | public void callThrowable(Throwable e) { 52 | 53 | } 54 | }); 55 | producer.connect(connectOptions); 56 | // producer.sub(SubMessage.builder().qos(MqttQoS.AT_LEAST_ONCE).topic("/t1/t2").build()); 57 | producer.pub("/test","hah",2); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /iot_push_client_starter_test/src/main/java/com/lxr/iot/spring/springTest.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.spring; 2 | 3 | import com.lxr.iot.bootstrap.Producer; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * @author lxr 9 | * @create 2018-04-13 17:02 10 | **/ 11 | @Component 12 | public class springTest { 13 | 14 | @Autowired 15 | private Producer producer; 16 | /** 17 | * 系统初始化自动配置了,直接注入此类进行操作 18 | */ 19 | 20 | } 21 | -------------------------------------------------------------------------------- /iot_push_client_starter_test/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | 2 | lxr: 3 | iot: 4 | client: 5 | connectTime: 10 # 链接超时 6 | serverIp: 192.168.109.1 7 | port: 1884 #端口 8 | keepalive: true #Socket参数,连接保活,默认值为False。启用该功能时,TCP会主动探测空闲连接的有效性。可以将此功能视为TCP的心跳机制,需要注意的是:默认的心跳间隔是7200s即2小时。Netty默认关闭该功能 9 | reuseaddr: true #地址复用,默认值False。有四种情况可以使用:(1).当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你希望启动的程序的socket2要占用该地址和端口,比如重启服务且保持先前端口。(2).有多块网卡或用IP Alias技术的机器在同一端口启动多个进程,但每个进程绑定的本地IP地址不能相同。(3).单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。(4).完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。 10 | tcpNodelay: true #TCP参数,立即发送数据,默认值为Ture(Netty默认为True而操作系统默认为False)。该值设置Nagle算法的启用,改算法将小的碎片数据连接成更大的报文来最小化所发送的报文的数量,如果需要发送一些较小的报文,则需要禁用该算法。Netty默认禁用该算法,从而最小化报文传输延时。 11 | sndbuf: 10485760 # Socket参数,TCP数据发送缓冲区大小。 12 | revbuf: 10485760 # Socket参数,TCP数据接收缓冲区大小。 13 | heart: 60 # 读超时时间 14 | backlog: 1024 #  Socket参数,服务端接受连接的队列长度,如果队列已满,客户端连接将被拒绝 15 | ssl: false # 使用ssl加密 16 | initalDelay: 10 # mqtt qos1 qos2 消息 重发延迟 17 | period: 10 # mqtt qos1 qos2 消息 重发周期 18 | jksFile: /securesocket.jks # ssl 加密 jks文件地址 19 | jksStorePassword: mu$tch8ng3 # 读取jks密码 20 | jksCertificatePassword: inc0rrect # 读取私钥密码 21 | workThread: 1 22 | mqtt: 23 | clientIdentifier: client-1 24 | hasWillFlag: true 25 | hasCleanSession: true 26 | keepAliveTimeSeconds: 3000 27 | hasUserName: true 28 | hasPassword: true 29 | willTopic: /lose/device # isWillFlag = true 时候配置 设备离线得时候 往这个主题默认发送离线设备id 30 | password: 123 # hasPassword = true 时候配置 31 | userName: 123 # hasUserName = true 时候配置 32 | hasWillRetain: false # isWillFlag = true 时候配置 33 | willQos: 1 # isWillFlag = true 时候配置 34 | KeepAliveTime: 10 # 心跳时长 35 | 36 | server: 37 | port: 8989 38 | logging: 39 | level: 40 | io.netty: debug 41 | -------------------------------------------------------------------------------- /iot_push_client_starter_test/src/main/resources/securesocket.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ssqq1lxr/iot_push/f65d6ef642c7fb8da329c01957d2adab7765fdde/iot_push_client_starter_test/src/main/resources/securesocket.jks -------------------------------------------------------------------------------- /iot_push_common/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | iot_push 5 | com.lxr.iot 6 | 1.0-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | iot_push_common 11 | jar 12 | 13 | iot_push_common 14 | http://maven.apache.org 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-tomcat 20 | ${springboot.version} 21 | 22 | 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter 28 | ${springboot.version} 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-test 34 | ${springboot.version} 35 | test 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-web 41 | ${springboot.version} 42 | 43 | 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter-data-redis 49 | ${springboot.version} 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-amqp 55 | ${springboot.version} 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-autoconfigure 60 | ${springboot.version} 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-starter-aop 65 | ${springboot.version} 66 | 67 | 68 | 69 | org.projectlombok 70 | lombok 71 | 1.16.16 72 | 73 | 74 | com.google.guava 75 | guava 76 | 23.3-jre 77 | 78 | 79 | io.netty 80 | netty-all 81 | 4.1.12.Final 82 | 83 | 84 | org.apache.commons 85 | commons-lang3 86 | 3.5 87 | 88 | 89 | org.projectlombok 90 | lombok 91 | 1.16.16 92 | 93 | 94 | org.apache.curator 95 | curator-recipes 96 | 2.9.1 97 | 98 | 99 | org.apache.curator 100 | curator-client 101 | 2.9.1 102 | 103 | 104 | org.apache.curator 105 | curator-framework 106 | 2.9.1 107 | 108 | 109 | org.springframework 110 | spring-context 111 | 4.3.7.RELEASE 112 | 113 | 114 | 115 | com.lmax 116 | disruptor 117 | 3.4.2 118 | 119 | 120 | 121 | io.projectreactor 122 | reactor-core 123 | 3.2.2.RELEASE 124 | 125 | 126 | 127 | 128 | 129 | 130 | org.apache.maven.plugins 131 | maven-compiler-plugin 132 | 3.3 133 | 134 | 1.8 135 | 1.8 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/enums/ConfirmStatus.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.enums;/** 2 | * Created by wangcy on 2018/1/6. 3 | */ 4 | 5 | /** 6 | * 确认状态 7 | * 8 | * @author lxr 9 | * @create 2018-01-06 17:15 10 | **/ 11 | public enum ConfirmStatus { 12 | PUB, 13 | PUBREC, 14 | PUBREL, 15 | COMPLETE, 16 | } 17 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/enums/ProtocolEnum.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.enums; 2 | 3 | 4 | /** 5 | * 协议 6 | * 7 | * @author lxr 8 | * @create 2018-01-06 15:13 9 | **/ 10 | public enum ProtocolEnum { 11 | 12 | MQTT, 13 | 14 | MQTT_WS_MQTT, 15 | 16 | MQTT_WS_PAHO, 17 | 18 | } 19 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/enums/QosStatus.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.enums;/** 2 | * Created by wangcy on 2017/12/14. 3 | */ 4 | 5 | /** 6 | * Qos确认状态 7 | * 8 | * @author lxr 9 | * @create 2017-12-14 17:53 10 | **/ 11 | public enum QosStatus { 12 | 13 | PUBD, // 已发送 没收到RECD (发送) 14 | 15 | RECD, //publish 推送回复过(发送) 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/enums/SessionStatus.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.enums;/** 2 | * Created by wangcy on 2017/11/23. 3 | */ 4 | 5 | /** 6 | * Channel 会话状态 7 | * 8 | * @author lxr 9 | * @create 2017-11-23 16:04 10 | **/ 11 | public enum SessionStatus { 12 | 13 | OPEN, 14 | CLOSE 15 | } 16 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/enums/SubStatus.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.enums; 2 | 3 | /** 4 | * 是否订阅过主题 5 | * 6 | * @author lxr 7 | * @create 2017-11-29 15:25 8 | **/ 9 | public enum SubStatus { 10 | YES, 11 | NO 12 | } 13 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/exception/ConnectionException.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.exception; 2 | 3 | /** 4 | * 连接异常 5 | * 6 | * @author lxr 7 | * @create 2017-11-23 14:34 8 | **/ 9 | public class ConnectionException extends RuntimeException { 10 | 11 | public ConnectionException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/exception/NoFindHandlerException.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.exception; 2 | 3 | /** 4 | * @author lxr 5 | * @create 2018-01-03 16:25 6 | **/ 7 | public class NoFindHandlerException extends RuntimeException{ 8 | 9 | public NoFindHandlerException(String message) { 10 | super(message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/ip/IpUtils.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.ip; 2 | 3 | import java.net.Inet4Address; 4 | import java.net.InetAddress; 5 | import java.net.NetworkInterface; 6 | import java.util.Enumeration; 7 | 8 | /** 9 | * ip操作 10 | * 11 | * @author lxr 12 | * @create 2017-11-16 9:44 13 | **/ 14 | public class IpUtils { 15 | 16 | /*** 17 | * 获取外网IP 18 | * @return 19 | */ 20 | public static String internetIp() { 21 | try { 22 | 23 | Enumeration networks = NetworkInterface.getNetworkInterfaces(); 24 | InetAddress inetAddress = null; 25 | Enumeration inetAddresses = null; 26 | while (networks.hasMoreElements()) { 27 | inetAddresses = networks.nextElement().getInetAddresses(); 28 | while (inetAddresses.hasMoreElements()) { 29 | inetAddress = inetAddresses.nextElement(); 30 | if (inetAddress != null 31 | && inetAddress instanceof Inet4Address 32 | && !inetAddress.isSiteLocalAddress() 33 | && !inetAddress.isLoopbackAddress() 34 | && inetAddress.getHostAddress().indexOf(":") == -1) { 35 | return inetAddress.getHostAddress(); 36 | } 37 | } 38 | } 39 | 40 | return null; 41 | 42 | } catch (Exception e) { 43 | 44 | throw new RuntimeException(e); 45 | } 46 | } 47 | 48 | /** 49 | * 获取内网IP 50 | * 51 | * @return 52 | */ 53 | public static String intranetIp() { 54 | try { 55 | return InetAddress.getLocalHost().getHostAddress(); 56 | } catch (Exception e) { 57 | throw new RuntimeException(e); 58 | } 59 | } 60 | 61 | /** 62 | * 获取服务启动host 63 | * @return 64 | */ 65 | public static String getHost(){ 66 | return internetIp()==null?intranetIp():internetIp(); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/mqtt/ClientMqttHandlerService.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.mqtt; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.handler.codec.mqtt.MqttMessage; 5 | import io.netty.handler.codec.mqtt.MqttSubAckMessage; 6 | import io.netty.handler.timeout.IdleStateEvent; 7 | 8 | /** 9 | * 抽象出服务端事件 10 | * 11 | * @author lxr 12 | * @create 2018-01-03 16:11 13 | **/ 14 | public abstract class ClientMqttHandlerService implements MqttHandlerIntf { 15 | 16 | @Override 17 | public void doTimeOut(Channel channel, IdleStateEvent evt) { 18 | heart(channel,evt); 19 | } 20 | 21 | public abstract void heart(Channel channel, IdleStateEvent evt); 22 | 23 | public abstract void suback(Channel channel,MqttSubAckMessage mqttMessage) ; 24 | 25 | public abstract void pubBackMessage(Channel channel, int i); 26 | 27 | public abstract void unsubBack(Channel channel, MqttMessage mqttMessage); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/mqtt/MqttHander.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.mqtt; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.channel.SimpleChannelInboundHandler; 5 | import io.netty.handler.codec.mqtt.MqttFixedHeader; 6 | import io.netty.handler.codec.mqtt.MqttMessage; 7 | import io.netty.handler.timeout.IdleStateEvent; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | import java.util.Optional; 11 | 12 | /** 13 | * mqtt协议处理器 14 | * 15 | * @author lxr 16 | * @create 2017-11-20 13:38 17 | **/ 18 | @Slf4j 19 | public abstract class MqttHander extends SimpleChannelInboundHandler { 20 | 21 | MqttHandlerIntf mqttHandlerApi; 22 | 23 | public MqttHander(MqttHandlerIntf mqttHandlerIntf){ 24 | this.mqttHandlerApi=mqttHandlerIntf; 25 | } 26 | 27 | @Override 28 | protected void channelRead0(ChannelHandlerContext channelHandlerContext, MqttMessage mqttMessage) throws Exception { 29 | MqttFixedHeader mqttFixedHeader = mqttMessage.fixedHeader(); 30 | Optional.ofNullable(mqttFixedHeader) 31 | .ifPresent(mqttFixedHeader1 -> doMessage(channelHandlerContext,mqttMessage)); 32 | } 33 | 34 | public abstract void doMessage(ChannelHandlerContext channelHandlerContext, MqttMessage mqttMessage); 35 | 36 | 37 | 38 | 39 | @Override 40 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 41 | log.info("【DefaultMqttHandler:channelInactive】"+ctx.channel().localAddress().toString()+"关闭成功"); 42 | mqttHandlerApi.close(ctx.channel()); 43 | } 44 | 45 | @Override 46 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 47 | if(evt instanceof IdleStateEvent){ 48 | mqttHandlerApi.doTimeOut(ctx.channel(),(IdleStateEvent)evt); 49 | } 50 | super.userEventTriggered(ctx, evt); 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/mqtt/MqttHandlerIntf.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.mqtt; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.handler.codec.mqtt.*; 5 | import io.netty.handler.timeout.IdleStateEvent; 6 | 7 | /** 8 | * 自定义 对外暴露 消息处理api 9 | * 10 | * @author lxr 11 | * @create 2017-11-21 9:53 12 | **/ 13 | public interface MqttHandlerIntf { 14 | 15 | void close(Channel channel); 16 | 17 | void puback(Channel channel, MqttMessage mqttMessage); 18 | 19 | void pubrec(Channel channel, MqttMessage mqttMessage); 20 | 21 | void pubrel(Channel channel, MqttMessage mqttMessage); 22 | 23 | void pubcomp(Channel channel, MqttMessage mqttMessage); 24 | 25 | void doTimeOut(Channel channel, IdleStateEvent evt); 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/mqtt/ServerMqttHandlerService.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.mqtt; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.handler.codec.mqtt.*; 5 | import io.netty.handler.timeout.IdleStateEvent; 6 | 7 | /** 8 | * 抽象出服务端事件 9 | * 10 | * @author lxr 11 | * @create 2018-01-03 16:11 12 | **/ 13 | public abstract class ServerMqttHandlerService implements MqttHandlerIntf { 14 | 15 | 16 | public abstract boolean login(Channel channel, MqttConnectMessage mqttConnectMessage); 17 | 18 | public abstract void publish(Channel channel, MqttPublishMessage mqttPublishMessage); 19 | 20 | public abstract void subscribe(Channel channel, MqttSubscribeMessage mqttSubscribeMessage); 21 | 22 | 23 | public abstract void pong(Channel channel); 24 | 25 | public abstract void unsubscribe(Channel channel, MqttUnsubscribeMessage mqttMessage); 26 | 27 | 28 | public abstract void disconnect(Channel channel); 29 | 30 | public abstract void doTimeOut(Channel channel, IdleStateEvent evt); 31 | } 32 | 33 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/pool/DefaultThreadFactory.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.pool; 2 | 3 | 4 | import java.util.concurrent.ThreadFactory; 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | 7 | /** 8 | * 线程池 9 | * 10 | * @author lxr 11 | * @create 2017-11-18 14:05 12 | **/ 13 | public class DefaultThreadFactory implements ThreadFactory { 14 | 15 | private static final AtomicInteger poolNumber = new AtomicInteger(1); 16 | private final ThreadGroup threadGroup; 17 | private final AtomicInteger currentThreadNumber = new AtomicInteger(1); 18 | private final String namePrefix; 19 | private int priority = Thread.NORM_PRIORITY; 20 | private boolean isDaemon = false; 21 | 22 | 23 | public DefaultThreadFactory(String prefix) { 24 | this(prefix, false); 25 | } 26 | 27 | public DefaultThreadFactory(String prefix, boolean isDaemon) { 28 | this(prefix, isDaemon, Thread.NORM_PRIORITY); 29 | } 30 | 31 | public DefaultThreadFactory(String prefix, boolean isDaemon, int priority) { 32 | SecurityManager s = System.getSecurityManager(); 33 | this.threadGroup = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); 34 | this.namePrefix = prefix + "-" + poolNumber.getAndIncrement() + "-thread-"; 35 | this.isDaemon = isDaemon; 36 | this.priority = priority; 37 | } 38 | 39 | @Override 40 | public Thread newThread(Runnable r) { 41 | Thread thread = new Thread(threadGroup, r, namePrefix + currentThreadNumber.getAndIncrement(), 0); 42 | thread.setDaemon(isDaemon); 43 | thread.setPriority(priority); 44 | return thread; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/pool/ExecutorQueue.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.pool; 2 | 3 | import java.util.concurrent.LinkedTransferQueue; 4 | import java.util.concurrent.RejectedExecutionException; 5 | 6 | /** 7 | * LinkedTransferQueue 能保证更高性能,相比与LinkedBlockingQueue有明显提升 8 | *不过LinkedTransferQueue的缺点是没有队列长度控制,需要在外层协助控制 9 | * * @author lxr 10 | * @create 2017-11-18 14:05 11 | */ 12 | public class ExecutorQueue extends LinkedTransferQueue { 13 | private static final long serialVersionUID = -265236426751004839L; 14 | private StandardThreadExecutor threadPoolExecutor; 15 | 16 | public ExecutorQueue() { 17 | super(); 18 | } 19 | 20 | public void setStandardThreadExecutor(StandardThreadExecutor threadPoolExecutor) { 21 | this.threadPoolExecutor = threadPoolExecutor; 22 | } 23 | 24 | // 注:代码来源于 tomcat 25 | public boolean force(Runnable o) { 26 | if (threadPoolExecutor.isShutdown()) { 27 | throw new RejectedExecutionException("Executor not running, can't force a command into the queue"); 28 | } 29 | // forces the item onto the queue, to be used if the task is rejected 30 | return super.offer(o); 31 | } 32 | 33 | // 注:tomcat的代码进行一些小变更 34 | public boolean offer(Runnable o) { 35 | int poolSize = threadPoolExecutor.getPoolSize(); 36 | 37 | // we are maxed out on threads, simply queue the object 38 | if (poolSize == threadPoolExecutor.getMaximumPoolSize()) { 39 | return super.offer(o); 40 | } 41 | // we have idle threads, just add it to the queue 42 | // note that we don't use getActiveCount(), see BZ 49730 43 | if (poolSize >= threadPoolExecutor.getSubmittedTasksCount()) { 44 | return super.offer(o); 45 | } 46 | // if we have less threads than maximum force creation of a new 47 | // thread 48 | if (poolSize < threadPoolExecutor.getMaximumPoolSize()) { 49 | return false; 50 | } 51 | // if we reached here, we need to add it to the queue 52 | return super.offer(o); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/pool/Scheduled.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.pool; 2 | 3 | import java.util.concurrent.ScheduledFuture; 4 | 5 | /** 6 | * 接口 7 | * 8 | * @author lxr 9 | * @create 2017-12-14 10:47 10 | **/ 11 | @FunctionalInterface 12 | public interface Scheduled { 13 | 14 | ScheduledFuture submit(Runnable runnable); 15 | } 16 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/pool/StandardThreadExecutor.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.pool; 2 | 3 | 4 | import java.util.concurrent.*; 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | 7 | /** 8 | * 代码和思路主要来自于: 9 | * tomcat: org.apache.catalina.core.StandardThreadExecutor 10 | * 11 | * java.util.concurrent.threadPoolExecutor execute执行策略: 12 | * 优先offer到queue,queue满后再扩充线程到maxThread,如果已经到了maxThread就reject 13 | * 比较适合于CPU密集型应用(比如runnable内部执行的操作都在JVM内部,memory copy, or compute等等) 14 | * 15 | * StandardThreadExecutor execute执行策略: 16 | * 优先扩充线程到maxThread,再offer到queue,如果满了就reject 17 | * 比较适合于业务处理需要远程资源的场景 18 | * 19 | * @author renhuan 20 | * @version v1.0 create date: 2017-11-10 21 | * 22 | */ 23 | public class StandardThreadExecutor extends ThreadPoolExecutor { 24 | 25 | public static final int DEFAULT_MIN_THREADS = 20; 26 | public static final int DEFAULT_MAX_THREADS = 200; 27 | public static final int DEFAULT_MAX_IDLE_TIME = 60000; // 1 minutes 28 | 29 | protected AtomicInteger submittedTasksCount; // 正在处理的任务数 30 | private int maxSubmittedTaskCount; // 最大允许同时处理的任务数 31 | 32 | public StandardThreadExecutor() { 33 | this(DEFAULT_MIN_THREADS, DEFAULT_MAX_THREADS); 34 | } 35 | 36 | public StandardThreadExecutor(int coreThread, int maxThreads) { 37 | this(coreThread, maxThreads, maxThreads); 38 | } 39 | 40 | public StandardThreadExecutor(int coreThread, int maxThreads, long keepAliveTime, TimeUnit unit) { 41 | this(coreThread, maxThreads, keepAliveTime, unit, maxThreads); 42 | } 43 | 44 | public StandardThreadExecutor(int coreThreads, int maxThreads, int queueCapacity) { 45 | this(coreThreads, maxThreads, queueCapacity, Executors.defaultThreadFactory()); 46 | } 47 | 48 | public StandardThreadExecutor(int coreThreads, int maxThreads, int queueCapacity, ThreadFactory threadFactory) { 49 | this(coreThreads, maxThreads, DEFAULT_MAX_IDLE_TIME, TimeUnit.MILLISECONDS, queueCapacity, threadFactory); 50 | } 51 | 52 | public StandardThreadExecutor(int coreThreads, int maxThreads, long keepAliveTime, TimeUnit unit, int queueCapacity) { 53 | this(coreThreads, maxThreads, keepAliveTime, unit, queueCapacity, Executors.defaultThreadFactory()); 54 | } 55 | 56 | public StandardThreadExecutor(int coreThreads, int maxThreads, long keepAliveTime, TimeUnit unit, 57 | int queueCapacity, ThreadFactory threadFactory) { 58 | this(coreThreads, maxThreads, keepAliveTime, unit, queueCapacity, threadFactory, new AbortPolicy()); 59 | } 60 | 61 | public StandardThreadExecutor(int coreThreads, int maxThreads, long keepAliveTime, TimeUnit unit, 62 | int queueCapacity, ThreadFactory threadFactory, RejectedExecutionHandler handler) { 63 | super(coreThreads, maxThreads, keepAliveTime, unit, new ExecutorQueue(), threadFactory, handler); 64 | ((ExecutorQueue) getQueue()).setStandardThreadExecutor(this); 65 | 66 | submittedTasksCount = new AtomicInteger(0); 67 | 68 | // 最大并发任务限制: 队列buffer数 + 最大线程数 69 | maxSubmittedTaskCount = queueCapacity + maxThreads; 70 | } 71 | 72 | @Override 73 | public void execute(Runnable command) { 74 | int count = submittedTasksCount.incrementAndGet(); 75 | 76 | // 超过最大的并发任务限制,进行 reject 77 | // 依赖的LinkedTransferQueue没有长度限制,因此这里进行控制 78 | if (count > maxSubmittedTaskCount) { 79 | submittedTasksCount.decrementAndGet(); 80 | getRejectedExecutionHandler().rejectedExecution(command, this); 81 | } 82 | 83 | try { 84 | super.execute(command); 85 | } catch (RejectedExecutionException rx) { 86 | // there could have been contention around the queue 87 | if (!((ExecutorQueue) getQueue()).force(command)) { 88 | submittedTasksCount.decrementAndGet(); 89 | getRejectedExecutionHandler().rejectedExecution(command, this); 90 | } 91 | rx.printStackTrace(); 92 | } 93 | } 94 | 95 | public int getSubmittedTasksCount() { 96 | return this.submittedTasksCount.get(); 97 | } 98 | 99 | 100 | protected void afterExecute(Runnable r, Throwable t) { 101 | submittedTasksCount.decrementAndGet(); 102 | } 103 | } 104 | 105 | 106 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/properties/ConnectOptions.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.properties; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * 链接参数配置 8 | * 9 | * @author lxr 10 | * @create 2018-01-04 16:13 11 | **/ 12 | @ConfigurationProperties(prefix ="lxr.iot.client") 13 | @Data 14 | public class ConnectOptions { 15 | 16 | private long connectTime; 17 | 18 | private String serverIp; 19 | 20 | private int port ; 21 | 22 | private boolean keepalive ; 23 | 24 | private boolean reuseaddr ; 25 | 26 | private boolean tcpNodelay ; 27 | 28 | private int backlog ; 29 | 30 | private int sndbuf ; 31 | 32 | private int revbuf ; 33 | 34 | private int heart; 35 | 36 | private boolean ssl ; 37 | 38 | private String jksFile; 39 | 40 | private String jksStorePassword; 41 | 42 | private String jksCertificatePassword; 43 | 44 | private int minPeriod ; 45 | 46 | private int bossThread; 47 | 48 | private int workThread; 49 | 50 | private MqttOpntions mqtt; 51 | 52 | @Data 53 | public static class MqttOpntions{ 54 | 55 | private String clientIdentifier; 56 | 57 | private String willTopic; 58 | 59 | private String willMessage; 60 | 61 | private String userName; 62 | 63 | private String password; 64 | 65 | private boolean hasUserName; 66 | 67 | private boolean hasPassword; 68 | 69 | private boolean hasWillRetain; 70 | 71 | private int willQos; 72 | 73 | private boolean hasWillFlag; 74 | 75 | private boolean hasCleanSession; 76 | 77 | private int KeepAliveTime; 78 | 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/properties/InitBean.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.properties; 2 | 3 | import com.lxr.iot.enums.ProtocolEnum; 4 | import com.lxr.iot.mqtt.MqttHander; 5 | import lombok.Data; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * netty服务启动参数 11 | * 12 | * @author lxr 13 | * @create 2017-11-18 15:08 14 | **/ 15 | @ConfigurationProperties(prefix ="lxr.iot.server") 16 | @Data 17 | public class InitBean { 18 | 19 | private ProtocolEnum protocol; 20 | 21 | private int port ; 22 | 23 | private String serverName ; 24 | 25 | private boolean keepalive ; 26 | 27 | private boolean reuseaddr ; 28 | 29 | 30 | private boolean tcpNodelay ; 31 | 32 | private int backlog ; 33 | 34 | private int sndbuf ; 35 | 36 | private int revbuf ; 37 | 38 | 39 | private int heart ; 40 | 41 | private boolean ssl ; 42 | 43 | private String jksFile; 44 | 45 | private String jksStorePassword; 46 | 47 | private String jksCertificatePassword; 48 | 49 | private Class mqttHander ; 50 | 51 | 52 | private int initalDelay ; 53 | 54 | private int period ; 55 | 56 | private int bossThread; 57 | 58 | private int workThread; 59 | 60 | } 61 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/ssl/SecureSocketSslContextFactory.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.ssl; 2 | 3 | 4 | import org.jboss.netty.util.internal.SystemPropertyUtil; 5 | 6 | import javax.net.ssl.*; 7 | import java.security.KeyStore; 8 | import java.security.SecureRandom; 9 | 10 | /** 11 | * Creates a bogus {@link SSLContext}. A client-side context created by this 12 | * factory accepts any certificate even if it is invalid. A server-side context 13 | * created by this factory sends a bogus certificate defined in {@link }. 14 | *

15 | * You will have to create your context differently in a real world application. 16 | * 17 | *

Client Certificate Authentication

18 | * 19 | * To enable client certificate authentication: 20 | *
    21 | *
  • Enable client authentication on the server side by calling 22 | * {@link SSLEngine#setNeedClientAuth(boolean)} before creating 23 | * {@link }.
  • 24 | *
  • When initializing an {@link SSLContext} on the client side, 25 | * specify the {@link KeyManager} that contains the client certificate as 26 | * the first argument of {@link SSLContext#init(KeyManager[], TrustManager[], SecureRandom)}.
  • 27 | *
  • When initializing an {@link SSLContext} on the server side, 28 | * specify the proper {@link TrustManager} as the second argument of 29 | * {@link SSLContext#init(KeyManager[], TrustManager[], SecureRandom)} 30 | * to validate the client certificate.
  • 31 | *
32 | */ 33 | public final class SecureSocketSslContextFactory { 34 | 35 | private static final String PROTOCOL = "TLS"; 36 | private static final SSLContext SERVER_CONTEXT; 37 | private static final SSLContext CLIENT_CONTEXT; 38 | 39 | static { 40 | String algorithm = SystemPropertyUtil.get("ssl.KeyManagerFactory.algorithm"); 41 | if (algorithm == null) { 42 | algorithm = "SunX509"; 43 | } 44 | 45 | SSLContext serverContext; 46 | SSLContext clientContext; 47 | try { 48 | // 49 | //SecureSocketSslContextFactory.class.getResourceAsStream("/securesocket.jks") 50 | KeyStore ks = KeyStore.getInstance("JKS"); 51 | ks.load(SecureSocketKeyStore.asInputStream(), 52 | SecureSocketKeyStore.getKeyStorePassword()); 53 | 54 | // Set up key manager factory to use our key store 55 | KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); 56 | kmf.init(ks, SecureSocketKeyStore.getCertificatePassword()); 57 | 58 | // Initialize the SSLContext to work with our key managers. 59 | serverContext = SSLContext.getInstance(PROTOCOL); 60 | serverContext.init(kmf.getKeyManagers(), null, null); 61 | } catch (Exception e) { 62 | throw new Error( 63 | "Failed to initialize the server-side SSLContext", e); 64 | } 65 | 66 | try { 67 | clientContext = SSLContext.getInstance(PROTOCOL); 68 | clientContext.init(null, SecureSokcetTrustManagerFactory.getTrustManagers(), null); 69 | } catch (Exception e) { 70 | throw new Error( 71 | "Failed to initialize the client-side SSLContext", e); 72 | } 73 | 74 | SERVER_CONTEXT = serverContext; 75 | CLIENT_CONTEXT = clientContext; 76 | } 77 | 78 | public static SSLContext getServerContext() { 79 | return SERVER_CONTEXT; 80 | } 81 | 82 | public static SSLContext getClientContext() { 83 | return CLIENT_CONTEXT; 84 | } 85 | 86 | private SecureSocketSslContextFactory() { 87 | // Unused 88 | } 89 | } -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/ssl/SecureSokcetTrustManagerFactory.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.ssl; 2 | 3 | import javax.net.ssl.ManagerFactoryParameters; 4 | import javax.net.ssl.TrustManager; 5 | import javax.net.ssl.TrustManagerFactorySpi; 6 | import javax.net.ssl.X509TrustManager; 7 | import java.security.InvalidAlgorithmParameterException; 8 | import java.security.KeyStore; 9 | import java.security.KeyStoreException; 10 | import java.security.cert.X509Certificate; 11 | 12 | /** 13 | * Bogus {@link TrustManagerFactorySpi} which accepts any certificate 14 | * even if it is invalid. 15 | */ 16 | public class SecureSokcetTrustManagerFactory extends TrustManagerFactorySpi { 17 | 18 | private static final TrustManager DUMMY_TRUST_MANAGER = new X509TrustManager() { 19 | @Override 20 | public X509Certificate[] getAcceptedIssuers() { 21 | return new X509Certificate[0]; 22 | } 23 | 24 | @Override 25 | public void checkClientTrusted(X509Certificate[] chain, String authType) { 26 | // Always trust - it is an example. 27 | // You should do something in the real world. 28 | // You will reach here only if you enabled client certificate auth, 29 | // as described in SecureChatSslContextFactory. 30 | System.err.println( 31 | "UNKNOWN CLIENT CERTIFICATE: " + chain[0].getSubjectDN()); 32 | } 33 | 34 | @Override 35 | public void checkServerTrusted(X509Certificate[] chain, String authType) { 36 | // Always trust - it is an example. 37 | // You should do something in the real world. 38 | System.err.println( 39 | "UNKNOWN SERVER CERTIFICATE: 222 " + chain[0].getSubjectDN()); 40 | } 41 | }; 42 | 43 | public static TrustManager[] getTrustManagers() { 44 | return new TrustManager[] { DUMMY_TRUST_MANAGER }; 45 | } 46 | 47 | @Override 48 | protected TrustManager[] engineGetTrustManagers() { 49 | return getTrustManagers(); 50 | } 51 | 52 | @Override 53 | protected void engineInit(KeyStore keystore) throws KeyStoreException { 54 | // Unused 55 | } 56 | 57 | @Override 58 | protected void engineInit(ManagerFactoryParameters managerFactoryParameters) 59 | throws InvalidAlgorithmParameterException { 60 | // Unused 61 | } 62 | } -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/ssl/StreamReader.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.ssl; 2 | 3 | import java.io.InputStream; 4 | 5 | public class StreamReader { 6 | 7 | 8 | public String toByteArray(InputStream fin) 9 | { 10 | int i = -1; 11 | StringBuilder buf = new StringBuilder(); 12 | try{ 13 | while((i=fin.read())!=-1){ 14 | if(buf.length()>0) buf.append(","); 15 | buf.append("(byte)"); 16 | buf.append(i); 17 | } 18 | 19 | }catch(Throwable e){ 20 | ; 21 | } 22 | 23 | return buf.toString(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/ssl/X509CertTool.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.ssl; 2 | 3 | import sun.security.x509.*; 4 | 5 | import java.io.IOException; 6 | import java.math.BigInteger; 7 | import java.security.GeneralSecurityException; 8 | import java.security.KeyPair; 9 | import java.security.PrivateKey; 10 | import java.security.SecureRandom; 11 | import java.security.cert.X509Certificate; 12 | import java.util.Date; 13 | 14 | 15 | /* 16 | * This class would require rt.jar in the class path in order to 17 | * generated it alternative is using keytool. 18 | */ 19 | 20 | public class X509CertTool { 21 | 22 | /** 23 | * Create a self-signed X.509 Certificate 24 | * @param dn the X.509 Distinguished Name, eg "CN=Test, L=London, C=GB" 25 | * @param pair the KeyPair 26 | * @param days how many days from now the Certificate is valid for 27 | * @param algorithm the signing algorithm, eg "SHA1withRSA" 28 | */ 29 | @SuppressWarnings("restriction") 30 | X509Certificate generateCertificate(String dn, KeyPair pair, int days, 31 | String algorithm) throws GeneralSecurityException, IOException { 32 | PrivateKey privkey = pair.getPrivate(); 33 | X509CertInfo info = new X509CertInfo(); 34 | Date from = new Date(); 35 | Date to = new Date(from.getTime() + days * 86400000l); 36 | CertificateValidity interval = new CertificateValidity(from, to); 37 | BigInteger sn = new BigInteger(64, new SecureRandom()); 38 | X500Name owner = new X500Name(dn); 39 | 40 | info.set(X509CertInfo.VALIDITY, interval); 41 | info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn)); 42 | info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner)); 43 | info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner)); 44 | info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic())); 45 | info.set(X509CertInfo.VERSION, new CertificateVersion( 46 | CertificateVersion.V3)); 47 | AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid); 48 | info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo)); 49 | 50 | // Sign the cert to identify the algorithm that's used. 51 | X509CertImpl cert = new X509CertImpl(info); 52 | cert.sign(privkey, algorithm); 53 | 54 | // Update the algorith, and resign. 55 | algo = (AlgorithmId) cert.get(X509CertImpl.SIG_ALG); 56 | info.set(CertificateAlgorithmId.NAME + "." 57 | + CertificateAlgorithmId.ALGORITHM, algo); 58 | cert = new X509CertImpl(info); 59 | cert.sign(privkey, algorithm); 60 | return cert; 61 | } 62 | 63 | public static void main(String[] args) { 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/util/ByteBufUtil.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.util; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | /** 6 | * 跨线程情况下 byteBuf 需要转换成byte[] 7 | * 8 | * @author lxr 9 | * @create 2017-11-29 9:07 10 | **/ 11 | public class ByteBufUtil { 12 | 13 | public static byte[] copyByteBuf(ByteBuf byteBuf){ 14 | byte[] bytes = new byte[byteBuf.readableBytes()]; 15 | byteBuf.readBytes(bytes); 16 | return bytes; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/util/IdWorker.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.util; 2 | 3 | import java.lang.management.ManagementFactory; 4 | import java.net.InetAddress; 5 | import java.net.NetworkInterface; 6 | 7 | /** 8 | * 全局id生成器 9 | * 10 | * @author lxr 11 | * @create 2017-11-23 19:38 12 | **/ 13 | public class IdWorker { 14 | 15 | // 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动) 16 | private final static long twepoch = 1288834974657L; 17 | // 机器标识位数 18 | private final static long workerIdBits = 5L; 19 | // 数据中心标识位数 20 | private final static long datacenterIdBits = 5L; 21 | // 机器ID最大值 22 | private final static long maxWorkerId = -1L ^ (-1L << workerIdBits); 23 | // 数据中心ID最大值 24 | private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); 25 | // 毫秒内自增位 26 | private final static long sequenceBits = 12L; 27 | // 机器ID偏左移12位 28 | private final static long workerIdShift = sequenceBits; 29 | // 数据中心ID左移17位 30 | private final static long datacenterIdShift = sequenceBits + workerIdBits; 31 | // 时间毫秒左移22位 32 | private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; 33 | 34 | private final static long sequenceMask = -1L ^ (-1L << sequenceBits); 35 | /* 上次生产id时间戳 */ 36 | private static long lastTimestamp = -1L; 37 | // 0,并发控制 38 | private long sequence = 0L; 39 | 40 | private final long workerId; 41 | // 数据标识id部分 42 | private final long datacenterId; 43 | 44 | public IdWorker(){ 45 | this.datacenterId = getDatacenterId(maxDatacenterId); 46 | this.workerId = getMaxWorkerId(datacenterId, maxWorkerId); 47 | } 48 | /** 49 | * @param workerId 50 | * 工作机器ID 51 | * @param datacenterId 52 | * 序列号 53 | */ 54 | public IdWorker(long workerId, long datacenterId) { 55 | if (workerId > maxWorkerId || workerId < 0) { 56 | throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); 57 | } 58 | if (datacenterId > maxDatacenterId || datacenterId < 0) { 59 | throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); 60 | } 61 | this.workerId = workerId; 62 | this.datacenterId = datacenterId; 63 | } 64 | /** 65 | * 获取下一个ID 66 | * 67 | * @return 68 | */ 69 | public synchronized long nextId() { 70 | long timestamp = timeGen(); 71 | if (timestamp < lastTimestamp) { 72 | throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); 73 | } 74 | 75 | if (lastTimestamp == timestamp) { 76 | // 当前毫秒内,则+1 77 | sequence = (sequence + 1) & sequenceMask; 78 | if (sequence == 0) { 79 | // 当前毫秒内计数满了,则等待下一秒 80 | timestamp = tilNextMillis(lastTimestamp); 81 | } 82 | } else { 83 | sequence = 0L; 84 | } 85 | lastTimestamp = timestamp; 86 | // ID偏移组合生成最终的ID,并返回ID 87 | long nextId = ((timestamp - twepoch) << timestampLeftShift) 88 | | (datacenterId << datacenterIdShift) 89 | | (workerId << workerIdShift) | sequence; 90 | 91 | return nextId; 92 | } 93 | 94 | private long tilNextMillis(final long lastTimestamp) { 95 | long timestamp = this.timeGen(); 96 | while (timestamp <= lastTimestamp) { 97 | timestamp = this.timeGen(); 98 | } 99 | return timestamp; 100 | } 101 | 102 | private long timeGen() { 103 | return System.currentTimeMillis(); 104 | } 105 | 106 | /** 107 | *

108 | * 获取 maxWorkerId 109 | *

110 | */ 111 | protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) { 112 | StringBuffer mpid = new StringBuffer(); 113 | mpid.append(datacenterId); 114 | String name = ManagementFactory.getRuntimeMXBean().getName(); 115 | if (!name.isEmpty()) { 116 | /* 117 | * GET jvmPid 118 | */ 119 | mpid.append(name.split("@")[0]); 120 | } 121 | /* 122 | * MAC + PID 的 hashcode 获取16个低位 123 | */ 124 | return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1); 125 | } 126 | 127 | /** 128 | *

129 | * 数据标识id部分 130 | *

131 | */ 132 | protected static long getDatacenterId(long maxDatacenterId) { 133 | long id = 0L; 134 | try { 135 | InetAddress ip = InetAddress.getLocalHost(); 136 | NetworkInterface network = NetworkInterface.getByInetAddress(ip); 137 | if (network == null) { 138 | id = 1L; 139 | } else { 140 | byte[] mac = network.getHardwareAddress(); 141 | id = ((0x000000FF & (long) mac[mac.length - 1]) 142 | | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6; 143 | id = id % (maxDatacenterId + 1); 144 | } 145 | } catch (Exception e) { 146 | System.out.println(" getDatacenterId: " + e.getMessage()); 147 | } 148 | return id; 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/util/MessageId.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.util; 2 | 3 | import ch.qos.logback.core.net.SyslogOutputStream; 4 | 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | 7 | /** 8 | * 生成messgaeId 9 | * 10 | * @author lxr 11 | * @create 2017-11-23 19:43 12 | **/ 13 | public class MessageId { 14 | 15 | private static AtomicInteger index = new AtomicInteger(1); 16 | /** 17 | * 获取messageId 18 | * @return id 19 | */ 20 | public static int messageId(){ 21 | for (;;) { 22 | int current = index.get(); 23 | int next = (current >= Integer.MAX_VALUE ? 0: current + 1); 24 | if (index.compareAndSet(current, next)) { 25 | return current; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/util/RemotingUtil.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.util; 2 | 3 | /* 4 | * Licensed to the Apache Software Foundation (ASF) under one or more 5 | * contributor license agreements. See the NOTICE file distributed with 6 | * this work for additional information regarding copyright ownership. 7 | * The ASF licenses this file to You under the Apache License, Version 2.0 8 | * (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import java.io.IOException; 24 | import java.lang.reflect.Method; 25 | import java.net.*; 26 | import java.nio.channels.Selector; 27 | import java.nio.channels.SocketChannel; 28 | import java.nio.channels.spi.SelectorProvider; 29 | import java.util.ArrayList; 30 | import java.util.Enumeration; 31 | 32 | public class RemotingUtil { 33 | public static final String OS_NAME = System.getProperty("os.name"); 34 | 35 | private static final Logger log = LoggerFactory.getLogger(RemotingUtil.class); 36 | private static boolean isLinuxPlatform = false; 37 | private static boolean isWindowsPlatform = false; 38 | 39 | static { 40 | if (OS_NAME != null && OS_NAME.toLowerCase().contains("linux")) { 41 | isLinuxPlatform = true; 42 | } 43 | 44 | if (OS_NAME != null && OS_NAME.toLowerCase().contains("windows")) { 45 | isWindowsPlatform = true; 46 | } 47 | } 48 | 49 | public static boolean isWindowsPlatform() { 50 | return isWindowsPlatform; 51 | } 52 | 53 | public static Selector openSelector() throws IOException { 54 | Selector result = null; 55 | 56 | if (isLinuxPlatform()) { 57 | try { 58 | final Class providerClazz = Class.forName("sun.nio.ch.EPollSelectorProvider"); 59 | if (providerClazz != null) { 60 | try { 61 | final Method method = providerClazz.getMethod("provider"); 62 | if (method != null) { 63 | final SelectorProvider selectorProvider = (SelectorProvider) method.invoke(null); 64 | if (selectorProvider != null) { 65 | result = selectorProvider.openSelector(); 66 | } 67 | } 68 | } catch (final Exception e) { 69 | log.warn("Open ePoll Selector for linux platform exception", e); 70 | } 71 | } 72 | } catch (final Exception e) { 73 | // ignore 74 | } 75 | } 76 | 77 | if (result == null) { 78 | result = Selector.open(); 79 | } 80 | 81 | return result; 82 | } 83 | 84 | public static boolean isLinuxPlatform() { 85 | return isLinuxPlatform; 86 | } 87 | 88 | public static String getLocalAddress() { 89 | try { 90 | // Traversal Network interface to get the first non-loopback and non-private address 91 | Enumeration enumeration = NetworkInterface.getNetworkInterfaces(); 92 | ArrayList ipv4Result = new ArrayList(); 93 | ArrayList ipv6Result = new ArrayList(); 94 | while (enumeration.hasMoreElements()) { 95 | final NetworkInterface networkInterface = enumeration.nextElement(); 96 | final Enumeration en = networkInterface.getInetAddresses(); 97 | while (en.hasMoreElements()) { 98 | final InetAddress address = en.nextElement(); 99 | if (!address.isLoopbackAddress()) { 100 | if (address instanceof Inet6Address) { 101 | ipv6Result.add(normalizeHostAddress(address)); 102 | } else { 103 | ipv4Result.add(normalizeHostAddress(address)); 104 | } 105 | } 106 | } 107 | } 108 | 109 | // prefer ipv4 110 | if (!ipv4Result.isEmpty()) { 111 | for (String ip : ipv4Result) { 112 | if (ip.startsWith("127.0") || ip.startsWith("192.168")) { 113 | continue; 114 | } 115 | 116 | return ip; 117 | } 118 | 119 | return ipv4Result.get(ipv4Result.size() - 1); 120 | } else if (!ipv6Result.isEmpty()) { 121 | return ipv6Result.get(0); 122 | } 123 | //If failed to find,fall back to localhost 124 | final InetAddress localHost = InetAddress.getLocalHost(); 125 | return normalizeHostAddress(localHost); 126 | } catch (Exception e) { 127 | log.error("Failed to obtain local address", e); 128 | } 129 | 130 | return null; 131 | } 132 | 133 | public static String normalizeHostAddress(final InetAddress localHost) { 134 | if (localHost instanceof Inet6Address) { 135 | return "[" + localHost.getHostAddress() + "]"; 136 | } else { 137 | return localHost.getHostAddress(); 138 | } 139 | } 140 | 141 | public static SocketAddress string2SocketAddress(final String addr) { 142 | String[] s = addr.split(":"); 143 | InetSocketAddress isa = new InetSocketAddress(s[0], Integer.parseInt(s[1])); 144 | return isa; 145 | } 146 | 147 | public static String socketAddress2String(final SocketAddress addr) { 148 | StringBuilder sb = new StringBuilder(); 149 | InetSocketAddress inetSocketAddress = (InetSocketAddress) addr; 150 | sb.append(inetSocketAddress.getAddress().getHostAddress()); 151 | sb.append(":"); 152 | sb.append(inetSocketAddress.getPort()); 153 | return sb.toString(); 154 | } 155 | 156 | public static SocketChannel connect(SocketAddress remote) { 157 | return connect(remote, 1000 * 5); 158 | } 159 | 160 | public static SocketChannel connect(SocketAddress remote, final int timeoutMillis) { 161 | SocketChannel sc = null; 162 | try { 163 | sc = SocketChannel.open(); 164 | sc.configureBlocking(true); 165 | sc.socket().setSoLinger(false, -1); 166 | sc.socket().setTcpNoDelay(true); 167 | sc.socket().setReceiveBufferSize(1024 * 64); 168 | sc.socket().setSendBufferSize(1024 * 64); 169 | sc.socket().connect(remote, timeoutMillis); 170 | sc.configureBlocking(false); 171 | return sc; 172 | } catch (Exception e) { 173 | if (sc != null) { 174 | try { 175 | sc.close(); 176 | } catch (IOException e1) { 177 | e1.printStackTrace(); 178 | } 179 | } 180 | } 181 | 182 | return null; 183 | } 184 | 185 | 186 | } 187 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/util/SpringBeanUtils.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.util; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | /** 8 | * 获取 spring当前bean 9 | * 10 | * @author lxr 11 | * @create 2017-11-21 19:56 12 | **/ 13 | @Component 14 | public class SpringBeanUtils implements ApplicationContextAware { 15 | 16 | private static ApplicationContext applicationContext; 17 | 18 | @Override 19 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 20 | if(SpringBeanUtils.applicationContext == null) { 21 | SpringBeanUtils.applicationContext = applicationContext; 22 | } 23 | } 24 | 25 | //获取applicationContext 26 | public static ApplicationContext getApplicationContext() { 27 | return applicationContext; 28 | } 29 | 30 | //通过name获取 Bean. 31 | public static Object getBean(String name){ 32 | return getApplicationContext().getBean(name); 33 | } 34 | 35 | //通过class获取Bean. 36 | public static T getBean(Class clazz){ 37 | return getApplicationContext().getBean(clazz); 38 | } 39 | 40 | //通过name,以及Clazz返回指定的Bean 41 | public static T getBean(String name,Class clazz){ 42 | return getApplicationContext().getBean(name, clazz); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /iot_push_common/src/main/java/com/lxr/iot/zookeeper/ZkStateListener.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.zookeeper; 2 | 3 | import org.apache.curator.framework.CuratorFramework; 4 | import org.apache.curator.framework.state.ConnectionState; 5 | 6 | /** 7 | * zookeeper 监听 8 | * 9 | * @author lxr 10 | * @create 2017-11-16 9:44 11 | **/ 12 | public interface ZkStateListener { 13 | 14 | default void connectedEvent(CuratorFramework curator, ConnectionState state) { 15 | } 16 | 17 | default void ReconnectedEvent(CuratorFramework curator, ConnectionState state) { 18 | } 19 | 20 | default void lostEvent(CuratorFramework curator, ConnectionState state) { 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /iot_push_server/iot_push_server.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /iot_push_server/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | iot_push 5 | com.lxr.iot 6 | 1.0-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | iot_push_server 11 | jar 12 | 13 | iot_push_server 14 | http://maven.apache.org 15 | 16 | 17 | 18 | 19 | 20 | com.lxr.iot 21 | iot_push_common 22 | 1.0-SNAPSHOT 23 | 24 | 25 | junit 26 | junit 27 | 3.8.1 28 | test 29 | 30 | 31 | 32 | 33 | 34 | org.apache.maven.plugins 35 | maven-compiler-plugin 36 | 3.3 37 | 38 | 1.8 39 | 1.8 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/auto/InitServer.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.auto; 2 | 3 | import com.lxr.iot.bootstrap.BootstrapServer; 4 | import com.lxr.iot.bootstrap.NettyBootstrapServer; 5 | import com.lxr.iot.properties.InitBean; 6 | 7 | /** 8 | * 初始化服务 9 | * 10 | * @author lxr 11 | * @create 2017-11-29 20:12 12 | **/ 13 | public class InitServer { 14 | 15 | private InitBean serverBean; 16 | 17 | public InitServer(InitBean serverBean) { 18 | this.serverBean = serverBean; 19 | } 20 | 21 | BootstrapServer bootstrapServer; 22 | 23 | public void open(){ 24 | if(serverBean!=null){ 25 | bootstrapServer = new NettyBootstrapServer(); 26 | bootstrapServer.setServerBean(serverBean); 27 | bootstrapServer.start(); 28 | } 29 | } 30 | 31 | 32 | public void close(){ 33 | if(bootstrapServer!=null){ 34 | bootstrapServer.shutdown(); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/auto/ServerAutoConfigure.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.auto; 2 | 3 | import com.lxr.iot.enums.ProtocolEnum; 4 | import com.lxr.iot.properties.InitBean; 5 | import org.apache.commons.lang3.ObjectUtils; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 9 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | /** 14 | * 自动化配置初始化服务 15 | * 16 | * @author lxr 17 | * @create 2017-11-29 19:52 18 | **/ 19 | @Configuration 20 | @ConditionalOnClass 21 | @EnableConfigurationProperties({InitBean.class}) 22 | public class ServerAutoConfigure { 23 | 24 | 25 | private static final int _BLACKLOG = 1024; 26 | 27 | private static final int CPU =Runtime.getRuntime().availableProcessors(); 28 | 29 | private static final int SEDU_DAY =10; 30 | 31 | private static final int TIMEOUT =120; 32 | 33 | private static final int BUF_SIZE=10*1024*1024; 34 | 35 | 36 | public ServerAutoConfigure(){ 37 | 38 | } 39 | 40 | 41 | 42 | @Bean(initMethod = "open", destroyMethod = "close") 43 | @ConditionalOnMissingBean 44 | public InitServer initServer(InitBean serverBean){ 45 | if(!ObjectUtils.allNotNull(serverBean.getPort(),serverBean.getServerName())){ 46 | throw new NullPointerException("not set port"); 47 | } 48 | if(serverBean.getBacklog()<1){ 49 | serverBean.setBacklog(_BLACKLOG); 50 | } 51 | if(serverBean.getBossThread()<1){ 52 | serverBean.setBossThread(CPU); 53 | } 54 | if(serverBean.getInitalDelay()<0){ 55 | serverBean.setInitalDelay(SEDU_DAY); 56 | } 57 | if(serverBean.getPeriod()<1){ 58 | serverBean.setPeriod(SEDU_DAY); 59 | } 60 | if(serverBean.getHeart()<1){ 61 | serverBean.setHeart(TIMEOUT); 62 | } 63 | if(serverBean.getRevbuf()<1){ 64 | serverBean.setRevbuf(BUF_SIZE); 65 | } 66 | if(serverBean.getWorkThread()<1){ 67 | serverBean.setWorkThread(CPU*2); 68 | } 69 | if(serverBean.getProtocol()==null){ 70 | serverBean.setProtocol(ProtocolEnum.MQTT); 71 | } 72 | return new InitServer(serverBean); 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/AbstractBootstrapServer.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap; 2 | 3 | import com.lxr.iot.bootstrap.coder.ByteBufToWebSocketFrameEncoder; 4 | import com.lxr.iot.bootstrap.coder.WebSocketFrameToByteBufDecoder; 5 | import com.lxr.iot.properties.InitBean; 6 | import com.lxr.iot.ssl.SecureSocketSslContextFactory; 7 | import com.lxr.iot.util.SpringBeanUtils; 8 | import io.netty.channel.ChannelPipeline; 9 | import io.netty.handler.codec.http.HttpObjectAggregator; 10 | import io.netty.handler.codec.http.HttpServerCodec; 11 | import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; 12 | import io.netty.handler.codec.mqtt.MqttDecoder; 13 | import io.netty.handler.codec.mqtt.MqttEncoder; 14 | import io.netty.handler.ssl.SslHandler; 15 | import io.netty.handler.timeout.IdleStateHandler; 16 | import org.apache.commons.lang3.ObjectUtils; 17 | import org.jboss.netty.util.internal.SystemPropertyUtil; 18 | 19 | import javax.net.ssl.KeyManagerFactory; 20 | import javax.net.ssl.SSLContext; 21 | import javax.net.ssl.SSLEngine; 22 | import java.security.KeyStore; 23 | import java.util.concurrent.ExecutorService; 24 | import java.util.concurrent.Executors; 25 | 26 | /** 27 | * 抽象类 负责加载edec handler 28 | * 29 | * @author lxr 30 | * @create 2017-11-20 13:46 31 | **/ 32 | public abstract class AbstractBootstrapServer implements BootstrapServer { 33 | 34 | 35 | 36 | private String PROTOCOL = "TLS"; 37 | 38 | private SSLContext SERVER_CONTEXT; 39 | 40 | private static final String MQTT_CSV_LIST = "mqtt, mqttv3.1, mqttv3.1.1"; 41 | 42 | 43 | /** 44 | * 45 | * @param channelPipeline channelPipeline 46 | * @param serverBean 服务配置参数 47 | */ 48 | protected void initHandler(ChannelPipeline channelPipeline, InitBean serverBean){ 49 | if(serverBean.isSsl()){ 50 | if(!ObjectUtils.allNotNull(serverBean.getJksCertificatePassword(),serverBean.getJksFile(),serverBean.getJksStorePassword())){ 51 | throw new NullPointerException("SSL file and password is null"); 52 | } 53 | initSsl(serverBean); 54 | SSLEngine engine = 55 | SERVER_CONTEXT.createSSLEngine(); 56 | engine.setUseClientMode(false); 57 | channelPipeline.addLast("ssl", new SslHandler(engine)); 58 | } 59 | 60 | intProtocolHandler(channelPipeline,serverBean); 61 | channelPipeline.addLast(new IdleStateHandler(serverBean.getHeart(),0,0)); 62 | channelPipeline.addLast( SpringBeanUtils.getBean(serverBean.getMqttHander())); 63 | 64 | } 65 | 66 | private void intProtocolHandler(ChannelPipeline channelPipeline,InitBean serverBean){ 67 | switch (serverBean.getProtocol()){ 68 | case MQTT: 69 | channelPipeline.addLast("encoder", MqttEncoder.INSTANCE); 70 | channelPipeline.addLast("decoder", new MqttDecoder()); 71 | break; 72 | case MQTT_WS_MQTT: 73 | channelPipeline.addLast("httpCode", new HttpServerCodec()); 74 | channelPipeline.addLast("aggregator", new HttpObjectAggregator(65536)); 75 | channelPipeline.addLast("webSocketHandler", 76 | new WebSocketServerProtocolHandler("/", MQTT_CSV_LIST)); 77 | channelPipeline.addLast("wsDecoder", new WebSocketFrameToByteBufDecoder()); 78 | channelPipeline.addLast("wsEncoder", new ByteBufToWebSocketFrameEncoder()); 79 | channelPipeline.addLast("decoder", new MqttDecoder()); 80 | channelPipeline.addLast("encoder", MqttEncoder.INSTANCE); 81 | break; 82 | case MQTT_WS_PAHO: 83 | channelPipeline.addLast("httpCode", new HttpServerCodec()); 84 | channelPipeline.addLast("aggregator", new HttpObjectAggregator(65536)); 85 | channelPipeline.addLast("webSocketHandler", 86 | new WebSocketServerProtocolHandler("/mqtt", MQTT_CSV_LIST)); 87 | channelPipeline.addLast("wsDecoder", new WebSocketFrameToByteBufDecoder()); 88 | channelPipeline.addLast("wsEncoder", new ByteBufToWebSocketFrameEncoder()); 89 | channelPipeline.addLast("decoder", new MqttDecoder()); 90 | channelPipeline.addLast("encoder", MqttEncoder.INSTANCE); 91 | break; 92 | } 93 | } 94 | 95 | private void initSsl(InitBean serverBean){ 96 | ExecutorService executorService = Executors.newCachedThreadPool(); 97 | executorService.submit(() -> {}); 98 | String algorithm = SystemPropertyUtil.get("ssl.KeyManagerFactory.algorithm"); 99 | if (algorithm == null) { 100 | algorithm = "SunX509"; 101 | } 102 | SSLContext serverContext; 103 | try { 104 | // 105 | KeyStore ks = KeyStore.getInstance("JKS"); 106 | ks.load( SecureSocketSslContextFactory.class.getResourceAsStream(serverBean.getJksFile()), 107 | serverBean.getJksStorePassword().toCharArray()); 108 | KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); 109 | kmf.init(ks,serverBean.getJksCertificatePassword().toCharArray()); 110 | serverContext = SSLContext.getInstance(PROTOCOL); 111 | serverContext.init(kmf.getKeyManagers(), null, null); 112 | } catch (Exception e) { 113 | throw new Error( 114 | "Failed to initialize the server-side SSLContext", e); 115 | } 116 | SERVER_CONTEXT = serverContext; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/BaseApi.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap; 2 | 3 | import javax.validation.constraints.NotNull; 4 | import java.util.Arrays; 5 | import java.util.Optional; 6 | import java.util.function.Consumer; 7 | import java.util.function.Predicate; 8 | 9 | /** 10 | * 逻辑操作封装 11 | * 12 | * @author lxr 13 | * @create 2017-11-27 9:12 14 | **/ 15 | public interface BaseApi { 16 | 17 | 18 | default void doIfElse(T t, Predicate predicate, Consumer consumer){ 19 | if(t!=null){ 20 | if(predicate.test(t)){ 21 | consumer.accept(t); 22 | } 23 | } 24 | } 25 | 26 | 27 | default void doIfElse(T t, Predicate predicate, Consumer consumer, Consumer consumer2){ 28 | if(t!=null){ 29 | if(predicate.test(t)){ 30 | consumer.accept(t); 31 | } 32 | else{ 33 | consumer2.accept(t); 34 | } 35 | } 36 | } 37 | default boolean doIf(T t, Predicate... predicates){ 38 | if(t!=null){ 39 | for(Predicate p:predicates){ 40 | if(!p.test(t)){ 41 | return false; 42 | } 43 | } 44 | return true; 45 | } 46 | return false; 47 | } 48 | 49 | default void doIfAnd(T t, Consumer consumer2, Predicate... predicates){ 50 | boolean flag =true; 51 | if(t!=null){ 52 | for(Predicate p:predicates){ 53 | if(!p.test(t)){ 54 | flag= false; 55 | break; 56 | } 57 | } 58 | } 59 | if(flag){ 60 | consumer2.accept(t); 61 | } 62 | } 63 | 64 | default void doIfAnd1(@NotNull T t, @NotNull Consumer consumer2, @NotNull Predicate... predicates){ 65 | Predicate one = predicates[0]; 66 | int l; 67 | if((l=predicates.length)>1){ 68 | for(int i=1;i topics); 28 | 29 | 30 | void loginSuccess(Channel channel, String deviceId, MqttConnectMessage mqttConnectMessage); 31 | 32 | void publishSuccess(Channel channel, MqttPublishMessage mqttPublishMessage); 33 | 34 | void closeSuccess(String deviceId,boolean isDisconnect); 35 | 36 | void sendWillMsg(WillMeaasge willMeaasge); 37 | 38 | String getDeviceId(Channel channel); 39 | 40 | void unsubscribe(String deviceId, List topics1); 41 | 42 | void doPubrel(Channel channel, int mqttMessage); 43 | 44 | void doPubrec(Channel channel, int mqttMessage); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/NettyBootstrapServer.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap; 2 | 3 | import com.lxr.iot.ip.IpUtils; 4 | import com.lxr.iot.properties.InitBean; 5 | import com.lxr.iot.util.RemotingUtil; 6 | import io.netty.bootstrap.ServerBootstrap; 7 | import io.netty.buffer.PooledByteBufAllocator; 8 | import io.netty.channel.ChannelFutureListener; 9 | import io.netty.channel.ChannelInitializer; 10 | import io.netty.channel.ChannelOption; 11 | import io.netty.channel.EventLoopGroup; 12 | import io.netty.channel.epoll.Epoll; 13 | import io.netty.channel.epoll.EpollEventLoopGroup; 14 | import io.netty.channel.epoll.EpollServerSocketChannel; 15 | import io.netty.channel.nio.NioEventLoopGroup; 16 | import io.netty.channel.socket.SocketChannel; 17 | import io.netty.channel.socket.nio.NioServerSocketChannel; 18 | import lombok.Getter; 19 | import lombok.Setter; 20 | 21 | import java.util.concurrent.ThreadFactory; 22 | import java.util.concurrent.atomic.AtomicInteger; 23 | 24 | /** 25 | * netty 服务启动类 26 | * 27 | * @author lxr 28 | * @create 2017-11-18 14:03 29 | **/ 30 | @Setter 31 | @Getter 32 | @lombok.extern.slf4j.Slf4j 33 | public class NettyBootstrapServer extends AbstractBootstrapServer { 34 | 35 | private InitBean serverBean; 36 | 37 | public InitBean getServerBean() { 38 | return serverBean; 39 | } 40 | 41 | public void setServerBean(InitBean serverBean) { 42 | this.serverBean = serverBean; 43 | } 44 | 45 | private EventLoopGroup bossGroup; 46 | 47 | private EventLoopGroup workGroup; 48 | 49 | ServerBootstrap bootstrap=null ;// 启动辅助类 50 | 51 | /** 52 | * 服务开启 53 | */ 54 | public void start() { 55 | initEventPool(); 56 | bootstrap.group(bossGroup, workGroup) 57 | .channel(useEpoll()?EpollServerSocketChannel.class:NioServerSocketChannel.class) 58 | .option(ChannelOption.SO_REUSEADDR, serverBean.isReuseaddr()) 59 | .option(ChannelOption.SO_BACKLOG, serverBean.getBacklog()) 60 | .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) 61 | .option(ChannelOption.SO_RCVBUF, serverBean.getRevbuf()) 62 | .childHandler(new ChannelInitializer() { 63 | protected void initChannel(SocketChannel ch) throws Exception { 64 | initHandler(ch.pipeline(),serverBean); 65 | } 66 | }) 67 | .childOption(ChannelOption.TCP_NODELAY, serverBean.isTcpNodelay()) 68 | .childOption(ChannelOption.SO_KEEPALIVE, serverBean.isKeepalive()) 69 | .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 70 | bootstrap.bind(IpUtils.getHost(),serverBean.getPort()).addListener((ChannelFutureListener) channelFuture -> { 71 | if (channelFuture.isSuccess()) 72 | log.info("服务端启动成功【" + IpUtils.getHost() + ":" + serverBean.getPort() + "】"); 73 | else 74 | log.info("服务端启动失败【" + IpUtils.getHost() + ":" + serverBean.getPort() + "】"); 75 | }); 76 | } 77 | /** 78 | * 初始化EnentPool 参数 79 | */ 80 | private void initEventPool(){ 81 | bootstrap= new ServerBootstrap(); 82 | if(useEpoll()){ 83 | bossGroup = new EpollEventLoopGroup(serverBean.getBossThread(), new ThreadFactory() { 84 | private AtomicInteger index = new AtomicInteger(0); 85 | 86 | public Thread newThread(Runnable r) { 87 | return new Thread(r, "LINUX_BOSS_" + index.incrementAndGet()); 88 | } 89 | }); 90 | workGroup = new EpollEventLoopGroup(serverBean.getWorkThread(), new ThreadFactory() { 91 | private AtomicInteger index = new AtomicInteger(0); 92 | 93 | public Thread newThread(Runnable r) { 94 | return new Thread(r, "LINUX_WORK_" + index.incrementAndGet()); 95 | } 96 | }); 97 | 98 | } 99 | else { 100 | bossGroup = new NioEventLoopGroup(serverBean.getBossThread(), new ThreadFactory() { 101 | private AtomicInteger index = new AtomicInteger(0); 102 | 103 | public Thread newThread(Runnable r) { 104 | return new Thread(r, "BOSS_" + index.incrementAndGet()); 105 | } 106 | }); 107 | workGroup = new NioEventLoopGroup(serverBean.getWorkThread(), new ThreadFactory() { 108 | private AtomicInteger index = new AtomicInteger(0); 109 | 110 | public Thread newThread(Runnable r) { 111 | return new Thread(r, "WORK_" + index.incrementAndGet()); 112 | } 113 | }); 114 | } 115 | } 116 | 117 | /** 118 | * 关闭资源 119 | */ 120 | public void shutdown() { 121 | if(workGroup!=null && bossGroup!=null ){ 122 | try { 123 | bossGroup.shutdownGracefully().sync();// 优雅关闭 124 | workGroup.shutdownGracefully().sync(); 125 | } catch (InterruptedException e) { 126 | log.info("服务端关闭资源失败【" + IpUtils.getHost() + ":" + serverBean.getPort() + "】"); 127 | } 128 | } 129 | } 130 | 131 | private boolean useEpoll() { 132 | return RemotingUtil.isLinuxPlatform() 133 | && Epoll.isAvailable(); 134 | } 135 | } 136 | 137 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/ScheduledPool.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap; 2 | 3 | import com.lxr.iot.pool.Scheduled; 4 | import com.lxr.iot.properties.InitBean; 5 | import org.springframework.stereotype.Service; 6 | 7 | import java.util.concurrent.*; 8 | 9 | /** 10 | * 定时任务 11 | * 12 | * @author lxr 13 | * @create 2017-12-14 10:39 14 | **/ 15 | @Service 16 | public class ScheduledPool implements Scheduled { 17 | 18 | private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(20); 19 | 20 | 21 | private final InitBean serverBean; 22 | 23 | public ScheduledPool(InitBean serverBean) { 24 | this.serverBean = serverBean; 25 | } 26 | 27 | public ScheduledFuture submit(Runnable runnable){ 28 | int initalDelay = serverBean.getInitalDelay(); 29 | int period = serverBean.getPeriod(); 30 | return scheduledExecutorService.scheduleAtFixedRate(runnable, initalDelay, period, TimeUnit.SECONDS); 31 | } 32 | 33 | // public static void main(String[] a) throws InterruptedException { 34 | // ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(100); 35 | // ScheduledFuture schedule = scheduledExecutorService.schedule(new Runnable() { 36 | // @Override 37 | // public void run() { 38 | // System.out.print("123"); 39 | // } 40 | // }, 2, TimeUnit.SECONDS); 41 | // } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/bean/MqttChannel.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.bean; 2 | 3 | import com.lxr.iot.enums.SessionStatus; 4 | import com.lxr.iot.enums.SubStatus; 5 | import io.netty.channel.Channel; 6 | import io.netty.util.AttributeKey; 7 | import lombok.Builder; 8 | import lombok.Getter; 9 | import lombok.Setter; 10 | 11 | import java.util.Optional; 12 | import java.util.Set; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | import java.util.concurrent.atomic.AtomicInteger; 15 | 16 | /** 17 | * channel 封装类 18 | * 19 | * @author lxr 20 | * @create 2017-11-21 14:04 21 | **/ 22 | @Builder 23 | @Getter 24 | @Setter 25 | public class MqttChannel { 26 | 27 | private transient volatile Channel channel; 28 | 29 | 30 | private String deviceId; 31 | 32 | 33 | private boolean isWill; 34 | 35 | 36 | private volatile SubStatus subStatus; // 是否订阅过主题 37 | 38 | 39 | private Set topic ; 40 | 41 | 42 | 43 | private volatile SessionStatus sessionStatus; // 在线 - 离线 44 | 45 | 46 | 47 | private volatile boolean cleanSession; // 当为 true 时 channel close 时 从缓存中删除 此channel 48 | 49 | 50 | 51 | 52 | private ConcurrentHashMap message ; // messageId - message(qos1) // 待确认消息 53 | 54 | 55 | 56 | private AtomicInteger index ; 57 | 58 | 59 | public int messageId(){ 60 | for (;;) { 61 | int current = index.get(); 62 | int next = (current >= Short.MAX_VALUE ? 0: current + 1); 63 | if (index.compareAndSet(current, next)) { 64 | return current; 65 | } 66 | } 67 | } 68 | 69 | private Set receive; 70 | 71 | public void addRecevice(int messageId){ 72 | receive.add(messageId); 73 | } 74 | 75 | public boolean checkRecevice(int messageId){ 76 | return receive.contains(messageId); 77 | } 78 | 79 | public boolean removeRecevice(int messageId){ 80 | return receive.remove(messageId); 81 | } 82 | 83 | 84 | public void addSendMqttMessage(int messageId,SendMqttMessage msg){ 85 | message.put(messageId,msg); 86 | } 87 | 88 | 89 | public SendMqttMessage getSendMqttMessage(int messageId){ 90 | return message.get(messageId); 91 | } 92 | 93 | 94 | public void removeSendMqttMessage(int messageId){ 95 | message.remove(messageId); 96 | } 97 | 98 | 99 | /** 100 | * 判断当前channel 是否登录过 101 | * @return 102 | */ 103 | public boolean isLogin(){ 104 | return Optional.ofNullable(this.channel).map(channel1 -> { 105 | AttributeKey _login = AttributeKey.valueOf("login"); 106 | return channel1.isActive() && channel1.hasAttr(_login); 107 | }).orElse(false); 108 | } 109 | 110 | /** 111 | * 非正常关闭 112 | */ 113 | public void close(){ 114 | Optional.ofNullable(this.channel).ifPresent(channel1 -> channel1.close()); 115 | } 116 | 117 | /** 118 | * 通道是否活跃 119 | * @return 120 | */ 121 | public boolean isActive(){ 122 | return channel!=null&&this.channel.isActive(); 123 | } 124 | 125 | 126 | 127 | public boolean addTopic(Set topics){ 128 | return topic.addAll(topics); 129 | } 130 | 131 | 132 | } 133 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/bean/RetainMessage.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.bean; 2 | 3 | import io.netty.handler.codec.mqtt.MqttQoS; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | /** 8 | * 保留消息 9 | * @author lxr 10 | * @create 2017-11-24 16:06 11 | **/ 12 | @Builder 13 | @Data 14 | public class RetainMessage { 15 | 16 | private byte[] byteBuf; 17 | 18 | private MqttQoS qoS; 19 | public String getString(){ 20 | return new String(byteBuf); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/bean/SendMqttMessage.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.bean; 2 | 3 | import com.lxr.iot.enums.ConfirmStatus; 4 | import io.netty.channel.Channel; 5 | import io.netty.handler.codec.mqtt.MqttQoS; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | 9 | /** 10 | * mqtt 消息 11 | * 12 | * @author lxr 13 | * @create 2018-01-17 19:54 14 | **/ 15 | @Builder 16 | @Data 17 | public class SendMqttMessage { 18 | 19 | private int messageId; 20 | 21 | private Channel channel; 22 | 23 | private volatile ConfirmStatus confirmStatus; 24 | 25 | private long time; 26 | 27 | private byte[] byteBuf; 28 | 29 | private boolean isRetain; 30 | 31 | private MqttQoS qos; 32 | 33 | private String topic; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/bean/SessionMessage.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.bean; 2 | 3 | import io.netty.handler.codec.mqtt.MqttQoS; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | /** 8 | * Session会话数据保存 9 | * 10 | * @author lxr 11 | * @create 2017-11-27 19:28 12 | **/ 13 | @Builder 14 | @Data 15 | 16 | public class SessionMessage { 17 | 18 | private byte[] byteBuf; 19 | 20 | private MqttQoS qoS; 21 | 22 | private String topic; 23 | 24 | 25 | public String getString(){ 26 | return new String(byteBuf); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/bean/WillMeaasge.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.bean; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | /** 7 | * 遗嘱消息 8 | * 9 | * @author lxr 10 | * @create 2017-11-23 15:08 11 | **/ 12 | @Builder 13 | @Data 14 | public class WillMeaasge { 15 | 16 | private String willTopic; 17 | 18 | private String willMessage; 19 | 20 | 21 | private boolean isRetain; 22 | 23 | private int qos; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/channel/AbstractChannelService.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.channel; 2 | 3 | import com.google.common.cache.Cache; 4 | import com.google.common.cache.CacheBuilder; 5 | import com.lxr.iot.bootstrap.bean.MqttChannel; 6 | import com.lxr.iot.bootstrap.bean.RetainMessage; 7 | import com.lxr.iot.bootstrap.BaseApi; 8 | import com.lxr.iot.bootstrap.ChannelService; 9 | import com.lxr.iot.bootstrap.channel.cache.CacheMap; 10 | import com.lxr.iot.bootstrap.queue.MessageTransfer; 11 | import io.netty.channel.Channel; 12 | import io.netty.util.AttributeKey; 13 | import lombok.extern.slf4j.Slf4j; 14 | import org.apache.commons.lang3.StringUtils; 15 | 16 | import java.util.Collection; 17 | import java.util.Optional; 18 | import java.util.concurrent.ConcurrentHashMap; 19 | import java.util.concurrent.ConcurrentLinkedQueue; 20 | import java.util.concurrent.ExecutorService; 21 | import java.util.concurrent.Executors; 22 | 23 | /** 24 | * 抽象类 25 | * 26 | * @author lxr 27 | * @create 2017-12-12 20:01 28 | **/ 29 | @Slf4j 30 | public abstract class AbstractChannelService extends PublishApiSevice implements ChannelService ,BaseApi { 31 | 32 | 33 | protected AttributeKey _login = AttributeKey.valueOf("login"); 34 | 35 | protected AttributeKey _deviceId = AttributeKey.valueOf("deviceId"); 36 | 37 | protected static char SPLITOR = '/'; 38 | 39 | protected ExecutorService executorService =Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*2); 40 | 41 | 42 | protected static CacheMap cacheMap= new CacheMap<>(); 43 | 44 | 45 | protected static ConcurrentHashMap mqttChannels = new ConcurrentHashMap<>(); // deviceId - mqChannel 登录 46 | 47 | 48 | protected static ConcurrentHashMap> retain = new ConcurrentHashMap<>(); // topic - 保留消息 49 | 50 | 51 | 52 | protected static Cache> mqttChannelCache = CacheBuilder.newBuilder().maximumSize(100).build(); 53 | 54 | public AbstractChannelService(MessageTransfer transfer ) { 55 | super(transfer); 56 | } 57 | 58 | 59 | protected Collection getChannels(String topic,TopicFilter topicFilter){ 60 | try { 61 | return mqttChannelCache.get(topic, () -> topicFilter.filter(topic)); 62 | } catch (Exception e) { 63 | log.info(String.format("guava cache key topic【%s】 channel value== null ",topic)); 64 | } 65 | return null; 66 | } 67 | 68 | 69 | @FunctionalInterface 70 | interface TopicFilter{ 71 | Collection filter(String topic); 72 | } 73 | 74 | protected boolean deleteChannel(String topic,MqttChannel mqttChannel){ 75 | return Optional.ofNullable(topic).map(s -> { 76 | mqttChannelCache.invalidate(s); 77 | return cacheMap.delete(getTopic(s),mqttChannel); 78 | }).orElse(false); 79 | } 80 | 81 | protected boolean addChannel(String topic,MqttChannel mqttChannel) 82 | { 83 | return Optional.ofNullable(topic).map(s -> { 84 | mqttChannelCache.invalidate(s); 85 | return cacheMap.putData(getTopic(s),mqttChannel); 86 | }).orElse(false); 87 | } 88 | 89 | /** 90 | * 获取channel 91 | */ 92 | public MqttChannel getMqttChannel(String deviceId){ 93 | return Optional.ofNullable(deviceId).map(s -> mqttChannels.get(s)) 94 | .orElse(null); 95 | 96 | } 97 | 98 | /** 99 | * 获取channelId 100 | */ 101 | public String getDeviceId(Channel channel){ 102 | return Optional.ofNullable(channel).map( channel1->channel1.attr(_deviceId).get()) 103 | .orElse(null); 104 | } 105 | 106 | 107 | 108 | protected String[] getTopic(String topic) { 109 | return Optional.ofNullable(topic).map(s -> 110 | StringUtils.split(topic,SPLITOR) 111 | ).orElse(null); 112 | } 113 | 114 | 115 | 116 | } 117 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/channel/ClientSessionService.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.channel; 2 | 3 | import com.lxr.iot.bootstrap.bean.SessionMessage; 4 | import org.springframework.stereotype.Service; 5 | 6 | import java.util.Optional; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | import java.util.concurrent.ConcurrentLinkedQueue; 9 | 10 | /** 11 | * 会话保留处理 12 | * 13 | * @author lxr 14 | * @create 2017-11-23 11:21 15 | **/ 16 | @Service 17 | public class ClientSessionService { 18 | 19 | private static ConcurrentHashMap> queueSession = new ConcurrentHashMap<>(); // 连接关闭后 保留此session 数据 deviceId 20 | 21 | 22 | public void saveSessionMsg(String deviceId, SessionMessage sessionMessage) { 23 | ConcurrentLinkedQueue sessionMessages = queueSession.getOrDefault(deviceId, new ConcurrentLinkedQueue<>()); 24 | boolean flag; 25 | do{ 26 | flag = sessionMessages.add(sessionMessage); 27 | } 28 | while (!flag); 29 | queueSession.put(deviceId,sessionMessages); 30 | } 31 | 32 | public ConcurrentLinkedQueue getByteBuf(String deviceId){ 33 | return Optional.ofNullable(deviceId).map(s -> queueSession.get(s)) 34 | .orElse(null); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/channel/PublishApiSevice.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.channel; 2 | 3 | import com.lxr.iot.bootstrap.bean.MqttChannel; 4 | import com.lxr.iot.bootstrap.bean.SendMqttMessage; 5 | import com.lxr.iot.bootstrap.bean.WillMeaasge; 6 | import com.lxr.iot.bootstrap.queue.MessageTransfer; 7 | import com.lxr.iot.enums.ConfirmStatus; 8 | import io.netty.buffer.Unpooled; 9 | import io.netty.channel.Channel; 10 | import io.netty.handler.codec.mqtt.*; 11 | import lombok.extern.slf4j.Slf4j; 12 | 13 | /** 14 | * 发送消息以及确认 15 | * 16 | * @author lxr 17 | * @create 2017-11-24 11:04 18 | **/ 19 | @Slf4j 20 | public class PublishApiSevice { 21 | 22 | private final MessageTransfer transfer ; 23 | 24 | public PublishApiSevice(MessageTransfer transfer) { 25 | this.transfer = transfer; 26 | } 27 | 28 | 29 | /** 30 | * 写入遗嘱消息 31 | */ 32 | protected void writeWillMsg(MqttChannel mqttChannel, WillMeaasge willMeaasge) { 33 | // dup保证消息可靠传输,默认为0,只占用一个字节,表示第一次发送。不能用于检测消息重复发送等 34 | switch (willMeaasge.getQos()){ 35 | case 0: // qos0 36 | sendQos0Msg(mqttChannel.getChannel(),willMeaasge.getWillTopic(),willMeaasge.getWillMessage().getBytes()); 37 | break; 38 | case 1: // qos1 39 | sendQosConfirmMsg(MqttQoS.AT_LEAST_ONCE,mqttChannel,willMeaasge.getWillTopic(),willMeaasge.getWillMessage().getBytes()); 40 | break; 41 | case 2: // qos2 42 | sendQosConfirmMsg(MqttQoS.EXACTLY_ONCE,mqttChannel,willMeaasge.getWillTopic(),willMeaasge.getWillMessage().getBytes()); 43 | break; 44 | } 45 | 46 | 47 | } 48 | 49 | protected void sendQosConfirmMsg(MqttQoS qos, MqttChannel mqttChannel, String topic, byte[] bytes) { 50 | if(mqttChannel.isLogin()){ 51 | int messageId = mqttChannel.messageId(); 52 | switch (qos){ 53 | case AT_LEAST_ONCE: 54 | mqttChannel.addSendMqttMessage(messageId,sendQos1Msg(mqttChannel.getChannel(),topic,false,bytes,messageId)); 55 | break; 56 | case EXACTLY_ONCE: 57 | mqttChannel.addSendMqttMessage(messageId,sendQos2Msg(mqttChannel.getChannel(),topic,false,bytes,messageId)); 58 | break; 59 | } 60 | } 61 | 62 | } 63 | 64 | 65 | /** 66 | * 发送 qos1 类的消息 67 | */ 68 | private SendMqttMessage sendQos1Msg(Channel channel, String topic,boolean isDup, byte[] byteBuf,int messageId){ 69 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH,isDup, MqttQoS.AT_LEAST_ONCE,false,0); 70 | MqttPublishVariableHeader mqttPublishVariableHeader = new MqttPublishVariableHeader(topic,messageId ); 71 | MqttPublishMessage mqttPublishMessage = new MqttPublishMessage(mqttFixedHeader,mqttPublishVariableHeader, Unpooled.wrappedBuffer(byteBuf)); 72 | channel.writeAndFlush(mqttPublishMessage); 73 | return addQueue(channel,messageId,topic,byteBuf,MqttQoS.AT_LEAST_ONCE,ConfirmStatus.PUB); 74 | } 75 | 76 | 77 | 78 | /** 79 | * 发送 qos0 类的消息 byte 80 | */ 81 | protected void sendQos0Msg(Channel channel, String topic, byte[] byteBuf){ 82 | if(channel!=null){ 83 | sendQos0Msg(channel,topic,byteBuf,0); 84 | } 85 | } 86 | private void sendQos0Msg(Channel channel, String topic, byte[] byteBuf,int messageId){ 87 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH,false, MqttQoS.AT_MOST_ONCE,false,0); 88 | MqttPublishVariableHeader mqttPublishVariableHeader = new MqttPublishVariableHeader(topic,messageId ); 89 | MqttPublishMessage mqttPublishMessage = new MqttPublishMessage(mqttFixedHeader,mqttPublishVariableHeader,Unpooled.wrappedBuffer(byteBuf)); 90 | channel.writeAndFlush(mqttPublishMessage); 91 | } 92 | 93 | 94 | 95 | 96 | private SendMqttMessage sendQos2Msg(Channel channel, String topic,boolean isDup, byte[] byteBuf, int messageId) { 97 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH,isDup, MqttQoS.EXACTLY_ONCE,false,0); 98 | MqttPublishVariableHeader mqttPublishVariableHeader = new MqttPublishVariableHeader(topic,messageId ); 99 | MqttPublishMessage mqttPublishMessage = new MqttPublishMessage(mqttFixedHeader,mqttPublishVariableHeader, Unpooled.wrappedBuffer(byteBuf)); 100 | channel.writeAndFlush(mqttPublishMessage); 101 | return addQueue(channel,messageId,topic,byteBuf,MqttQoS.EXACTLY_ONCE,ConfirmStatus.PUB); 102 | } 103 | 104 | 105 | /** 106 | * 发送qos1 publish 确认消息 107 | */ 108 | protected void sendPubBack(Channel channel,int messageId){ 109 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBACK,false, MqttQoS.AT_MOST_ONCE,false,0x02); 110 | MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(messageId); 111 | MqttPubAckMessage mqttPubAckMessage = new MqttPubAckMessage(mqttFixedHeader,from); 112 | channel.writeAndFlush(mqttPubAckMessage); 113 | } 114 | 115 | 116 | /** 117 | * 发送qos2 publish 确认消息 第一步 118 | */ 119 | protected void sendPubRec( MqttChannel mqttChannel,int messageId){ 120 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBREC,false, MqttQoS.AT_LEAST_ONCE,false,0x02); 121 | MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(messageId); 122 | MqttPubAckMessage mqttPubAckMessage = new MqttPubAckMessage(mqttFixedHeader,from); 123 | Channel channel = mqttChannel.getChannel(); 124 | channel.writeAndFlush(mqttPubAckMessage); 125 | SendMqttMessage sendMqttMessage = addQueue(channel, messageId, null, null, null, ConfirmStatus.PUBREC); 126 | mqttChannel.addSendMqttMessage(messageId,sendMqttMessage); 127 | } 128 | 129 | /** 130 | * 发送qos2 publish 确认消息 第二步 131 | */ 132 | protected void sendPubRel(Channel channel,boolean isDup,int messageId){ 133 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBREL,isDup, MqttQoS.AT_LEAST_ONCE,false,0x02); 134 | MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(messageId); 135 | MqttPubAckMessage mqttPubAckMessage = new MqttPubAckMessage(mqttFixedHeader,from); 136 | channel.writeAndFlush(mqttPubAckMessage); 137 | } 138 | 139 | /** 140 | * 发送qos2 publish 确认消息 第三步 141 | */ 142 | protected void sendToPubComp(Channel channel,int messageId){ 143 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBCOMP,false, MqttQoS.AT_MOST_ONCE,false,0x02); 144 | MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(messageId); 145 | MqttPubAckMessage mqttPubAckMessage = new MqttPubAckMessage(mqttFixedHeader,from); 146 | channel.writeAndFlush(mqttPubAckMessage); 147 | } 148 | 149 | private SendMqttMessage addQueue(Channel channel,int messageId,String topic,byte[] datas,MqttQoS mqttQoS,ConfirmStatus confirmStatus){ 150 | SendMqttMessage build = SendMqttMessage.builder(). 151 | channel(channel). 152 | confirmStatus(confirmStatus). 153 | messageId(messageId) 154 | .topic(topic) 155 | .qos(mqttQoS) 156 | .byteBuf(datas) 157 | .build(); 158 | transfer.addQueue(build); 159 | return build; 160 | } 161 | 162 | 163 | } 164 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/channel/WillService.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.channel; 2 | 3 | import com.lxr.iot.bootstrap.bean.WillMeaasge; 4 | import com.lxr.iot.bootstrap.BaseApi; 5 | import com.lxr.iot.bootstrap.ChannelService; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | import lombok.Setter; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.concurrent.ConcurrentHashMap; 15 | 16 | /** 17 | * 遗嘱消息处理 18 | * 19 | * @author lxr 20 | * @create 2017-11-23 11:20 21 | **/ 22 | @Slf4j 23 | @Component 24 | @Setter 25 | @Getter 26 | @NoArgsConstructor 27 | public class WillService implements BaseApi { 28 | 29 | 30 | @Autowired 31 | ChannelService channelService; 32 | 33 | private static ConcurrentHashMap willMeaasges = new ConcurrentHashMap<>(); // deviceid -WillMeaasge 34 | 35 | 36 | 37 | /** 38 | * 保存遗嘱消息 39 | */ 40 | public void save(String deviceid, WillMeaasge build) { 41 | willMeaasges.put(deviceid,build); // 替换旧的 42 | } 43 | 44 | 45 | public void doSend( String deviceId) { // 客户端断开连接后 开启遗嘱消息发送 46 | if(StringUtils.isNotBlank(deviceId)&&(willMeaasges.get(deviceId))!=null){ 47 | WillMeaasge willMeaasge = willMeaasges.get(deviceId); 48 | channelService.sendWillMsg(willMeaasge); // 发送遗嘱消息 49 | if(!willMeaasge.isRetain()){ // 移除 50 | willMeaasges.remove(deviceId); 51 | log.info("deviceId will message["+willMeaasge.getWillMessage()+"] is removed"); 52 | } 53 | } 54 | } 55 | 56 | /** 57 | * 删除遗嘱消息 58 | */ 59 | public void del(String deviceid ) {willMeaasges.remove(deviceid);} 60 | } 61 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/channel/cache/CacheMap.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.channel.cache; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | import java.util.concurrent.CopyOnWriteArrayList; 9 | 10 | /** 11 | * 缓存操作 12 | * 13 | * @author lxr 14 | * @create 2017-12-02 13:48 15 | **/ 16 | @Slf4j 17 | public class CacheMap { 18 | 19 | private ConcurrentHashMap> datas = new ConcurrentHashMap<>(); 20 | 21 | public boolean putData(K[] topic, V v){ 22 | if(topic.length==1){ 23 | Node kvNode = buildOne(topic[0], v); 24 | if(kvNode!=null && kvNode.topic.equals(topic[0])){ 25 | return true; 26 | } 27 | } 28 | else{ 29 | Node kvNode = buildOne(topic[0], null); 30 | for(int i=1;i kvNode = datas.get(ks[0]); 48 | for(int i=1;i getData(K[] ks){ 57 | if(ks.length==1){ 58 | return datas.get(ks[0]).get(); 59 | } 60 | else{ 61 | Node node = datas.get(ks[0]); 62 | if(node!=null){ 63 | List all = new ArrayList<>(); 64 | all.addAll(node.get()); 65 | for(int i=1;i buildOne(K k,V v){ 79 | 80 | Node node = this.datas.computeIfAbsent(k, key -> { 81 | Node kObjectNode = new Node<>(k); 82 | return kObjectNode; 83 | }); 84 | if(v!=null){ 85 | node.put(v); 86 | } 87 | return node; 88 | } 89 | 90 | 91 | 92 | class Node{ 93 | 94 | private final K topic; 95 | 96 | 97 | private volatile ConcurrentHashMap> map =new ConcurrentHashMap<>() ; 98 | 99 | 100 | List vs = new CopyOnWriteArrayList<>(); 101 | 102 | 103 | public K getTopic() {return topic;} 104 | 105 | Node(K topic) { 106 | this.topic = topic; 107 | } 108 | 109 | public boolean delValue(V v){ 110 | return vs.remove(v); 111 | } 112 | 113 | public Node putNextValue(K k,V v){ 114 | Node kvNode = map.computeIfAbsent(k, key -> { 115 | Node node = new Node<>(k); 116 | return node; 117 | }); 118 | if(v!=null){ 119 | kvNode.put(v); 120 | } 121 | return kvNode; 122 | } 123 | 124 | 125 | public Node getNext(K k){ 126 | return map.get(k); 127 | } 128 | 129 | 130 | public boolean put(V v){ 131 | return vs.add(v); 132 | } 133 | 134 | 135 | public List get(){ 136 | return vs; 137 | } 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/coder/ByteBufToWebSocketFrameEncoder.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.coder; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.MessageToMessageEncoder; 6 | import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * ByteBufToWebSocketFrameEncoder 12 | * 13 | * @author lxr 14 | * @create 2017-11-20 13:46 15 | **/ 16 | public class ByteBufToWebSocketFrameEncoder extends MessageToMessageEncoder { 17 | 18 | 19 | @Override 20 | protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List out) throws Exception { 21 | if (byteBuf == null) { 22 | return; 23 | } 24 | BinaryWebSocketFrame result = new BinaryWebSocketFrame(); 25 | result.content().writeBytes(byteBuf); 26 | out.add(result); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/coder/WebSocketFrameToByteBufDecoder.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.coder; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.MessageToMessageDecoder; 6 | import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * WebSocketFrameToByteBufDecoder 12 | * 13 | * @author lxr 14 | * @create 2017-11-20 13:46 15 | **/ 16 | public class WebSocketFrameToByteBufDecoder extends MessageToMessageDecoder { 17 | 18 | @Override 19 | protected void decode(ChannelHandlerContext channelHandlerContext, BinaryWebSocketFrame wsFrame, List out) throws Exception { 20 | ByteBuf buf = wsFrame.content(); 21 | buf.retain(); 22 | out.add(buf); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/handler/DefaultMqttHandler.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.handler; 2 | 3 | import com.lxr.iot.bootstrap.ChannelService; 4 | import com.lxr.iot.bootstrap.bean.MqttChannel; 5 | import com.lxr.iot.exception.NoFindHandlerException; 6 | import com.lxr.iot.mqtt.MqttHander; 7 | import com.lxr.iot.mqtt.MqttHandlerIntf; 8 | import com.lxr.iot.mqtt.ServerMqttHandlerService; 9 | import com.lxr.iot.properties.ConnectOptions; 10 | import io.netty.channel.Channel; 11 | import io.netty.channel.ChannelHandler; 12 | import io.netty.channel.ChannelHandlerContext; 13 | import io.netty.handler.codec.mqtt.*; 14 | import lombok.extern.slf4j.Slf4j; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.stereotype.Component; 17 | 18 | /** 19 | * 默认 mqtthandler处理 20 | * 21 | * @author lxr 22 | * @create 2017-11-20 13:58 23 | **/ 24 | 25 | @ChannelHandler.Sharable 26 | @Slf4j 27 | @Component 28 | public class DefaultMqttHandler extends MqttHander { 29 | 30 | 31 | private final MqttHandlerIntf mqttHandlerApi; 32 | 33 | @Autowired 34 | ChannelService channelService; 35 | 36 | 37 | public DefaultMqttHandler(MqttHandlerIntf mqttHandlerApi) { 38 | super(mqttHandlerApi); 39 | this.mqttHandlerApi = mqttHandlerApi; 40 | } 41 | 42 | @Override 43 | public void doMessage(ChannelHandlerContext channelHandlerContext, MqttMessage mqttMessage) { 44 | Channel channel = channelHandlerContext.channel(); 45 | ServerMqttHandlerService serverMqttHandlerService; 46 | if(mqttHandlerApi instanceof ServerMqttHandlerService){ 47 | serverMqttHandlerService =(ServerMqttHandlerService)mqttHandlerApi; 48 | } 49 | else{ 50 | throw new NoFindHandlerException("server handler 不匹配"); 51 | } 52 | MqttFixedHeader mqttFixedHeader = mqttMessage.fixedHeader(); 53 | if(mqttFixedHeader.messageType().equals(MqttMessageType.CONNECT)){ 54 | if(!serverMqttHandlerService.login(channel, (MqttConnectMessage) mqttMessage)){ 55 | channel.close(); 56 | } 57 | return ; 58 | } 59 | MqttChannel mqttChannel = channelService.getMqttChannel(channelService.getDeviceId(channel)); 60 | if(mqttChannel!=null && mqttChannel.isLogin()){ 61 | switch (mqttFixedHeader.messageType()){ 62 | case PUBLISH: 63 | serverMqttHandlerService.publish(channel, (MqttPublishMessage) mqttMessage); 64 | break; 65 | case SUBSCRIBE: 66 | serverMqttHandlerService.subscribe(channel, (MqttSubscribeMessage) mqttMessage); 67 | break; 68 | case PINGREQ: 69 | serverMqttHandlerService.pong(channel); 70 | break; 71 | case DISCONNECT: 72 | serverMqttHandlerService.disconnect(channel); 73 | break; 74 | case UNSUBSCRIBE: 75 | serverMqttHandlerService.unsubscribe(channel,(MqttUnsubscribeMessage)mqttMessage); 76 | break; 77 | case PUBACK: 78 | mqttHandlerApi.puback(channel,mqttMessage); 79 | break; 80 | case PUBREC: 81 | mqttHandlerApi.pubrec(channel,mqttMessage); 82 | break; 83 | case PUBREL: 84 | mqttHandlerApi.pubrel(channel,mqttMessage); 85 | break; 86 | case PUBCOMP: 87 | mqttHandlerApi.pubcomp(channel,mqttMessage); 88 | break; 89 | default: 90 | break; 91 | } 92 | } 93 | } 94 | 95 | 96 | 97 | @Override 98 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 99 | log.info("【DefaultMqttHandler:channelActive】"+ctx.channel().remoteAddress().toString()+"链接成功"); 100 | } 101 | 102 | @Override 103 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 104 | log.error("exception",cause); 105 | mqttHandlerApi.close(ctx.channel()); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/queue/DisruptorMessageStarter.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.queue; 2 | 3 | import com.lmax.disruptor.BlockingWaitStrategy; 4 | import com.lmax.disruptor.EventHandler; 5 | import com.lmax.disruptor.RingBuffer; 6 | import com.lmax.disruptor.YieldingWaitStrategy; 7 | import com.lmax.disruptor.dsl.Disruptor; 8 | import com.lmax.disruptor.dsl.ProducerType; 9 | import com.lmax.disruptor.util.DaemonThreadFactory; 10 | import org.springframework.beans.factory.DisposableBean; 11 | import org.springframework.stereotype.Component; 12 | 13 | 14 | @Component 15 | public class DisruptorMessageStarter implements MessageStarter , DisposableBean { 16 | 17 | 18 | private Disruptor disruptor = new Disruptor<>(MessageEvent::new, 19 | 1024, DaemonThreadFactory.INSTANCE, ProducerType.MULTI, 20 | new BlockingWaitStrategy()); 21 | 22 | private final EventHandler eventHandler; 23 | 24 | public DisruptorMessageStarter(EventHandler eventHandler) { 25 | this.eventHandler = eventHandler; 26 | disruptor.handleEventsWith(eventHandler); 27 | disruptor.start(); 28 | } 29 | 30 | 31 | @Override 32 | public RingBuffer getRingBuffer() { 33 | return disruptor.getRingBuffer(); 34 | } 35 | 36 | @Override 37 | public void shutdown() { 38 | disruptor.shutdown(); 39 | } 40 | 41 | 42 | @Override 43 | public void destroy() throws Exception { 44 | shutdown(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/queue/MessageEvent.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.queue; 2 | 3 | import com.lxr.iot.bootstrap.bean.SendMqttMessage; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class MessageEvent { 8 | 9 | private SendMqttMessage message; 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/queue/MessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.queue; 2 | 3 | 4 | import com.lmax.disruptor.EventHandler; 5 | import com.lxr.iot.bootstrap.bean.SendMqttMessage; 6 | import io.netty.buffer.Unpooled; 7 | import io.netty.channel.Channel; 8 | import io.netty.handler.codec.mqtt.*; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | public class MessageHandler implements EventHandler { 13 | 14 | private final MessageTransfer messageTransfer; 15 | 16 | public MessageHandler(MessageTransfer messageTransfer) { 17 | this.messageTransfer = messageTransfer; 18 | } 19 | 20 | 21 | @Override 22 | public void onEvent(MessageEvent messageEvent, long l, boolean b) throws Exception { 23 | SendMqttMessage message=messageEvent.getMessage(); 24 | if(message.getChannel().isActive()){ 25 | switch (message.getConfirmStatus()){ 26 | case PUB: 27 | pubMessage(message.getChannel(),message); 28 | break; 29 | case PUBREL: 30 | sendAck(MqttMessageType.PUBREL,message); 31 | break; 32 | case PUBREC: 33 | sendAck(MqttMessageType.PUBREC,message); 34 | break; 35 | } 36 | } 37 | } 38 | 39 | private void pubMessage(Channel channel, SendMqttMessage mqttMessage){ 40 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH,true, mqttMessage.getQos(),mqttMessage.isRetain(),0); 41 | MqttPublishVariableHeader mqttPublishVariableHeader = new MqttPublishVariableHeader(mqttMessage.getTopic(),mqttMessage.getMessageId()); 42 | MqttPublishMessage mqttPublishMessage = new MqttPublishMessage(mqttFixedHeader,mqttPublishVariableHeader, Unpooled.wrappedBuffer(mqttMessage.getByteBuf())); 43 | channel.writeAndFlush(mqttPublishMessage); 44 | } 45 | 46 | protected void sendAck(MqttMessageType type,SendMqttMessage mqttMessage){ 47 | MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(type,true, MqttQoS.AT_LEAST_ONCE,false,0x02); 48 | MqttMessageIdVariableHeader from = MqttMessageIdVariableHeader.from(mqttMessage.getMessageId()); 49 | MqttPubAckMessage mqttPubAckMessage = new MqttPubAckMessage(mqttFixedHeader,from); 50 | mqttMessage.getChannel().writeAndFlush(mqttPubAckMessage); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/queue/MessageStarter.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.queue; 2 | 3 | import com.lmax.disruptor.RingBuffer; 4 | 5 | public interface MessageStarter { 6 | 7 | RingBuffer getRingBuffer(); 8 | 9 | void shutdown(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /iot_push_server/src/main/java/com/lxr/iot/bootstrap/queue/MessageTransfer.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.bootstrap.queue; 2 | 3 | import com.lmax.disruptor.RingBuffer; 4 | import com.lxr.iot.bootstrap.bean.SendMqttMessage; 5 | import io.netty.channel.Channel; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Component; 8 | import reactor.core.Disposable; 9 | import reactor.core.publisher.Mono; 10 | 11 | import java.time.Duration; 12 | import java.util.Optional; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | 15 | @Component 16 | public class MessageTransfer { 17 | 18 | private ConcurrentHashMap> concurrentHashMap = new ConcurrentHashMap<>(); 19 | 20 | @Autowired 21 | private MessageStarter messageStarter; 22 | 23 | public void addQueue(SendMqttMessage sendMqttMessage){ 24 | RingBuffer ringBuffer= messageStarter.getRingBuffer(); 25 | ConcurrentHashMap qos=concurrentHashMap.computeIfAbsent(sendMqttMessage.getChannel().id().toString(),(key)->{ 26 | ConcurrentHashMap map=new ConcurrentHashMap<>(); 27 | return map; 28 | }); 29 | qos.put(sendMqttMessage.getMessageId(), 30 | Mono.fromRunnable(()->{ 31 | ringBuffer.publishEvent((event, sequence) -> event.setMessage(sendMqttMessage)); 32 | qos.remove(sendMqttMessage.getMessageId()); 33 | }) 34 | .delaySubscription(Duration.ofSeconds(10)).subscribe()); 35 | } 36 | 37 | public void removeQueue(Channel channel,Integer messageId){ 38 | Optional.ofNullable(concurrentHashMap.get(channel.id().toString())) 39 | .ifPresent(map-> Optional.ofNullable(map.get(messageId)) 40 | .ifPresent(disposable -> { 41 | disposable.dispose(); 42 | map.remove(messageId); 43 | })); 44 | } 45 | 46 | 47 | 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /iot_push_server/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.lxr.iot.auto.ServerAutoConfigure -------------------------------------------------------------------------------- /iot_push_server_starter_test/iot_push_server_starter.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /iot_push_server_starter_test/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | iot_push 5 | com.lxr.iot 6 | 1.0-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | iot_push_server_starter 11 | jar 12 | 13 | iot_push_server_starter 14 | http://maven.apache.org 15 | 16 | 17 | UTF-8 18 | 19 | 20 | 21 | 22 | com.lxr.iot 23 | iot_push_server 24 | 1.0-SNAPSHOT 25 | 26 | 27 | junit 28 | junit 29 | 3.8.1 30 | test 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-compiler-plugin 38 | 3.3 39 | 40 | 1.8 41 | 1.8 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-maven-plugin 47 | 48 | com.lxr.iot.ServerApplication 49 | 50 | 51 | 52 | 53 | repackage 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /iot_push_server_starter_test/src/main/java/com/lxr/iot/DefaultAutoService.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot; 2 | 3 | import com.lxr.iot.bootstrap.BaseAuthService; 4 | import org.springframework.stereotype.Service; 5 | 6 | /** 7 | * @author lxr 8 | * @create 2018-02-09 14:58 9 | **/ 10 | 11 | @Service 12 | public class DefaultAutoService implements BaseAuthService{ 13 | @Override 14 | public boolean authorized(String username, String password) { 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /iot_push_server_starter_test/src/main/java/com/lxr/iot/ServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | import org.springframework.context.ConfigurableApplicationContext; 5 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 6 | 7 | import static org.springframework.boot.SpringApplication.run; 8 | 9 | /** 10 | * 启动类 11 | * 12 | * @author lxr 13 | * @create 2017-11-18 13:54 14 | **/ 15 | @SpringBootApplication 16 | @EnableAspectJAutoProxy //注解开启对aspectJ的支持 17 | public class ServerApplication { 18 | public static void main(String[] args) { 19 | ConfigurableApplicationContext run = run(ServerApplication.class, args); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /iot_push_server_starter_test/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | 2 | lxr: 3 | iot: 4 | server: 5 | serverName: test #服务名 6 | port: 1884 #端口 7 | keepalive: true #Socket参数,连接保活,默认值为False。启用该功能时,TCP会主动探测空闲连接的有效性。可以将此功能视为TCP的心跳机制,需要注意的是:默认的心跳间隔是7200s即2小时。Netty默认关闭该功能 8 | reuseaddr: true #地址复用,默认值False。有四种情况可以使用:(1).当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你希望启动的程序的socket2要占用该地址和端口,比如重启服务且保持先前端口。(2).有多块网卡或用IP Alias技术的机器在同一端口启动多个进程,但每个进程绑定的本地IP地址不能相同。(3).单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。(4).完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。 9 | tcpNodelay: true #TCP参数,立即发送数据,默认值为Ture(Netty默认为True而操作系统默认为False)。该值设置Nagle算法的启用,改算法将小的碎片数据连接成更大的报文来最小化所发送的报文的数量,如果需要发送一些较小的报文,则需要禁用该算法。Netty默认禁用该算法,从而最小化报文传输延时。 10 | sndbuf: 10485760 # Socket参数,TCP数据发送缓冲区大小。 11 | revbuf: 10485760 # Socket参数,TCP数据接收缓冲区大小。 12 | heart: 180 # 读超时时间 13 | backlog: 1024 #  Socket参数,服务端接受连接的队列长度,如果队列已满,客户端连接将被拒绝 14 | ssl: false # 使用ssl加密 15 | mqttHander: com.lxr.iot.bootstrap.handler.DefaultMqttHandler # 默认处理 16 | initalDelay: 10 # mqtt qos1 qos2 消息 重发延迟 17 | protocol: MQTT # MQTT MQTT_WS_MQTT(mqtt.js) MQTT_WS_PAHO(paho.js) 18 | period: 10 # mqtt qos1 qos2 消息 重发周期 19 | jksFile: /securesocket.jks # ssl 加密 jks文件地址 20 | jksStorePassword: mu$tch8ng3 # 读取jks密码 21 | jksCertificatePassword: inc0rrect # 读取私钥密码 22 | logging: 23 | level: 24 | io.netty: debug -------------------------------------------------------------------------------- /iot_push_server_starter_test/src/main/resources/securesocket.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ssqq1lxr/iot_push/f65d6ef642c7fb8da329c01957d2adab7765fdde/iot_push_server_starter_test/src/main/resources/securesocket.jks -------------------------------------------------------------------------------- /iot_push_test/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | iot_push 5 | com.lxr.iot 6 | 1.0-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | iot_push_test 11 | jar 12 | 13 | iot_push_test 14 | http://maven.apache.org 15 | 16 | 17 | UTF-8 18 | 19 | 20 | 21 | 22 | com.lxr.iot 23 | iot_push_client 24 | 1.0-SNAPSHOT 25 | 26 | 27 | com.lxr.iot 28 | iot_push_common 29 | 1.0-SNAPSHOT 30 | 31 | 32 | junit 33 | junit 34 | 3.8.1 35 | test 36 | 37 | 38 | org.eclipse.paho 39 | org.eclipse.paho.client.mqttv3 40 | 1.1.1 41 | 42 | 43 | 44 | 45 | 46 | org.apache.maven.plugins 47 | maven-compiler-plugin 48 | 3.3 49 | 50 | 1.8 51 | 1.8 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /iot_push_test/src/main/java/com/lxr/iot/client/MyListener.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.client; 2 | 3 | import com.lxr.iot.auto.MqttListener; 4 | import com.lxr.iot.auto.MqttMessageListener; 5 | import io.netty.handler.codec.mqtt.MqttQoS; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | * @author lxr 11 | * @create 2018-01-12 15:14 12 | **/ 13 | @Slf4j 14 | @Service 15 | @MqttMessageListener(qos = MqttQoS.AT_LEAST_ONCE,topic = "/t1/t2") 16 | public class MyListener implements MqttListener{ 17 | @Override 18 | public void callBack(String topic, String msg) { 19 | log.info("============================="+topic+msg); 20 | } 21 | 22 | @Override 23 | public void callThrowable(Throwable e) { 24 | log.info("exception",e); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iot_push_test/src/main/java/com/lxr/iot/client/main.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.client; 2 | 3 | import com.lxr.iot.auto.MqttListener; 4 | import com.lxr.iot.bootstrap.MqttProducer; 5 | import com.lxr.iot.bootstrap.Producer; 6 | import com.lxr.iot.properties.ConnectOptions; 7 | 8 | /** 9 | * 测试 10 | * 11 | * @author lxr 12 | * @create 2018-01-10 10:09 13 | **/ 14 | public class main { 15 | 16 | public static void main(String[] strings) throws InterruptedException { 17 | Producer producer = new MqttProducer(); 18 | ConnectOptions connectOptions = new ConnectOptions(); 19 | connectOptions.setBacklog(1024); 20 | connectOptions.setConnectTime(1000l); 21 | connectOptions.setSsl(false); 22 | connectOptions.setServerIp("192.168.91.1"); 23 | connectOptions.setPort(1884); 24 | connectOptions.setBossThread(1); 25 | connectOptions.setWorkThread(8); 26 | connectOptions.setMinPeriod(10); 27 | connectOptions.setRevbuf(1024); 28 | connectOptions.setSndbuf(1024); 29 | connectOptions.setTcpNodelay(true); 30 | connectOptions.setKeepalive(true); 31 | ConnectOptions.MqttOpntions mqttOpntions = new ConnectOptions.MqttOpntions(); 32 | mqttOpntions.setClientIdentifier("111"); 33 | mqttOpntions.setHasPassword(false); 34 | mqttOpntions.setHasPassword(false); 35 | mqttOpntions.setClientIdentifier("client-2"); 36 | connectOptions.setMqtt(mqttOpntions); 37 | producer.setMqttListener(new MqttListener() { 38 | @Override 39 | public void callBack(String topic, String msg) { 40 | System.out.print("========================================"+topic+msg); 41 | } 42 | @Override 43 | public void callThrowable(Throwable e) { 44 | 45 | } 46 | }); 47 | producer.connect(connectOptions); 48 | // producer.sub(SubMessage.builder().qos(MqttQoS.AT_LEAST_ONCE).topic("/t1/t2").build()); 49 | producer.pub("/test","123123",2); 50 | producer.pub("/haha","123213123",2); 51 | Thread.sleep(1000000l); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /iot_push_test/src/main/java/com/lxr/iot/paho/MqttClientProducerTest.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.paho; 2 | 3 | import org.eclipse.paho.client.mqttv3.*; 4 | import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; 5 | 6 | import java.io.UnsupportedEncodingException; 7 | import java.util.Random; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Executors; 10 | 11 | /** 12 | * 测试客户端 13 | * 14 | * @author lxr 15 | * @create 2017-11-28 14:14 16 | **/ 17 | public class MqttClientProducerTest { 18 | 19 | private static int qos = 1; //只有一次 20 | private static String broker = "tcp://127.0.0.1:8882"; 21 | private static String userName = "smqtt"; 22 | private static String passWord = "smqtt"; 23 | 24 | 25 | static ExecutorService service = Executors.newFixedThreadPool(100); 26 | 27 | private static MqttClient connect(String clientId, String userName, 28 | String password) throws MqttException { 29 | MemoryPersistence persistence = new MemoryPersistence(); 30 | MqttConnectOptions connOpts = new MqttConnectOptions(); 31 | connOpts.setCleanSession(true); 32 | connOpts.setUserName(userName); 33 | connOpts.setPassword(password.toCharArray()); 34 | connOpts.setConnectionTimeout(10); 35 | connOpts.setKeepAliveInterval(20); 36 | // SSLSocketFactory socketFactory = SecureSocketSslContextFactory.getClientContext().getSocketFactory(); 37 | // connOpts.setSocketFactory(socketFactory); 38 | connOpts.setWill("test/lxr", "haha".getBytes(), 0, false); 39 | // String[] uris = {"tcp://10.100.124.206:1883","tcp://10.100.124.207:1883"}; 40 | // connOpts.setServerURIs(uris); //起到负载均衡和高可用的作用 41 | MqttClient mqttClient = new MqttClient(broker, clientId, persistence); 42 | mqttClient.setCallback(new PushCallback("test")); 43 | mqttClient.connect(connOpts); 44 | return mqttClient; 45 | } 46 | 47 | private static void pub(MqttClient sampleClient, String msg, String topic) 48 | throws MqttPersistenceException, MqttException { 49 | MqttMessage message = null; 50 | try { 51 | message = new MqttMessage(msg.getBytes("utf-8")); 52 | } catch (UnsupportedEncodingException e) { 53 | e.printStackTrace(); 54 | } 55 | message.setQos(qos); 56 | message.setRetained(true); 57 | sampleClient.publish(topic, message); 58 | } 59 | 60 | private static void publish(String str, String clientId, String topic) throws MqttException { 61 | MqttClient mqttClient = connect(clientId, userName, passWord); 62 | if (mqttClient != null) { 63 | 64 | for (; ; ) { 65 | pub(mqttClient, str, topic); 66 | try { 67 | Thread.sleep(10); 68 | 69 | } catch (InterruptedException e) { 70 | e.printStackTrace(); 71 | } 72 | 73 | } 74 | } 75 | 76 | // if (mqttClient != null) { 77 | // mqttClient.connect(); 78 | // } 79 | } 80 | 81 | public static void main(String[] args) throws MqttException, InterruptedException { 82 | for(int i =0 ;i<100;i++){ 83 | final int index = i; 84 | service.execute((()->{ 85 | try { 86 | publish("message content"+index, String.valueOf(new Random().nextInt(100000000)), "test/"+index); 87 | } catch (MqttException e) { 88 | e.printStackTrace(); 89 | } 90 | })); 91 | 92 | } 93 | 94 | } 95 | } 96 | 97 | class PushCallback implements MqttCallback { 98 | 99 | 100 | private String threadId; 101 | 102 | public PushCallback(String threadId) { 103 | this.threadId = threadId; 104 | } 105 | 106 | public void connectionLost(Throwable cause) { 107 | 108 | } 109 | 110 | public void deliveryComplete(IMqttDeliveryToken token) { 111 | // System.out.println("deliveryComplete---------" + token.isComplete()); 112 | } 113 | 114 | public void messageArrived(String topic, MqttMessage message) throws Exception { 115 | String msg = new String(message.getPayload()); 116 | System.out.println(topic + " " + msg); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /iot_push_test/src/main/java/com/lxr/iot/paho/comsumer2/MqttClientConsumerTest.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.paho.comsumer2; 2 | 3 | import com.lxr.iot.ssl.SecureSocketSslContextFactory; 4 | import org.eclipse.paho.client.mqttv3.*; 5 | import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; 6 | 7 | import javax.net.ssl.SSLSocketFactory; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Executors; 10 | 11 | /** 12 | * 测试客户端 13 | * 14 | * @author lxr 15 | * @create 2017-11-28 14:14 16 | **/ 17 | public class MqttClientConsumerTest { 18 | 19 | private static int qos = 1; //只有一次 20 | private static String broker = "tcp://127.0.0.1:8882"; 21 | private static String userName = "smqtt"; 22 | private static String passWord = "smqtt"; 23 | static ExecutorService service = Executors.newFixedThreadPool(100); 24 | 25 | 26 | 27 | 28 | private static MqttClient connect(String clientId, String userName, 29 | String password) throws MqttException { 30 | MemoryPersistence persistence = new MemoryPersistence(); 31 | MqttConnectOptions connOpts = new MqttConnectOptions(); 32 | connOpts.setCleanSession(false); 33 | connOpts.setUserName(userName); 34 | connOpts.setPassword(password.toCharArray()); 35 | connOpts.setConnectionTimeout(10); 36 | connOpts.setKeepAliveInterval(20); 37 | // SSLSocketFactory socketFactory = SecureSocketSslContextFactory.getClientContext().getSocketFactory(); 38 | // connOpts.setSocketFactory(socketFactory); 39 | // String[] uris = {"tcp://10.100.124.206:1883","tcp://10.100.124.207:1883"}; 40 | // connOpts.setServerURIs(uris); //起到负载均衡和高可用的作用 41 | MqttClient mqttClient = new MqttClient(broker, clientId, persistence); 42 | mqttClient.setCallback(new PushCallback("test")); 43 | mqttClient.connect(connOpts); 44 | return mqttClient; 45 | } 46 | 47 | private static void sub(MqttClient sampleClient, String msg, String topic) 48 | throws MqttPersistenceException, MqttException { 49 | sampleClient.subscribe(topic); 50 | } 51 | 52 | private static void sub(String str,String clientId,String topic) throws MqttException{ 53 | MqttClient mqttClient = connect(clientId,userName,passWord); 54 | 55 | if (mqttClient != null) { 56 | sub(mqttClient, str, topic); 57 | System.out.println(topic+" " + str); 58 | } 59 | 60 | // if (mqttClient != null) { 61 | // mqttClient.connect(); 62 | // } 63 | } 64 | 65 | public static void main(String[] args) throws MqttException { 66 | for(int i=0;i<100;i++){ 67 | final int index = i; 68 | service.execute(()->{ 69 | try { 70 | sub("message content","client-id-"+index,"test/"+index); 71 | } catch (MqttException e) { 72 | 73 | } 74 | }); 75 | } 76 | } 77 | } 78 | 79 | class PushCallback implements MqttCallback { 80 | 81 | 82 | private String threadId; 83 | public PushCallback(String threadId){ 84 | this.threadId = threadId; 85 | } 86 | 87 | public void connectionLost(Throwable cause) { 88 | 89 | } 90 | 91 | public void deliveryComplete(IMqttDeliveryToken token) { 92 | // System.out.println("deliveryComplete---------" + token.isComplete()); 93 | } 94 | 95 | public void messageArrived(String topic, MqttMessage message) throws Exception { 96 | String msg = new String(message.getPayload()); 97 | System.out.println(topic + " " + msg); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /iot_push_test/src/main/java/com/lxr/iot/paho/consumer/MqttClientConsumerTest1.java: -------------------------------------------------------------------------------- 1 | package com.lxr.iot.paho.consumer; 2 | 3 | import com.lxr.iot.ssl.SecureSocketSslContextFactory; 4 | import org.eclipse.paho.client.mqttv3.*; 5 | import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; 6 | 7 | import javax.net.ssl.SSLSocketFactory; 8 | 9 | /** 10 | * 测试客户端 11 | * 12 | * @author lxr 13 | * @create 2017-11-28 14:14 14 | **/ 15 | public class MqttClientConsumerTest1 { 16 | 17 | private static int qos = 0; //只有一次 18 | private static String broker = "ssl://127.0.0.1:8882"; 19 | private static String userName = "smqtt"; 20 | private static String passWord = "smqtt"; 21 | 22 | 23 | private static MqttClient connect(String clientId, String userName, 24 | String password) throws MqttException { 25 | MemoryPersistence persistence = new MemoryPersistence(); 26 | MqttConnectOptions connOpts = new MqttConnectOptions(); 27 | connOpts.setCleanSession(true); 28 | connOpts.setUserName(userName); 29 | connOpts.setPassword(password.toCharArray()); 30 | connOpts.setConnectionTimeout(10); 31 | connOpts.setKeepAliveInterval(20); 32 | SSLSocketFactory socketFactory = SecureSocketSslContextFactory.getClientContext().getSocketFactory(); 33 | connOpts.setSocketFactory(socketFactory); 34 | // String[] uris = {"tcp://10.100.124.206:1883","tcp://10.100.124.207:1883"}; 35 | // connOpts.setServerURIs(uris); //起到负载均衡和高可用的作用 36 | MqttClient mqttClient = new MqttClient(broker, clientId, persistence); 37 | mqttClient.setCallback(new PushCallback("test1")); 38 | mqttClient.connect(connOpts); 39 | return mqttClient; 40 | } 41 | 42 | private static void sub(MqttClient sampleClient, String msg, String topic) 43 | throws MqttPersistenceException, MqttException { 44 | sampleClient.subscribe(topic); 45 | } 46 | 47 | private static void sub(String str,String clientId,String topic) throws MqttException{ 48 | MqttClient mqttClient = connect(clientId,userName,passWord); 49 | 50 | if (mqttClient != null) { 51 | sub(mqttClient, str, topic); 52 | System.out.println(topic+" " + str); 53 | } 54 | 55 | // if (mqttClient != null) { 56 | // mqttClient.connect(); 57 | // } 58 | } 59 | 60 | public static void main(String[] args) throws MqttException { 61 | sub("message content","client-id-0","/test/haha"); 62 | } 63 | } 64 | 65 | class PushCallback implements MqttCallback { 66 | 67 | 68 | private String threadId; 69 | public PushCallback(String threadId){ 70 | this.threadId = threadId; 71 | } 72 | 73 | public void connectionLost(Throwable cause) { 74 | 75 | } 76 | 77 | public void deliveryComplete(IMqttDeliveryToken token) { 78 | // System.out.println("deliveryComplete---------" + token.isComplete()); 79 | } 80 | 81 | public void messageArrived(String topic, MqttMessage message) throws Exception { 82 | String msg = new String(message.getPayload()); 83 | System.out.println(topic + " " + msg); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /iot_push_test/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | 2 | lxr: 3 | iot: 4 | client: 5 | connectTime: 10 # 链接超时 6 | serverIp: 127.0.0.1 7 | port: 1884 #端口 8 | keepalive: true #Socket参数,连接保活,默认值为False。启用该功能时,TCP会主动探测空闲连接的有效性。可以将此功能视为TCP的心跳机制,需要注意的是:默认的心跳间隔是7200s即2小时。Netty默认关闭该功能 9 | reuseaddr: true #地址复用,默认值False。有四种情况可以使用:(1).当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你希望启动的程序的socket2要占用该地址和端口,比如重启服务且保持先前端口。(2).有多块网卡或用IP Alias技术的机器在同一端口启动多个进程,但每个进程绑定的本地IP地址不能相同。(3).单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。(4).完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。 10 | tcpNodelay: true #TCP参数,立即发送数据,默认值为Ture(Netty默认为True而操作系统默认为False)。该值设置Nagle算法的启用,改算法将小的碎片数据连接成更大的报文来最小化所发送的报文的数量,如果需要发送一些较小的报文,则需要禁用该算法。Netty默认禁用该算法,从而最小化报文传输延时。 11 | sndbuf: 10485760 # Socket参数,TCP数据发送缓冲区大小。 12 | revbuf: 10485760 # Socket参数,TCP数据接收缓冲区大小。 13 | heart: 60 # 读超时时间 14 | backlog: 1024 #  Socket参数,服务端接受连接的队列长度,如果队列已满,客户端连接将被拒绝 15 | ssl: true # 使用ssl加密 16 | initalDelay: 10 # mqtt qos1 qos2 消息 重发延迟 17 | period: 10 # mqtt qos1 qos2 消息 重发周期 18 | jksFile: /securesocket.jks # ssl 加密 jks文件地址 19 | jksStorePassword: mu$tch8ng3 # 读取jks密码 20 | jksCertificatePassword: inc0rrect # 读取私钥密码 21 | workThread: 1 22 | mqtt: 23 | clientIdentifier: client-1 24 | isWillFlag: false 25 | isCleanSession: true 26 | keepAliveTimeSeconds: 3000 27 | hasUserName: false 28 | hasPassword: false 29 | willTopic: /test # isWillFlag = true 时候配置 30 | willMessage: 123123 # isWillFlag = true 时候配置 31 | password: 123 # hasPassword = true 时候配置 32 | userName: 123 # hasUserName = true 时候配置 33 | isWillRetain: true # isWillFlag = true 时候配置 34 | willQos: 0 # isWillFlag = true 时候配置 35 | KeepAliveTime: 10 # 心跳时长 36 | 37 | server: 38 | port: 8989 39 | logging: 40 | level: 41 | io.netty: debug 42 | -------------------------------------------------------------------------------- /iot_push_test/src/main/resources/securesocket.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ssqq1lxr/iot_push/f65d6ef642c7fb8da329c01957d2adab7765fdde/iot_push_test/src/main/resources/securesocket.jks -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.lxr.iot 6 | iot_push 7 | 1.0-SNAPSHOT 8 | 9 | iot_push_common 10 | iot_push_server 11 | iot_push_test 12 | iot_push_client 13 | iot_push_client_starter_test 14 | iot_push_server_starter_test 15 | 16 | pom 17 | 18 | iot_push 19 | http://maven.apache.org 20 | 21 | 22 | UTF-8 23 | ${encoding} 24 | ${encoding} 25 | 1.8 26 | 1.5.3.RELEASE 27 | 28 | 29 | 30 | 31 | --------------------------------------------------------------------------------