add old netty frameWork
This commit is contained in:
49
netty-pad/build.gradle
Normal file
49
netty-pad/build.gradle
Normal file
@@ -0,0 +1,49 @@
|
||||
buildscript {
|
||||
ext {
|
||||
springBootVersion = '1.5.6.RELEASE'
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url 'http://maven.aliyun.com/nexus/content/groups/public/'
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
|
||||
}
|
||||
}
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'org.springframework.boot'
|
||||
|
||||
group 'smartvenue'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
|
||||
repositories {
|
||||
// maven {url 'http://maven.ydd100.cn/repository/maven-public/'}
|
||||
//mavenCentral()
|
||||
maven {
|
||||
url 'http://maven.aliyun.com/nexus/content/groups/public/'
|
||||
}
|
||||
}
|
||||
ext {
|
||||
version = '1.0'
|
||||
springBootVersion = '1.5.4.RELEASE'
|
||||
nettyVersion = '4.1.10.Final'
|
||||
junitVersion = "4.11"
|
||||
jacksonVersion = "2.5.0"
|
||||
}
|
||||
|
||||
|
||||
[compileJava, compileTestJava, javadoc]*.options*.encoding = 'UTF-8'
|
||||
|
||||
dependencies {
|
||||
compile fileTree(include: '*.jar', dir: 'src/libs')
|
||||
compile project(':service')
|
||||
|
||||
compile "io.netty:netty-all:${nettyVersion}"
|
||||
compile 'mysql:mysql-connector-java:6.0.6'
|
||||
|
||||
|
||||
}
|
||||
73
netty-pad/src/main/java/com/NettyApplication.java
Normal file
73
netty-pad/src/main/java/com/NettyApplication.java
Normal file
@@ -0,0 +1,73 @@
|
||||
package com;
|
||||
|
||||
import com.sv.netty.config.SpringContextHolder;
|
||||
import com.sv.netty.netty.BootService;
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@EnableAsync
|
||||
@MapperScan(value = {"com.sv.mapper"})
|
||||
public class NettyApplication {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(NettyApplication.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
logger.debug("应用启动成功...");
|
||||
ConfigurableApplicationContext context = SpringApplication.run(NettyApplication.class, args);
|
||||
SpringContextHolder.setContext(context);
|
||||
//启动netty
|
||||
BootService bootService = (BootService) context.getBean("bootService");
|
||||
bootService.run();
|
||||
|
||||
//启动任务队列,处理
|
||||
// QueueTaskQueueDaemonThread queueTaskQueueDaemonThread = (QueueTaskQueueDaemonThread) context.getBean("queueTaskQueueDaemonThread");
|
||||
// queueTaskQueueDaemonThread.init();
|
||||
}
|
||||
|
||||
private int corePoolSize = 5;//线程池维护线程的最少数量
|
||||
|
||||
private int maxPoolSize = 15;//线程池维护线程的最大数量
|
||||
|
||||
private int queueCapacity = 5; //缓存队列
|
||||
|
||||
private int keepAlive = 60;//允许的空闲时间
|
||||
|
||||
|
||||
@Bean("scheduledExecutorService")
|
||||
public ScheduledExecutorService initScheduledExecutorService() {
|
||||
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(400,
|
||||
new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
|
||||
return executorService;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Executor executor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(corePoolSize);
|
||||
executor.setMaxPoolSize(maxPoolSize);
|
||||
executor.setQueueCapacity(queueCapacity);
|
||||
executor.setThreadNamePrefix("mqExecutor-");
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
|
||||
executor.setKeepAliveSeconds(keepAlive);
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.sv.netty.config;
|
||||
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* 通道类型全局存储
|
||||
*
|
||||
* @author Demon
|
||||
* @since 09/01/2018
|
||||
*/
|
||||
public class ChannelTypeCache {
|
||||
|
||||
public static final Integer TCP = 1;
|
||||
|
||||
public static final Integer WEB_SOCKET = 2;
|
||||
|
||||
/**
|
||||
* 存储用户和通道类型
|
||||
* value: 1Tcp 2WebSocket
|
||||
*/
|
||||
public static final ConcurrentMap<Integer, Integer> channelTypes = PlatformDependent.newConcurrentHashMap();
|
||||
|
||||
/**
|
||||
* 设置通道类型
|
||||
*
|
||||
* @param memberId 会员编号
|
||||
* @param channelType 通道类型:1Tcp 2WebSocket
|
||||
*/
|
||||
public static void putChannelType(Integer memberId, Integer channelType) {
|
||||
channelTypes.put(memberId, channelType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取通道类型
|
||||
*
|
||||
* @param memberId 会员编号
|
||||
* @return 通道类型:1Tcp 2WebSocket
|
||||
*/
|
||||
public static Integer getChannelType(Integer memberId) {
|
||||
return channelTypes.get(memberId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否存在用户
|
||||
*
|
||||
* @param memberId 会员编号
|
||||
* @return 是否存在用户
|
||||
*/
|
||||
public static boolean contains(Integer memberId) {
|
||||
return channelTypes.containsKey(memberId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除通道类型
|
||||
*
|
||||
* @param memberId 会员编号
|
||||
*/
|
||||
public static void removeChannelType(Integer memberId) {
|
||||
channelTypes.remove(memberId);
|
||||
}
|
||||
}
|
||||
33
netty-pad/src/main/java/com/sv/netty/config/Constant.java
Normal file
33
netty-pad/src/main/java/com/sv/netty/config/Constant.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package com.sv.netty.config;
|
||||
|
||||
import com.sv.netty.netty.ChannelParam;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
/**
|
||||
* Created by ranfi on 2/22/16.
|
||||
*/
|
||||
public class Constant {
|
||||
|
||||
public static final String ACCESS_TOKEN_KEY = "access_token_key";
|
||||
|
||||
//消息队列发送消息到netty
|
||||
public static final String sendToMachine = "netty-doll-machine";
|
||||
|
||||
//消息队列发送消息到netty
|
||||
public static final String sendToService = "netty-doll-service";
|
||||
|
||||
//消息队列发送消息到netty
|
||||
public static final String ROOM_SERVICE_TOPIC = "gt-room-service-topic";
|
||||
|
||||
/**
|
||||
* session中存储终端发送的额外参数
|
||||
*/
|
||||
public static AttributeKey<ChannelParam> CHANNEL_PARAM = AttributeKey.newInstance("CHANNEL_PARAM");
|
||||
|
||||
/**
|
||||
* 每局结束后的保护时间
|
||||
*/
|
||||
public static final Integer GAME_OVER_PROTECTION_SECONDS = 5;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Description:
|
||||
* SpringContextHolder.java Create on 2013-3-8 上午10:19:20
|
||||
* @author xiaopin
|
||||
* @version 1.0
|
||||
* Copyright (c) 2013 BMS,Inc. All Rights Reserved.
|
||||
*/
|
||||
package com.sv.netty.config;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候中取出ApplicaitonContext.
|
||||
*
|
||||
* @author calvin
|
||||
*/
|
||||
public class SpringContextHolder {
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.
|
||||
*/
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
SpringContextHolder.applicationContext = applicationContext; // NOSONAR
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.
|
||||
*/
|
||||
public static void setContext(ApplicationContext applicationContext) {
|
||||
SpringContextHolder.applicationContext = applicationContext; // NOSONAR
|
||||
}
|
||||
/**
|
||||
* 取得存储在静态变量中的ApplicationContext.
|
||||
*/
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
checkApplicationContext();
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋于对象的类
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getBean(String name) {
|
||||
checkApplicationContext();
|
||||
return (T) applicationContext.getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋对象的类 如果有多个Bean符合Class, 取出第一个
|
||||
*/
|
||||
public static <T> T getBean(Class<T> requiredType) {
|
||||
checkApplicationContext();
|
||||
return SpringContextHolder.applicationContext.getBean(requiredType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除applicationContext静态变量.
|
||||
*/
|
||||
public static void cleanApplicationContext() {
|
||||
applicationContext = null;
|
||||
}
|
||||
|
||||
private static void checkApplicationContext() {
|
||||
if (SpringContextHolder.applicationContext == null) {
|
||||
throw new IllegalStateException("applicaitonContext未注册请在applicationContext.xml中定义SpringContextHolder");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.sv.netty.controller;
|
||||
|
||||
import com.sv.entity.face.FaceRecognizeResponse;
|
||||
import com.sv.netty.netty.MemberDto;
|
||||
import com.sv.netty.netty.ResponseDTO;
|
||||
import com.sv.netty.service.MessageService;
|
||||
import com.sv.netty.service.impl.TcpMessageHandlerAdapter;
|
||||
import com.sv.service.oms.DeviceService;
|
||||
import com.sv.service.oms.VenueService;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@RestController
|
||||
public class MessageControler {
|
||||
|
||||
@Resource()
|
||||
private TcpMessageHandlerAdapter messageService;
|
||||
|
||||
@Resource
|
||||
private DeviceService deviceService;
|
||||
|
||||
@Resource
|
||||
private VenueService venueService;
|
||||
|
||||
@RequestMapping("/message/send/t")
|
||||
public ResponseDTO sendMessage1() {
|
||||
FaceRecognizeResponse recognizeResponse = new FaceRecognizeResponse();
|
||||
recognizeResponse.setPerson(new FaceRecognizeResponse.PersonBeanX());
|
||||
recognizeResponse.getPerson().setId(49);
|
||||
messageService.enter(deviceService.findById(1), recognizeResponse, venueService.findById(32));
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
//
|
||||
// @RequestMapping("/upload/image")
|
||||
// public ResponseDTO sendMessage(){
|
||||
// messageService.sendMessage(memberDto);
|
||||
// return ResponseDTO.ok();
|
||||
// }
|
||||
|
||||
}
|
||||
93
netty-pad/src/main/java/com/sv/netty/netty/BaseDto.java
Normal file
93
netty-pad/src/main/java/com/sv/netty/netty/BaseDto.java
Normal file
@@ -0,0 +1,93 @@
|
||||
package com.sv.netty.netty;
|
||||
|
||||
|
||||
import com.sv.entity.Device;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Created by peakren on 19/01/2018.
|
||||
*/
|
||||
|
||||
public class BaseDto implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 3139438146199448677L;
|
||||
|
||||
|
||||
private String cmdId; //指令
|
||||
|
||||
private String deviceId; //设备ID
|
||||
|
||||
/**
|
||||
* 客户端IP
|
||||
*/
|
||||
private String clientIp;
|
||||
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 状态码 0正常 1错误
|
||||
*/
|
||||
|
||||
private int errorCode = 0;
|
||||
|
||||
/**
|
||||
* 错误返回信息
|
||||
*/
|
||||
|
||||
private String errorMsg;
|
||||
|
||||
|
||||
public String getCmdId() {
|
||||
return cmdId;
|
||||
}
|
||||
|
||||
public void setCmdId(String cmdId) {
|
||||
this.cmdId = cmdId;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端IP,获取的内网IP
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getClientIp() {
|
||||
return clientIp;
|
||||
}
|
||||
|
||||
public void setClientIp(String clientIp) {
|
||||
this.clientIp = clientIp;
|
||||
}
|
||||
|
||||
public int getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public void setErrorCode(int errorCode) {
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public String getErrorMsg() {
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
public void setErrorMsg(String errorMsg) {
|
||||
this.errorMsg = errorMsg;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
134
netty-pad/src/main/java/com/sv/netty/netty/BootService.java
Normal file
134
netty-pad/src/main/java/com/sv/netty/netty/BootService.java
Normal file
@@ -0,0 +1,134 @@
|
||||
package com.sv.netty.netty;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* netty启动服务
|
||||
*
|
||||
* @Author peakren
|
||||
* @Date 03/05/2017 6:18 PM
|
||||
*/
|
||||
@Component
|
||||
public class BootService {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(BootService.class);
|
||||
|
||||
private volatile EventLoopGroup workerGroup;
|
||||
private volatile EventLoopGroup bossGroup;
|
||||
private volatile ServerBootstrap bootstrap;
|
||||
private ChannelFuture serverChannelFuture;
|
||||
|
||||
/**
|
||||
* 启动netty的控制线程
|
||||
*/
|
||||
private Executor messageExecutor;
|
||||
|
||||
/**
|
||||
* 下面几个是由spring注入
|
||||
*/
|
||||
@Value("${netty.port}")
|
||||
private int port;
|
||||
|
||||
@Value("${so.keepalive}")
|
||||
private Boolean keepalive;
|
||||
|
||||
@Value("${so.backlog}")
|
||||
private int backlog;
|
||||
|
||||
@Value("${tcp_nodelay}")
|
||||
private boolean nodelay;
|
||||
|
||||
@Value("${so.reuseaddr}")
|
||||
private boolean reuseaddr;
|
||||
|
||||
@Value("${boss.thread.count}")
|
||||
private int bossCount;
|
||||
|
||||
@Value("${worker.thread.count}")
|
||||
private int workerCount;
|
||||
|
||||
private Map<ChannelOption, Object> channelOptions;
|
||||
|
||||
private final ServerProtocolInitializer serverProtocolInitializer;
|
||||
|
||||
/**
|
||||
* 初始化netty启动配置
|
||||
*/
|
||||
public void init() {
|
||||
// bossGroup 只处理连接请求,真正的客户端业务处理是由workerGroup 处理的 都是无效循环的
|
||||
workerGroup = new NioEventLoopGroup();
|
||||
bossGroup = new NioEventLoopGroup();
|
||||
channelOptions = Maps.newHashMap();
|
||||
try {
|
||||
// channelOptions.put(ChannelOption.SO_KEEPALIVE,keepalive);
|
||||
channelOptions.put(ChannelOption.SO_BACKLOG,backlog);
|
||||
// channelOptions.put(ChannelOption.TCP_NODELAY,TCP_NODELAY);
|
||||
channelOptions.put(ChannelOption.SO_REUSEADDR,reuseaddr);
|
||||
bootstrap = new ServerBootstrap();
|
||||
bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // 设置服务器的通道是NIOServerSocketChannel
|
||||
.childHandler(serverProtocolInitializer);
|
||||
for (Map.Entry<ChannelOption, Object> entry : channelOptions.entrySet()) {
|
||||
bootstrap.option(entry.getKey(), entry.getValue());
|
||||
}
|
||||
bootstrap.childOption(ChannelOption.TCP_NODELAY,nodelay);
|
||||
bootstrap.childOption(ChannelOption.SO_KEEPALIVE,keepalive);
|
||||
// 绑定一个端口并且同步 启动服务器
|
||||
serverChannelFuture = bootstrap.bind(new InetSocketAddress(port)).sync();
|
||||
// serverChannelFuture的用处
|
||||
serverChannelFuture.addListener(f -> {
|
||||
if (f.isSuccess()){
|
||||
logger.info("成功bind端口:" + port);
|
||||
}
|
||||
});
|
||||
// 对关闭通道进行监听 (异步模型)
|
||||
serverChannelFuture.channel().closeFuture().sync();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("启动NETTY TCP异常:", e);
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启单独的线程运行netty服务,避免和spring mvc冲突
|
||||
*/
|
||||
public void run() {
|
||||
// messageExecutor = Executors.newFixedThreadPool(1);
|
||||
messageExecutor = Executors.newSingleThreadExecutor();
|
||||
messageExecutor.execute(() -> init());
|
||||
}
|
||||
|
||||
public void destroy() throws Exception {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
serverChannelFuture.channel().closeFuture().sync();
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void setChannelOptions(Map<ChannelOption, Object> channelOptions) {
|
||||
this.channelOptions = channelOptions;
|
||||
}
|
||||
|
||||
public BootService(ServerProtocolInitializer serverProtocolInitializer) {
|
||||
this.serverProtocolInitializer = serverProtocolInitializer;
|
||||
}
|
||||
|
||||
}
|
||||
88
netty-pad/src/main/java/com/sv/netty/netty/ChannelParam.java
Normal file
88
netty-pad/src/main/java/com/sv/netty/netty/ChannelParam.java
Normal file
@@ -0,0 +1,88 @@
|
||||
package com.sv.netty.netty;
|
||||
|
||||
/**
|
||||
* 会话中存储的对象
|
||||
*
|
||||
* @author peakren
|
||||
* @since 16/05/2017 11:09 PM
|
||||
*/
|
||||
public class ChannelParam {
|
||||
|
||||
/**
|
||||
* 设备ip
|
||||
*/
|
||||
private String clientIp;
|
||||
|
||||
/**
|
||||
* 设备编号
|
||||
*/
|
||||
private String deviceSn;
|
||||
|
||||
/**
|
||||
* 会员编号
|
||||
*/
|
||||
private Integer memberId;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
public ChannelParam() {
|
||||
|
||||
}
|
||||
|
||||
public ChannelParam(String clientIp){
|
||||
this.clientIp = clientIp;
|
||||
}
|
||||
|
||||
public ChannelParam(String clientIp, String deviceSn) {
|
||||
this.clientIp = clientIp;
|
||||
this.deviceSn = deviceSn;
|
||||
}
|
||||
|
||||
public String getClientIp() {
|
||||
return clientIp;
|
||||
}
|
||||
|
||||
public void setClientIp(String clientIp) {
|
||||
this.clientIp = clientIp;
|
||||
}
|
||||
|
||||
public String getDeviceSn() {
|
||||
return deviceSn;
|
||||
}
|
||||
|
||||
public void setDeviceSn(String deviceSn) {
|
||||
this.deviceSn = deviceSn;
|
||||
}
|
||||
|
||||
public Integer getMemberId() {
|
||||
return memberId;
|
||||
}
|
||||
|
||||
public void setMemberId(Integer memberId) {
|
||||
this.memberId = memberId;
|
||||
}
|
||||
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
public void setNickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
public String getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(String avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.sv.netty.netty;
|
||||
|
||||
import com.sv.netty.utils.JsonMapper;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.handler.timeout.IdleState;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 客户端处理器
|
||||
*
|
||||
* @author ranfi
|
||||
*/
|
||||
public class ClientHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ClientHandler.class);
|
||||
|
||||
private static final String MESSAGE = "Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients.";
|
||||
|
||||
public ClientHandler() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当通道就绪就会触发
|
||||
* @param ctx
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelActive(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当通道失效就会触发
|
||||
* @param ctx
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当通道有读取事件时触发
|
||||
* @param ctx
|
||||
* @param msg
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
super.channelRead(ctx, msg);
|
||||
logger.info("接收服务器响应msg:[" + msg + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
|
||||
throws Exception {
|
||||
super.userEventTriggered(ctx, evt);
|
||||
IdleStateEvent event = (IdleStateEvent) evt;
|
||||
// 如果IoSession闲置,则关闭连接
|
||||
if (event.state() == IdleState.READER_IDLE) {
|
||||
String json = JsonMapper.nonDefaultMapper().toJson(null);
|
||||
ctx.writeAndFlush(json);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
|
||||
throws Exception {
|
||||
super.exceptionCaught(ctx, cause);
|
||||
logger.error("ClientHandler exceptionCaught",cause);
|
||||
Channel channel = ctx.channel();
|
||||
if(channel.isActive()) {
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
39
netty-pad/src/main/java/com/sv/netty/netty/Cmd.java
Normal file
39
netty-pad/src/main/java/com/sv/netty/netty/Cmd.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.sv.netty.netty;
|
||||
|
||||
/**
|
||||
* 消息协议指令定义
|
||||
*
|
||||
* @Author peakren
|
||||
* @Date 08/12/2017 11:51 AM
|
||||
*/
|
||||
public enum Cmd {
|
||||
|
||||
|
||||
HB("hb", "心跳"),
|
||||
|
||||
FACEID("faceid", "人脸识别"),
|
||||
OPEN_DOOR("open_door", "开门"),
|
||||
|
||||
FACEID_RESPONSE("faceid_response_upload", "人脸结果");
|
||||
|
||||
public String id;
|
||||
|
||||
public String text;
|
||||
|
||||
Cmd(String id, String text) {
|
||||
this.id = id;
|
||||
this.text = text;
|
||||
|
||||
}
|
||||
|
||||
public static Cmd getCmd(String id) {
|
||||
for (Cmd cmd : Cmd.values()) {
|
||||
if (cmd.id.equalsIgnoreCase(id)) {
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
return Cmd.HB;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
148
netty-pad/src/main/java/com/sv/netty/netty/MemberDto.java
Normal file
148
netty-pad/src/main/java/com/sv/netty/netty/MemberDto.java
Normal file
@@ -0,0 +1,148 @@
|
||||
package com.sv.netty.netty;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
|
||||
/**
|
||||
* 会员基本信息
|
||||
* MemberDto.java
|
||||
*
|
||||
* @author peakren
|
||||
* @date 2018/12/20 8:39 PM
|
||||
*/
|
||||
public class MemberDto implements Serializable {
|
||||
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 手机号码
|
||||
*/
|
||||
|
||||
private String mobile;
|
||||
|
||||
/**
|
||||
* 余额
|
||||
*/
|
||||
|
||||
private BigDecimal amount;
|
||||
|
||||
/**
|
||||
* 是否第一次进入
|
||||
*/
|
||||
private boolean first = false;
|
||||
|
||||
/**
|
||||
* 场地价格
|
||||
*/
|
||||
private BigDecimal placePrice;
|
||||
/**
|
||||
* 场地名称
|
||||
*/
|
||||
|
||||
private String placeName;
|
||||
|
||||
/**
|
||||
* 会员卡名称
|
||||
*/
|
||||
|
||||
private String cardName;
|
||||
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 1成功进场 0不允许进场
|
||||
*/
|
||||
private int code;
|
||||
|
||||
public String getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(String avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getMobile() {
|
||||
return mobile;
|
||||
}
|
||||
|
||||
public void setMobile(String mobile) {
|
||||
this.mobile = mobile;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public String getPlaceName() {
|
||||
return placeName;
|
||||
}
|
||||
|
||||
public void setPlaceName(String placeName) {
|
||||
this.placeName = placeName;
|
||||
}
|
||||
|
||||
public String getCardName() {
|
||||
return cardName;
|
||||
}
|
||||
|
||||
public void setCardName(String cardName) {
|
||||
this.cardName = cardName;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public BigDecimal getPlacePrice() {
|
||||
return placePrice;
|
||||
}
|
||||
|
||||
public void setPlacePrice(BigDecimal placePrice) {
|
||||
this.placePrice = placePrice;
|
||||
}
|
||||
|
||||
public boolean isFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public void setFirst(boolean first) {
|
||||
this.first = first;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.sv.netty.netty;
|
||||
|
||||
import com.sv.netty.utils.EncodeUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* TCP消息解码器
|
||||
*
|
||||
* @Author peakren
|
||||
* @Date 08/05/2017 10:34 PM
|
||||
*/
|
||||
public class MessageDecoder extends ByteToMessageDecoder {
|
||||
|
||||
private final static int MESSAGE_LENGTH = 4;
|
||||
private final static int MESSAGE_SEQNO = 4;
|
||||
private final static int MESSAGE_HEAD = 8;
|
||||
private final static int MESSAGE_MAX_LENGTH = 12;
|
||||
private final static int MAGIC_WORD = 0x9DDD; //码头
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(MessageDecoder.class);
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
if (in.capacity() >= MESSAGE_MAX_LENGTH) {
|
||||
int magicWord = in.readInt();
|
||||
if (magicWord == MAGIC_WORD) {
|
||||
int length = in.readInt();
|
||||
byte[] msg = new byte[length];
|
||||
in.readBytes(msg);
|
||||
String message = new String(msg, "utf-8");
|
||||
out.add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
24
netty-pad/src/main/java/com/sv/netty/netty/MessageDto.java
Normal file
24
netty-pad/src/main/java/com/sv/netty/netty/MessageDto.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package com.sv.netty.netty;
|
||||
|
||||
public class MessageDto extends BaseDto {
|
||||
|
||||
|
||||
private Object result;
|
||||
private Integer door;
|
||||
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(Object result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public Integer getDoor() {
|
||||
return door;
|
||||
}
|
||||
|
||||
public void setDoor(Integer door) {
|
||||
this.door = door;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.sv.netty.netty;
|
||||
|
||||
import com.sv.netty.utils.CommonUtils;
|
||||
import com.sv.netty.utils.JsonUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
|
||||
/**
|
||||
* 自定义消息编码器
|
||||
*
|
||||
* @Author peakren
|
||||
* @Date 07/05/2017 10:43 PM
|
||||
*/
|
||||
public class MessageEncoder extends MessageToByteEncoder<Object> {
|
||||
|
||||
private final static int MESSAGE_LENGTH = 4;
|
||||
private final static int MESSAGE_SEQNO = 8;
|
||||
private final static int MESSAGE_HEAD = 4;
|
||||
private final static String mSeqno = "doll";
|
||||
private final static int MAGIC_WORD = 0x9DDD;
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(MessageEncoder.class);
|
||||
|
||||
Charset charset = Charset.forName("UTF-8");
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
|
||||
String message = JsonUtils.encode(msg);
|
||||
logger.info("send message content:" + message);
|
||||
byte[] bodys = message.getBytes(charset.name());
|
||||
|
||||
int len = bodys.length;
|
||||
out.writeInt(MAGIC_WORD); //发送预留数据字节码
|
||||
out.writeInt(len); //发送head字节码
|
||||
out.writeBytes(bodys); //发送消息内容
|
||||
}
|
||||
|
||||
byte[] getIntBytes(int crc) {
|
||||
byte[] targets = new byte[4];
|
||||
targets[0] = (byte) (crc & 0xff);
|
||||
targets[1] = (byte) ((crc >> 8) & 0xff);
|
||||
targets[2] = (byte) ((crc >> 16) & 0xff);
|
||||
targets[3] = (byte) (crc >> 24);
|
||||
return targets;
|
||||
}
|
||||
|
||||
byte[] getLongBytes(long crc) {
|
||||
byte[] targets = new byte[8];
|
||||
targets[0] = (byte) (crc & 0xff);
|
||||
targets[1] = (byte) ((crc >> 8) & 0xff);
|
||||
targets[2] = (byte) ((crc >> 16) & 0xff);
|
||||
targets[3] = (byte) (crc >> 24 & 0xff);
|
||||
targets[4] = (byte) (crc >> 32 & 0xff);
|
||||
targets[5] = (byte) (crc >> 40 & 0xff);
|
||||
targets[6] = (byte) (crc >> 48 & 0xff);
|
||||
targets[7] = (byte) (crc >> 56);
|
||||
return targets;
|
||||
}
|
||||
|
||||
}
|
||||
60
netty-pad/src/main/java/com/sv/netty/netty/ResponseDTO.java
Normal file
60
netty-pad/src/main/java/com/sv/netty/netty/ResponseDTO.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package com.sv.netty.netty;
|
||||
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
public class ResponseDTO extends LinkedHashMap<String, Object> {
|
||||
public static final String ERR_CODE = "err_code";
|
||||
public static final String ERR_MSG = "err_msg";
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
private static final long serialVersionUID = 8410965932046471023L;
|
||||
|
||||
public ResponseDTO() {
|
||||
this(0, "OK");
|
||||
}
|
||||
|
||||
public ResponseDTO(Integer errCode, String errMsg) {
|
||||
this(errCode, errMsg, System.currentTimeMillis() / 1000L);
|
||||
}
|
||||
|
||||
public ResponseDTO(Integer errCode, String errMsg, Long timestamp) {
|
||||
this.addAttribute("err_code", errCode);
|
||||
this.addAttribute("err_msg", errMsg);
|
||||
this.addAttribute("timestamp", timestamp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public ResponseDTO(Integer errCode, String errMsg, String attrName, Object attrValue) {
|
||||
this(errCode, errMsg);
|
||||
this.addAttribute(attrName, attrValue);
|
||||
}
|
||||
|
||||
public static ResponseDTO ok() {
|
||||
return new ResponseDTO();
|
||||
}
|
||||
|
||||
public static ResponseDTO ok(String msg) {
|
||||
ResponseDTO ret = ok();
|
||||
ret.setErrorMsg(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public ResponseDTO addAttribute(String attrName, Object attrValue) {
|
||||
Assert.notNull(attrName, "属性名称不能为空");
|
||||
this.put(attrName, attrValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setErrorCode(Integer errCode) {
|
||||
this.addAttribute("err_code", errCode);
|
||||
}
|
||||
|
||||
public void setErrorMsg(String errMsg) {
|
||||
this.addAttribute("err_msg", errMsg);
|
||||
}
|
||||
}
|
||||
39
netty-pad/src/main/java/com/sv/netty/netty/Result.java
Normal file
39
netty-pad/src/main/java/com/sv/netty/netty/Result.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.sv.netty.netty;
|
||||
|
||||
public class Result {
|
||||
|
||||
|
||||
/**
|
||||
* Code : 0
|
||||
* Message : success
|
||||
* Data : false
|
||||
*/
|
||||
|
||||
private int Code;
|
||||
private String Message;
|
||||
private boolean Data;
|
||||
|
||||
public int getCode() {
|
||||
return Code;
|
||||
}
|
||||
|
||||
public void setCode(int Code) {
|
||||
this.Code = Code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return Message;
|
||||
}
|
||||
|
||||
public void setMessage(String Message) {
|
||||
this.Message = Message;
|
||||
}
|
||||
|
||||
public boolean isData() {
|
||||
return Data;
|
||||
}
|
||||
|
||||
public void setData(boolean Data) {
|
||||
this.Data = Data;
|
||||
}
|
||||
}
|
||||
133
netty-pad/src/main/java/com/sv/netty/netty/ServerHandler.java
Normal file
133
netty-pad/src/main/java/com/sv/netty/netty/ServerHandler.java
Normal file
@@ -0,0 +1,133 @@
|
||||
package com.sv.netty.netty;
|
||||
|
||||
import com.sv.netty.config.Constant;
|
||||
import com.sv.netty.config.SpringContextHolder;
|
||||
import com.sv.netty.service.MessageService;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.handler.timeout.IdleState;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* 服务器接受TCP协议数据处理器
|
||||
*
|
||||
* @author ranfi
|
||||
*/
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
public class ServerHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ServerHandler.class);
|
||||
|
||||
private MessageService messageService;
|
||||
|
||||
public ServerHandler() {
|
||||
messageService = SpringContextHolder.getBean("messageService");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通道就绪事件
|
||||
* @param ctx
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelActive(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取客户端发送的消息
|
||||
* @param ctx 上下文对象 管道(pipeline),通道channel , 地址
|
||||
* @param msg 客户端发送的数据
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
super.channelRead(ctx, msg);
|
||||
//taskQueue
|
||||
// ctx.channel().eventLoop().execute()
|
||||
//scheduleTaskQueue
|
||||
// ctx.channel().eventLoop().schedule()
|
||||
String clientIp = ctx.channel().attr(Constant.CHANNEL_PARAM).get().getClientIp();
|
||||
try {
|
||||
messageService.receive(ctx.channel(), msg.toString());
|
||||
} catch (Exception e) {
|
||||
logger.error("[" + clientIp + "] host unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
|
||||
String clientIP = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
|
||||
logger.info("There is a client Registered. ip:" + clientIP);
|
||||
ctx.channel().attr(Constant.CHANNEL_PARAM).set(new ChannelParam(clientIP));
|
||||
messageService.online(ctx.channel());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelUnregistered(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link ChannelHandlerContext#fireChannelInactive()} to forward
|
||||
* to the next {@link ChannelHandler} in the {@link ChannelPipeline}.
|
||||
* <p>
|
||||
* Sub-classes may override this method to change behavior.
|
||||
*
|
||||
* @param ctx
|
||||
*/
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
String clientIP = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
|
||||
logger.error("Client ip [" + clientIP + "] has inactive");
|
||||
Integer memberId = ctx.channel().attr(Constant.CHANNEL_PARAM).get().getMemberId();
|
||||
String nickname = ctx.channel().attr(Constant.CHANNEL_PARAM).get().getNickname();
|
||||
String machineSn = ctx.channel().attr(Constant.CHANNEL_PARAM).get().getDeviceSn();
|
||||
messageService.doOffLine(memberId, nickname,machineSn);
|
||||
messageService.destory(ctx.channel());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理异常、一般需要关闭通道
|
||||
* @param ctx
|
||||
* @param cause
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
|
||||
throws Exception {
|
||||
super.exceptionCaught(ctx, cause);
|
||||
logger.error("ServerHandler exceptionCaught",cause);
|
||||
Channel channel = ctx.channel();
|
||||
if(channel.isActive()) {
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link ChannelHandlerContext#fireUserEventTriggered(Object)} to forward
|
||||
* to the next {@link ChannelHandler} in the {@link ChannelPipeline}.
|
||||
* <p/>
|
||||
* Sub-classes may override this method to change behavior.
|
||||
*
|
||||
* @param ctx
|
||||
* @param evt
|
||||
*/
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
IdleStateEvent event = (IdleStateEvent) evt;
|
||||
// 如果Channel读取数据闲置,则关闭连接
|
||||
if (event.state() == IdleState.READER_IDLE) {
|
||||
String clientIP = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
|
||||
logger.info("Client [" + clientIP + "] has idle");
|
||||
messageService.destory(ctx.channel());
|
||||
ctx.channel().close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.sv.netty.netty;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 服务器端协议初始化适配器
|
||||
*
|
||||
* @Author peakren
|
||||
* @Date 09/05/2017 5:19 PM
|
||||
*/
|
||||
@Component
|
||||
public class ServerProtocolInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ServerProtocolInitializer.class);
|
||||
private final int IDLE_TIME = 30; //连接检测空闲时间
|
||||
private final int READ_TIME = 30; //读超时时间
|
||||
private final int WRITE_TIME = 30; //写超时时间
|
||||
private final int READ_TIMEOUT = 600; //读超时时间
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch){
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
logger.info("ServerProtocolInitializer");
|
||||
// 通过指定的长度来标识整包的信息,这样就可以自动的处理粘包和半包的问题
|
||||
pipeline.addFirst(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 4, 4, 0, 0));
|
||||
pipeline.addLast(new ReadTimeoutHandler(READ_TIMEOUT));
|
||||
pipeline.addLast(new MessageDecoder());
|
||||
pipeline.addLast(new MessageEncoder());
|
||||
// 心跳检测机制,通过调用触发下一个handler userEventTriggered 方法
|
||||
pipeline.addLast(new IdleStateHandler(READ_TIME, WRITE_TIME,IDLE_TIME));
|
||||
pipeline.addLast(new ServerHandler());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.sv.netty.service;
|
||||
|
||||
import com.sv.netty.netty.MemberDto;
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
/**
|
||||
* 消息实现接口
|
||||
*
|
||||
* @author peakren
|
||||
* @since 03/05/2017 10:04 PM
|
||||
*/
|
||||
public interface MessageService {
|
||||
|
||||
/**
|
||||
* 接受设备消息
|
||||
*
|
||||
* @param channel
|
||||
* @param content
|
||||
*/
|
||||
void receive(Channel channel, String content);
|
||||
|
||||
void online(Channel channel);
|
||||
|
||||
/**
|
||||
* 销毁设备的连接
|
||||
*
|
||||
* @param channel
|
||||
*/
|
||||
void destory(Channel channel);
|
||||
|
||||
|
||||
void doOffLine(Integer memberId, String nickname, String machineSn);
|
||||
|
||||
void sendMessage(MemberDto memberDto);
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
package com.sv.netty.service.impl;
|
||||
|
||||
import com.enums.FaceRecognizeEnum;
|
||||
import com.sv.dto.EnterResult;
|
||||
import com.sv.entity.Device;
|
||||
import com.sv.entity.Member;
|
||||
import com.sv.entity.MemberEnterVenueLog;
|
||||
import com.sv.entity.Venue;
|
||||
import com.sv.entity.face.FaceRecognizeResponse;
|
||||
import com.sv.netty.netty.*;
|
||||
import com.sv.netty.service.MessageService;
|
||||
import com.sv.netty.utils.JsonMapper;
|
||||
import com.sv.service.api.MemberEnterVenueLogService;
|
||||
import com.sv.service.api.MemberService;
|
||||
import com.sv.service.api.VenueService;
|
||||
import com.sv.service.oms.ConfigService;
|
||||
import com.sv.service.oms.DeviceService;
|
||||
import com.ydd.oms.entity.sys.Config;
|
||||
import io.netty.channel.Channel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/**
|
||||
* 消息实现类
|
||||
*
|
||||
* @Author peakren
|
||||
* @Date 08/05/2017 10:43 PM
|
||||
*/
|
||||
@Service("messageService")
|
||||
public class TcpMessageHandlerAdapter implements MessageService {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(TcpMessageHandlerAdapter.class);
|
||||
|
||||
|
||||
@Value("${nettym.url}")
|
||||
private String url;
|
||||
|
||||
@Resource
|
||||
private MemberService memberService;
|
||||
@Resource
|
||||
private VenueService venueService;
|
||||
|
||||
private Set<Channel> channels = new HashSet<>();
|
||||
|
||||
@Resource
|
||||
private DeviceService deviceService;
|
||||
|
||||
@Resource
|
||||
private ConfigService configService;
|
||||
|
||||
private Channel enter;
|
||||
|
||||
private Channel out;
|
||||
|
||||
@Resource(name = "scheduledExecutorService")
|
||||
private ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
@Resource
|
||||
private MemberEnterVenueLogService memberEnterVenueLogService;
|
||||
|
||||
|
||||
@Resource
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
public void sendMessage(MemberDto memberDto, Integer deviceId) {
|
||||
MessageDto messageDto = new MessageDto();
|
||||
messageDto.setCmdId(Cmd.FACEID.id);
|
||||
messageDto.setResult(memberDto);
|
||||
logger.info("发送消息" + channels.size() + ";deviceId:" + deviceId);
|
||||
if (deviceId == 1) {
|
||||
if (enter != null) {
|
||||
enter.writeAndFlush(messageDto);
|
||||
}
|
||||
} else {
|
||||
out.writeAndFlush(messageDto);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendOpenMessage(MessageDto messageDto, Integer deviceId) {
|
||||
logger.info("发送消息开门" + channels.size());
|
||||
if (enter != null) {
|
||||
enter.writeAndFlush(messageDto);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收设备发送过来的协议数据,业务处理后并返回
|
||||
*
|
||||
* @param channel netty的连接通道会话
|
||||
* @param content 消息内容
|
||||
*/
|
||||
@Override
|
||||
public void receive(Channel channel, String content) {
|
||||
try {
|
||||
//解析数据
|
||||
Cmd cmd = resolveCmd(content);
|
||||
logger.info("收到消息" + cmd.text);
|
||||
switch (cmd) {
|
||||
case HB:
|
||||
break;
|
||||
case FACEID_RESPONSE:
|
||||
synchronized (this) {
|
||||
resolve(channel, content);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("消息内容:" + content);
|
||||
logger.error("处理TCP消息异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void online(Channel channel) {
|
||||
channels.add(channel);
|
||||
}
|
||||
|
||||
|
||||
public synchronized void resolve(Channel channel, String content) {
|
||||
BaseDto baseDto = JsonMapper.nonEmptyMapper().fromJson(content, BaseDto.class);
|
||||
FaceRecognizeResponse response = JsonMapper.nonNullMapper().fromJson(baseDto.getContent(), FaceRecognizeResponse.class);
|
||||
Device device = deviceService.findById(Integer.parseInt(baseDto.getDeviceId()));
|
||||
Venue venue = venueService.findById(device.getVenueId());
|
||||
if (FaceRecognizeEnum.RECOGNIZED.name.equals(response.getType())) {
|
||||
//识别成功
|
||||
if (response.getPerson() != null) {
|
||||
if (device.getId() == 1) {
|
||||
//进门
|
||||
logger.info("开门");
|
||||
enter = channel;
|
||||
enter(device, response, venue);
|
||||
} else if (device.getId() == 2) {
|
||||
//出门
|
||||
logger.info("出门");
|
||||
out = channel;
|
||||
out(device, response, venue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void enter(Device device, FaceRecognizeResponse response, Venue venue) {
|
||||
try {
|
||||
int code = 1;
|
||||
Member member = memberService.findByFaceId(response.getPerson().getId());
|
||||
if (member != null) {
|
||||
//最后进场记录
|
||||
MemberEnterVenueLog enterVenueLog = memberEnterVenueLogService.findMemberLastLogNoType(member.getId(), device.getVenueId());
|
||||
if (enterVenueLog != null) {
|
||||
//有记录 查看 最后一次是否是出场
|
||||
if (enterVenueLog.getType() == 1) {
|
||||
//是出场 不用限制
|
||||
} else {
|
||||
//是进场
|
||||
Date date = new Date();
|
||||
//本日连续进场次数
|
||||
int continuityEnterCount = memberEnterVenueLogService.countEnterByDate(member.getId(), device.getVenueId(), date);
|
||||
logger.info("连续进场次数:" + continuityEnterCount);
|
||||
//距离上次入场差多少秒
|
||||
int differenceSeconds = (int) (date.getTime() - enterVenueLog.getCreatedTime().getTime()) / (1000);
|
||||
if (continuityEnterCount <= 0) {
|
||||
//没有连续进场 间隔5分钟
|
||||
Config config = configService.findById(1);
|
||||
if (differenceSeconds <= (Integer.parseInt(config.getValue()) * 60)) {
|
||||
//小于5分钟 不允许入场
|
||||
code = -1;
|
||||
}
|
||||
} else if (continuityEnterCount == 1) {
|
||||
//有过一次连续进场 间隔20分钟
|
||||
Config config = configService.findById(2);
|
||||
if (differenceSeconds <= (Integer.parseInt(config.getValue()) * 60)) {
|
||||
//小于20分钟 不允许入场
|
||||
code = -1;
|
||||
}
|
||||
} else {
|
||||
//超过两次连续入场 入场失败
|
||||
code = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
MemberDto memberMessageDto = new MemberDto();
|
||||
memberMessageDto.setName(member.getNickname());
|
||||
memberMessageDto.setAvatar(member.getAvatar());
|
||||
memberMessageDto.setMobile(member.getMobile());
|
||||
|
||||
memberMessageDto.setPlaceName("");
|
||||
member = memberService.findByFaceId(response.getPerson().getId());
|
||||
memberMessageDto.setAmount(member.getMoney());
|
||||
if (code > 0) {
|
||||
EnterResult result = venueService.enterVenue(response.getPerson().getId(), device.getId());
|
||||
if (result.getStatus() >= 0) {
|
||||
memberMessageDto.setCode(1);
|
||||
if (result.getStatus() == 1) {
|
||||
memberMessageDto.setCardName("会员卡");
|
||||
}
|
||||
if (result.getStatus() == 2) {
|
||||
memberMessageDto.setPlacePrice(result.getMoney());
|
||||
memberMessageDto.setFirst(true);
|
||||
logger.info(member.getId() + "入场成功:" + member.getMoney().toString());
|
||||
member = memberService.findByFaceId(response.getPerson().getId());
|
||||
memberMessageDto.setAmount(member.getMoney());
|
||||
//5秒后开门
|
||||
scheduledExecutorService.schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MessageDto messageDto = new MessageDto();
|
||||
messageDto.setCmdId(Cmd.OPEN_DOOR.id);
|
||||
messageDto.setDoor(1);
|
||||
sendOpenMessage(messageDto, 1);
|
||||
}
|
||||
}, 5, TimeUnit.SECONDS);
|
||||
} else {
|
||||
//开门
|
||||
MessageDto messageDto = new MessageDto();
|
||||
messageDto.setCmdId(Cmd.OPEN_DOOR.id);
|
||||
messageDto.setDoor(1);
|
||||
sendOpenMessage(messageDto, 1);
|
||||
logger.info(member.getId() + "入场成功:" + member.getMoney().toString());
|
||||
|
||||
}
|
||||
memberMessageDto.setMessage("门禁已开,请入门");
|
||||
venueService.addNumber(venue.getId(), 1, member.getId());
|
||||
venue = venueService.findById(venue.getId());
|
||||
sendNumberChange(venue.getNumber());
|
||||
} else {
|
||||
logger.info(member.getId() + "入场失败:" + member.getMoney().toString());
|
||||
memberMessageDto.setCode(-1);
|
||||
memberMessageDto.setMessage(member.getName() + "您好,您的余额不足,请扫描门禁上张贴的小程序二维码充值");
|
||||
}
|
||||
} else {
|
||||
logger.info(member.getId() + "入场失败:连续入场");
|
||||
memberMessageDto.setCode(0);
|
||||
Config config = configService.findById(1);
|
||||
memberMessageDto.setMessage("无出门记录连续入场,请" + config.getValue() + "分钟之后再试");
|
||||
}
|
||||
sendMessage(memberMessageDto, device.getId());
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void out(Device device, FaceRecognizeResponse response, Venue venue) {
|
||||
Member member = memberService.findByFaceId(response.getPerson().getId());
|
||||
if (member != null) {
|
||||
//出场 不用判断直接出
|
||||
MemberEnterVenueLog memberEnterVenueLog = new MemberEnterVenueLog();
|
||||
memberEnterVenueLog.setOrderSn("");
|
||||
memberEnterVenueLog.setType(1);
|
||||
memberEnterVenueLog.setMemberId(member.getId());
|
||||
memberEnterVenueLog.setVeneuType(device.getVenueType());
|
||||
memberEnterVenueLog.setVenueId(device.getVenueId());
|
||||
memberEnterVenueLog.setPlatformId(member.getPlatformId());
|
||||
memberEnterVenueLogService.save(memberEnterVenueLog);
|
||||
logger.info("用户" + member.getNickname() + "出场");
|
||||
MemberDto memberMessageDto = new MemberDto();
|
||||
memberMessageDto.setAmount(member.getMoney());
|
||||
memberMessageDto.setName(member.getNickname());
|
||||
memberMessageDto.setAvatar(member.getAvatar());
|
||||
memberMessageDto.setMobile(member.getMobile());
|
||||
memberMessageDto.setPlaceName("");
|
||||
memberMessageDto.setMessage("欢迎下次再来" + venue.getName());
|
||||
memberMessageDto.setCode(2);
|
||||
MessageDto messageDto = new MessageDto();
|
||||
messageDto.setCmdId(Cmd.OPEN_DOOR.id);
|
||||
messageDto.setDoor(2);
|
||||
sendOpenMessage(messageDto, device.getId());
|
||||
sendMessage(memberMessageDto, device.getId());
|
||||
venueService.addNumber(venue.getId(), -1, member.getId());
|
||||
venue = venueService.findById(venue.getId());
|
||||
sendNumberChange(venue.getNumber());
|
||||
}
|
||||
}
|
||||
|
||||
public void sendNumberChange(Integer number) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
|
||||
param.add("LingtekID", "5d14229fcb1f5c1a9046f429");
|
||||
param.add("Number", number.toString());
|
||||
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(param, headers);
|
||||
ResponseEntity<Result> result = restTemplate.exchange("http://lingtek.jalasmart.com/api/v1/lingtek/number", HttpMethod.PUT, request, Result.class);
|
||||
logger.info("灯光结果" + JsonMapper.nonDefaultMapper().toJson(result));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void destory(Channel channel) {
|
||||
channels.remove(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doOffLine(Integer memberId, String nickname, String machineSn) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(MemberDto memberDto) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected Cmd resolveCmd(String message) {
|
||||
BaseDto baseDto = JsonMapper.nonEmptyMapper().fromJson(message, BaseDto.class);
|
||||
return Cmd.getCmd(baseDto.getCmdId());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
76
netty-pad/src/main/java/com/sv/netty/utils/CommonUtils.java
Normal file
76
netty-pad/src/main/java/com/sv/netty/utils/CommonUtils.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package com.sv.netty.utils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* USER: douya
|
||||
* DATE: 2017-08-28
|
||||
*/
|
||||
public class CommonUtils {
|
||||
|
||||
|
||||
|
||||
public static byte[] intToBytes2(int n){
|
||||
byte[] b = new byte[4];
|
||||
|
||||
for(int i = 0;i < 4;i++)
|
||||
{
|
||||
b[i]=(byte)(n>>(24-i*8));
|
||||
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组转换成16进制字符串
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
public static String bytesToHexString(byte[] src){
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (src == null || src.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < src.length; i++) {
|
||||
int v = src[i] & 0xFF;
|
||||
String hv = Integer.toHexString(v);
|
||||
if (hv.length() < 2) {
|
||||
stringBuilder.append(0);
|
||||
}
|
||||
stringBuilder.append(hv);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* byte数组转换成16进制字符数组
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
public static String[] bytesToHexStrings(byte[] src){
|
||||
if (src == null || src.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
String[] str = new String[src.length];
|
||||
|
||||
for (int i = 0; i < src.length; i++) {
|
||||
int v = src[i] & 0xFF;
|
||||
String hv = Integer.toHexString(v);
|
||||
if (hv.length() < 2) {
|
||||
str[i] = "0";
|
||||
}
|
||||
str[i] = hv;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] arg){
|
||||
String message = "{\"cmd\":\"text_message\",\"content\":\"开心开心\",\"member_count\":0,\"system\":0,\"vmc_no\":\"322af403825a\"";
|
||||
byte[] bodys = message.getBytes(Charset.forName("UTF-8"));
|
||||
String magic = bytesToHexString("doll".getBytes(Charset.forName("UTF-8")));
|
||||
String length = bytesToHexString(intToBytes2(bodys.length+8));
|
||||
System.out.println(magic+length+bytesToHexString(bodys));
|
||||
}
|
||||
|
||||
}
|
||||
77
netty-pad/src/main/java/com/sv/netty/utils/EncodeUtils.java
Normal file
77
netty-pad/src/main/java/com/sv/netty/utils/EncodeUtils.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) 2005-2009 springside.org.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*
|
||||
* $Id: EncodeUtils.java 984 2010-03-21 13:02:44Z calvinxiu $
|
||||
*/
|
||||
package com.sv.netty.utils;
|
||||
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
|
||||
/**
|
||||
* 各种格式的编码加码工具类.
|
||||
*
|
||||
* 集成Commons-Codec,Commons-Lang及JDK提供的编解码方法.
|
||||
*
|
||||
* @author ranfi
|
||||
*/
|
||||
public class EncodeUtils {
|
||||
|
||||
private static final String DEFAULT_URL_ENCODING = "UTF-8";
|
||||
private static final char[] BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
|
||||
|
||||
/**
|
||||
* Hex编码.
|
||||
*/
|
||||
public static String encodeHex(byte[] input) {
|
||||
return Hex.encodeHexString(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hex解码.
|
||||
*/
|
||||
public static byte[] decodeHex(String input) {
|
||||
try {
|
||||
return Hex.decodeHex(input.toCharArray());
|
||||
} catch (DecoderException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64编码.
|
||||
*/
|
||||
public static String encodeBase64(byte[] input) {
|
||||
return Base64.encodeBase64String(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64编码, URL安全(将Base64中的URL非法字符'+'和'/'转为'-'和'_', 见RFC3548).
|
||||
*/
|
||||
public static String encodeUrlSafeBase64(byte[] input) {
|
||||
return Base64.encodeBase64URLSafeString(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64解码.
|
||||
*/
|
||||
public static byte[] decodeBase64(String input) {
|
||||
return Base64.decodeBase64(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base62编码。
|
||||
*/
|
||||
public static String encodeBase62(byte[] input) {
|
||||
char[] chars = new char[input.length];
|
||||
for (int i = 0; i < input.length; i++) {
|
||||
chars[i] = BASE62[(input[i] & 0xFF) % BASE62.length];
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
204
netty-pad/src/main/java/com/sv/netty/utils/JsonMapper.java
Normal file
204
netty-pad/src/main/java/com/sv/netty/utils/JsonMapper.java
Normal file
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* Copyright (c) 2005-2012 springside.org.cn
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*/
|
||||
package com.sv.netty.utils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.util.JSONPObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 简单封装Jackson,实现JSON String<->Java Object的Mapper.
|
||||
* <p>
|
||||
* 封装不同的输出风格, 使用不同的builder函数创建实例.
|
||||
*
|
||||
* @author calvin
|
||||
*/
|
||||
public class JsonMapper {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(JsonMapper.class);
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public JsonMapper() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public JsonMapper(Include include) {
|
||||
mapper = new ObjectMapper();
|
||||
// 设置输出时包含属性的风格
|
||||
if (include != null) {
|
||||
mapper.setSerializationInclusion(include);
|
||||
}
|
||||
// 设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性
|
||||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建只输出非Null且非Empty(如List.isEmpty)的属性到Json字符串的Mapper,建议在外部接口中使用.
|
||||
*/
|
||||
public static JsonMapper nonEmptyMapper() {
|
||||
return new JsonMapper(Include.NON_EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建只输出初始值被改变的属性到Json字符串的Mapper, 最节约的存储方式,建议在内部接口中使用。
|
||||
*/
|
||||
public static JsonMapper nonDefaultMapper() {
|
||||
return new JsonMapper(Include.NON_DEFAULT);
|
||||
}
|
||||
|
||||
public static JsonMapper nonNullMapper() {
|
||||
return new JsonMapper(Include.NON_NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Object可以是POJO,也可以是Collection或数组。 如果对象为Null, 返回"null". 如果集合为空集合, 返回"[]".
|
||||
*/
|
||||
public String toJson(Object object) {
|
||||
|
||||
try {
|
||||
return mapper.writeValueAsString(object);
|
||||
} catch (IOException e) {
|
||||
logger.warn("write to json string error:" + object, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化POJO或简单Collection如List<String>.
|
||||
* <p>
|
||||
* 如果JSON字符串为Null或"null"字符串, 返回Null. 如果JSON字符串为"[]", 返回空集合.
|
||||
* <p>
|
||||
* 如需反序列化复杂Collection如List<MyBean>, 请使用fromJson(String, JavaType)
|
||||
*
|
||||
* @see #fromJson(String, JavaType)
|
||||
*/
|
||||
public <T> T fromJson(String jsonString, Class<T> clazz) {
|
||||
if (StringUtils.isEmpty(jsonString)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return mapper.readValue(jsonString, clazz);
|
||||
} catch (IOException e) {
|
||||
logger.warn("parse json string error:" + jsonString, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化复杂Collection如List<Bean>,
|
||||
* 先使用createCollectionType()或contructMapType()构造类型, 然后调用本函数.
|
||||
*
|
||||
* @see #createCollectionType(Class, Class...)
|
||||
*/
|
||||
public <T> T fromJson(String jsonString, JavaType javaType) {
|
||||
if (StringUtils.isEmpty(jsonString)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return (T) mapper.readValue(jsonString, javaType);
|
||||
} catch (IOException e) {
|
||||
logger.warn("parse json string error:" + jsonString, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造Collection类型.
|
||||
*/
|
||||
public JavaType contructCollectionType(Class<? extends Collection> collectionClass, Class<?> elementClass) {
|
||||
return mapper.getTypeFactory().constructCollectionType(collectionClass, elementClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造Map类型.
|
||||
*/
|
||||
public JavaType contructMapType(Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass) {
|
||||
return mapper.getTypeFactory().constructMapType(mapClass, keyClass, valueClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* map 转 bean
|
||||
*
|
||||
* @param map
|
||||
* @param beanClass
|
||||
* @return
|
||||
*/
|
||||
public <T> T convertMapToBean(Map<String, ?> map, Class<T> beanClass) {
|
||||
try {
|
||||
return mapper.convertValue(map, beanClass);
|
||||
} catch (Exception e) {
|
||||
logger.warn("convertMapToBean error! ", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* map 转 bean
|
||||
*
|
||||
* @param bean
|
||||
* @param objectClass
|
||||
* @return
|
||||
*/
|
||||
public <T> T convertBeanToOther(Object bean, Class<T> objectClass) {
|
||||
try {
|
||||
return mapper.convertValue(bean, objectClass);
|
||||
} catch (Exception e) {
|
||||
logger.warn("convertMapToBean error! ", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当JSON里只含有Bean的部分屬性時,更新一個已存在Bean,只覆蓋該部分的屬性.
|
||||
*/
|
||||
public void update(String jsonString, Object object) {
|
||||
try {
|
||||
mapper.readerForUpdating(object).readValue(jsonString);
|
||||
} catch (JsonProcessingException e) {
|
||||
logger.warn("update json string:" + jsonString + " to object:" + object + " error.", e);
|
||||
} catch (IOException e) {
|
||||
logger.warn("update json string:" + jsonString + " to object:" + object + " error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 輸出JSONP格式數據.
|
||||
*/
|
||||
public String toJsonP(String functionName, Object object) {
|
||||
return toJson(new JSONPObject(functionName, object));
|
||||
}
|
||||
|
||||
/**
|
||||
* 設定是否使用Enum的toString函數來讀寫Enum, 為False時時使用Enum的name()函數來讀寫Enum, 默認為False.
|
||||
* 注意本函數一定要在Mapper創建後, 所有的讀寫動作之前調用.
|
||||
*/
|
||||
public void enableEnumUseToString() {
|
||||
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
|
||||
mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取出Mapper做进一步的设置或使用其他序列化API.
|
||||
*/
|
||||
public ObjectMapper getMapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
}
|
||||
94
netty-pad/src/main/java/com/sv/netty/utils/JsonUtils.java
Normal file
94
netty-pad/src/main/java/com/sv/netty/utils/JsonUtils.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package com.sv.netty.utils;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerationException;
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* USER: douya
|
||||
* DATE: 2017-11-01
|
||||
*/
|
||||
public class JsonUtils {
|
||||
|
||||
|
||||
/**
|
||||
* Logger for this class
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);
|
||||
|
||||
private final static ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
static {
|
||||
objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
|
||||
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
|
||||
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
|
||||
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
}
|
||||
|
||||
private JsonUtils() {
|
||||
}
|
||||
|
||||
public static String encode(Object obj) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(obj);
|
||||
} catch (JsonGenerationException e) {
|
||||
logger.error("encode(Object)", e); //$NON-NLS-1$
|
||||
} catch (JsonMappingException e) {
|
||||
logger.error("encode(Object)", e); //$NON-NLS-1$
|
||||
} catch (IOException e) {
|
||||
logger.error("encode(Object)", e); //$NON-NLS-1$
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json string反序列化成对象
|
||||
*
|
||||
* @param json
|
||||
* @param valueType
|
||||
* @return
|
||||
*/
|
||||
public static <T> T decode(String json, Class<T> valueType) {
|
||||
try {
|
||||
return objectMapper.readValue(json, valueType);
|
||||
} catch (JsonParseException e) {
|
||||
logger.error("decode(String, Class<T>)", e);
|
||||
} catch (JsonMappingException e) {
|
||||
logger.error("decode(String, Class<T>)", e);
|
||||
} catch (IOException e) {
|
||||
logger.error("decode(String, Class<T>)", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json array反序列化为对象
|
||||
*
|
||||
* @param json
|
||||
* @param typeReference
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T decode(String json, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return (T) objectMapper.readValue(json, typeReference);
|
||||
} catch (JsonParseException e) {
|
||||
logger.error("decode(String, JsonTypeReference<T>)", e);
|
||||
} catch (JsonMappingException e) {
|
||||
logger.error("decode(String, JsonTypeReference<T>)", e);
|
||||
} catch (IOException e) {
|
||||
logger.error("decode(String, JsonTypeReference<T>)", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
28
netty-pad/src/main/java/com/sv/netty/utils/LiveTime.java
Normal file
28
netty-pad/src/main/java/com/sv/netty/utils/LiveTime.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.sv.netty.utils;
|
||||
|
||||
/**
|
||||
* USER: douya
|
||||
* DATE: 2017-08-07
|
||||
*/
|
||||
public enum LiveTime {
|
||||
|
||||
SECONDS_15(15 * 1000), SECONDS_30(30 * 1000),
|
||||
|
||||
MINUTES_1(1 * 60 * 1000), MINUTES_2(2 * 60 * 1000), MINUTES_5(5 * 60 * 1000), MINUTES_10(10 * 60 * 1000), MINUTES_30(
|
||||
30 * 60 * 1000),
|
||||
|
||||
HOURS_1(1 * 60 * 60 * 1000), HOURS_2(2 * 60 * 60 * 1000), HOURS_5(5 * 60 * 60 * 1000), HOURS_12(
|
||||
12 * 60 * 60 * 1000),HOURS_36(36*60*60*1000),
|
||||
|
||||
DAYS_1(1 * 24 * 60 * 60 * 1000), DAYS_2(2 * 24 * 60 * 60 * 1000), DAYS_5(5 * 24 * 60 * 60 * 1000), DAYS_15(15
|
||||
* 24 * 60 * 60 * 1000L),DAYS_30(30* 24 * 60 * 60 * 1000L)
|
||||
|
||||
;
|
||||
public final long time;
|
||||
|
||||
LiveTime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
16
netty-pad/src/main/resources/config/application-dev.yml
Normal file
16
netty-pad/src/main/resources/config/application-dev.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://127.0.01:3306/smart_venue?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2b8&useAffectedRows=true&useSSL=false
|
||||
username: root
|
||||
password: 123456
|
||||
|
||||
jpa:
|
||||
show-sql: true
|
||||
|
||||
oss:
|
||||
accessKeyId: LTAIlbtS4W2Xe4OV
|
||||
accessKeySecret: qWMYkSfmXFtRoIv9q9OCbszcF9U7dX
|
||||
protocol: http
|
||||
name: smartvenue
|
||||
endPoint: http://oss-cn-beijing.aliyuncs.com
|
||||
url: https://smartvenue.oss-cn-beijing.aliyuncs.com/
|
||||
17
netty-pad/src/main/resources/config/application-prod.yml
Normal file
17
netty-pad/src/main/resources/config/application-prod.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://127.0.0.1:3306/smart_venue?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2b8&useAffectedRows=true
|
||||
username: root
|
||||
password: hyty1234
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
|
||||
jpa:
|
||||
show-sql: true
|
||||
|
||||
oss:
|
||||
accessKeyId: LTAIlbtS4W2Xe4OV
|
||||
accessKeySecret: qWMYkSfmXFtRoIv9q9OCbszcF9U7dX
|
||||
protocol: http
|
||||
name: smartvenue
|
||||
endPoint: http://oss-cn-beijing.aliyuncs.com
|
||||
url: https://smartvenue.oss-cn-beijing.aliyuncs.com/
|
||||
104
netty-pad/src/main/resources/config/application.yml
Normal file
104
netty-pad/src/main/resources/config/application.yml
Normal file
@@ -0,0 +1,104 @@
|
||||
server:
|
||||
port: 8021
|
||||
context-path: /netty
|
||||
tomcat:
|
||||
uri-encoding: utf-8
|
||||
|
||||
|
||||
|
||||
spring:
|
||||
profiles:
|
||||
include:
|
||||
-dev
|
||||
-prod
|
||||
active: dev
|
||||
|
||||
|
||||
# 数据库连接池配置
|
||||
druid:
|
||||
filters: stat
|
||||
initialSize: 1
|
||||
minIdle: 1
|
||||
maxActive: 40
|
||||
maxWait: 600000
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
validationQuery: SELECT 'x'
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
poolPreparedStatements: true
|
||||
maxPoolPreparedStatementPerConnectionSize: 20
|
||||
WebStatFilter:
|
||||
enabled: false
|
||||
urlPattern:
|
||||
exclusions:
|
||||
sessionStatMaxCount:
|
||||
sessionStatEnable:
|
||||
principalSessionName:
|
||||
principalCookieName:
|
||||
profileEnable:
|
||||
StatViewServlet:
|
||||
enabled: true
|
||||
urlPattern: /druid/*
|
||||
resetEnable: true
|
||||
loginUsername: druid
|
||||
loginPassword: druid
|
||||
allow:
|
||||
deny:
|
||||
aop:
|
||||
auto: true
|
||||
http:
|
||||
encoding:
|
||||
force: true
|
||||
charset: utf-8
|
||||
enabled: true
|
||||
|
||||
jackson:
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
property-naming-strategy: CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
|
||||
|
||||
redis:
|
||||
pool:
|
||||
max-active: 1000
|
||||
max-wait: -1
|
||||
max-idle: 8
|
||||
min-idle: 8
|
||||
timeout: 60000
|
||||
|
||||
# MyBatis Configuration
|
||||
mybatis:
|
||||
type-aliases-package: com.ydd.oms.entity
|
||||
config-location: classpath:mybatis/mybatis-config.xml
|
||||
mapper-locations: classpath:mybatis/mapper/*/*.xml
|
||||
|
||||
|
||||
face:
|
||||
url: 192.168.1.111
|
||||
account: test@test.com
|
||||
pwd: 123456
|
||||
|
||||
nettym:
|
||||
url: http://127.0.0.1:8021/netty/message/send
|
||||
|
||||
|
||||
#netty服务器配置
|
||||
netty:
|
||||
port: 56791
|
||||
boss:
|
||||
thread:
|
||||
count: 1
|
||||
worker:
|
||||
thread:
|
||||
count: 2
|
||||
so:
|
||||
keepalive: true
|
||||
backlog: 128
|
||||
reuseaddr: true
|
||||
tcp_nodelay: true
|
||||
|
||||
logging:
|
||||
level:
|
||||
com:
|
||||
sv:
|
||||
mapper: DEBUG
|
||||
46
netty-pad/src/main/resources/logback.xml
Normal file
46
netty-pad/src/main/resources/logback.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF8"?>
|
||||
<configuration>
|
||||
<jmxConfigurator />
|
||||
<property name="LOG_HOME" value="/home/log/pad_logs"/>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="COMMON-DEFAULT"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_HOME}/common-default.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_HOME}/common-default-%d{yyyy-MM-dd}.log
|
||||
</fileNamePattern>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="COMMON-ERROR"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_HOME}/common-error.log</file>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>ERROR</level>
|
||||
</filter>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_HOME}/common-error-%d{yyyy-MM-dd}.log
|
||||
</fileNamePattern>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- root -->
|
||||
<root level="info">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="COMMON-DEFAULT" />
|
||||
<appender-ref ref="COMMON-ERROR" />
|
||||
</root>
|
||||
</configuration>
|
||||
41
netty-pad/src/main/resources/mybatis/mybatis-config.xml
Normal file
41
netty-pad/src/main/resources/mybatis/mybatis-config.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE configuration
|
||||
PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
|
||||
|
||||
|
||||
<configuration>
|
||||
<settings>
|
||||
<setting name="cacheEnabled" value="true" />
|
||||
<!-- 打印sql日志 -->
|
||||
<setting name="logImpl" value="SLF4J" />
|
||||
</settings>
|
||||
<!--<settings>-->
|
||||
<!--<setting name="cacheEnabled" value="true"/>-->
|
||||
<!--<setting name="lazyLoadingEnabled" value="true"/>-->
|
||||
<!--<setting name="multipleResultSetsEnabled" value="true"/>-->
|
||||
<!--<setting name="useColumnLabel" value="true"/>-->
|
||||
<!--<setting name="useGeneratedKeys" value="false"/>-->
|
||||
<!--<setting name="autoMappingBehavior" value="PARTIAL"/>-->
|
||||
<!--<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>-->
|
||||
<!--<setting name="defaultExecutorType" value="SIMPLE"/>-->
|
||||
<!--<setting name="defaultStatementTimeout" value="25"/>-->
|
||||
<!--<setting name="defaultFetchSize" value="100"/>-->
|
||||
<!--<setting name="safeRowBoundsEnabled" value="false"/>-->
|
||||
<!--<setting name="mapUnderscoreToCamelCase" value="false"/>-->
|
||||
<!--<setting name="localCacheScope" value="SESSION"/>-->
|
||||
<!--<setting name="jdbcTypeForNull" value="OTHER"/>-->
|
||||
<!--<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>-->
|
||||
<!--</settings>-->
|
||||
|
||||
<typeAliases>
|
||||
<typeAlias alias="Integer" type="java.lang.Integer" />
|
||||
<typeAlias alias="Byte" type="java.lang.Byte"/>
|
||||
<typeAlias alias="String" type="java.lang.String" />
|
||||
<typeAlias alias="Long" type="java.lang.Long" />
|
||||
<typeAlias alias="HashMap" type="java.util.HashMap" />
|
||||
<typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />
|
||||
<typeAlias alias="ArrayList" type="java.util.ArrayList" />
|
||||
<typeAlias alias="LinkedList" type="java.util.LinkedList" />
|
||||
</typeAliases>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,13 @@
|
||||
package test.netty.nio.net;
|
||||
|
||||
import test.netty.nio.net.client.ClientThread;
|
||||
|
||||
/**
|
||||
* 开启一个客户端,
|
||||
*/
|
||||
public class ClientTest {
|
||||
public static void main(String[] args) {
|
||||
ClientThread instance = ClientThread.getInstance();
|
||||
instance.start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package test.netty.nio.net.client;
|
||||
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.handler.timeout.IdleState;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import test.netty.nio.net.dto.HeartBeat;
|
||||
import test.netty.nio.net.dto.Message;
|
||||
|
||||
/**
|
||||
* 通讯服务器请求处理
|
||||
*
|
||||
* @author peakren
|
||||
* @date 05/12/2017 10:27 PM
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
public class ClientHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final static String TAG = "ClientHandler";
|
||||
|
||||
private boolean hasRead = false;
|
||||
|
||||
@Override
|
||||
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelRegistered(ctx);
|
||||
// ClientTcpSession.getInstance().setContext(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelActive(ctx);
|
||||
//服务器连上以后立即模拟心跳返回
|
||||
ctx.writeAndFlush(getHbMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelInactive(ctx);
|
||||
// GlobalConfig.isConnected =false;
|
||||
ClientThread.getInstance().clearFuture();
|
||||
ClientThread.getInstance().restart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
super.channelRead(ctx, msg);
|
||||
// Message message = JsonMapper.fromJson(msg.toString(), Message.class);
|
||||
// MessageService.getInstance().execute(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
super.exceptionCaught(ctx, cause);
|
||||
// GlobalConfig.isConnected = false;
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取心跳返回消息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Message getHbMessage() {
|
||||
HeartBeat hb = new HeartBeat();
|
||||
// hb.setVersionCode(AppUtil.getVersionCode(StartApplication.getAppContext()));
|
||||
Message message = new Message();
|
||||
message.setCmdId(Cmd.HB.id);
|
||||
// message.setDeviceId(DeviceIdUtil.generateDeviceId(mContext));
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 心跳处理
|
||||
*
|
||||
* @param ctx
|
||||
* @param evt
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
if (IdleStateEvent.class.isAssignableFrom(evt.getClass())) {
|
||||
IdleStateEvent event = (IdleStateEvent) evt;
|
||||
if (event.state() == IdleState.ALL_IDLE) {
|
||||
ctx.writeAndFlush(getHbMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package test.netty.nio.net.client;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
|
||||
|
||||
public class ClientInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
private final static int TIME_HEART_BEAT = 20;
|
||||
|
||||
public ClientThread.ReConnectHandler reConnectHandler;
|
||||
public ClientHandler dmClientHandler;
|
||||
|
||||
public ClientInitializer(ClientThread.ReConnectHandler handler) {
|
||||
reConnectHandler = handler;
|
||||
}
|
||||
|
||||
public ClientInitializer(ClientThread.ReConnectHandler handler, ClientHandler dmClientHandler) {
|
||||
reConnectHandler = handler;
|
||||
this.dmClientHandler = dmClientHandler;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
pipeline.addLast("reconnect", reConnectHandler);
|
||||
pipeline.addLast("idleStateHandler", new IdleStateHandler(TIME_HEART_BEAT, TIME_HEART_BEAT, TIME_HEART_BEAT));
|
||||
pipeline.addLast(new MessageEncoder());
|
||||
pipeline.addFirst(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 4, 4, 0, 0));
|
||||
pipeline.addLast(new MessageDecoder());
|
||||
pipeline.addLast(dmClientHandler);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package test.netty.nio.net.client;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/**
|
||||
* 客户端通讯
|
||||
*
|
||||
* @author peakren
|
||||
* @date 07/12/2017 10:12 PM
|
||||
*/
|
||||
public class ClientThread extends Thread{
|
||||
|
||||
private static ClientThread instance;
|
||||
|
||||
private volatile EventLoopGroup workerGroup;
|
||||
private volatile Bootstrap bootstrap;
|
||||
private volatile boolean closed = false;
|
||||
private String remoteHost;
|
||||
private int remotePort;
|
||||
|
||||
private ChannelFuture future;
|
||||
|
||||
|
||||
public static ClientThread getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (ClientThread.class) {
|
||||
if (instance == null) {
|
||||
instance = new ClientThread("127.0.0.1", 56791);
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ClientThread(String remoteHost, int remotePort) {
|
||||
this.remoteHost = remoteHost;
|
||||
this.remotePort = remotePort;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
|
||||
closed = false;
|
||||
workerGroup = new NioEventLoopGroup();
|
||||
bootstrap = new Bootstrap();
|
||||
bootstrap.group(workerGroup);
|
||||
bootstrap.channel(NioSocketChannel.class);
|
||||
bootstrap.option(ChannelOption.TCP_NODELAY, true);
|
||||
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
|
||||
|
||||
ReConnectHandler reConnectHandler = new ReConnectHandler();
|
||||
ClientHandler dmClientHandler = new ClientHandler();
|
||||
ClientInitializer channelInitializer = new ClientInitializer(reConnectHandler, dmClientHandler);
|
||||
|
||||
bootstrap.handler(channelInitializer);
|
||||
doConnect();
|
||||
}
|
||||
|
||||
public void clearFuture(){
|
||||
future = null;
|
||||
}
|
||||
|
||||
public void doConnect() {
|
||||
System.out.println("现在开始链接了");
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
System.out.println("连接 = " + remoteHost + " " + remotePort);
|
||||
future = bootstrap.connect(new InetSocketAddress(remoteHost, remotePort));
|
||||
future.addListener(new ChannelFutureListener() {
|
||||
public void operationComplete(ChannelFuture f) throws Exception {
|
||||
f.channel().eventLoop().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!f.isSuccess()) {
|
||||
doConnect();
|
||||
System.out.println("等待连接");
|
||||
} else {
|
||||
System.out.println("已连接");
|
||||
}
|
||||
}
|
||||
}, 2, TimeUnit.SECONDS);
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void close() {
|
||||
closed = true;
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
|
||||
public void restart() {
|
||||
close();
|
||||
run();
|
||||
}
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
public class ReConnectHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelInactive(ctx);
|
||||
System.out.println("inactive");
|
||||
ctx.channel().eventLoop().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doConnect();
|
||||
}
|
||||
}, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package test.netty.nio.net.client;
|
||||
|
||||
/**
|
||||
* 消息协议指令定义
|
||||
*
|
||||
* @Author peakren
|
||||
* @Date 08/12/2017 11:51 AM
|
||||
*/
|
||||
public enum Cmd {
|
||||
|
||||
|
||||
HB("hb", "心跳"),
|
||||
|
||||
FACEID("faceid", "人脸识别"),
|
||||
|
||||
FACEID_RESPONSE("faceid_response_upload", "识别结果"),
|
||||
|
||||
RECEV_FACE_IMAGE("recev_face_image", "接收人脸照片"),
|
||||
|
||||
RECEV_FACE_IMAGE_R("recev_face_image_r", "返回上传图片结果"),
|
||||
|
||||
OPEN_DOOR("open_door", "开门禁");
|
||||
|
||||
|
||||
public String id;
|
||||
|
||||
public String text;
|
||||
|
||||
Cmd(String id, String text) {
|
||||
this.id = id;
|
||||
this.text = text;
|
||||
|
||||
}
|
||||
|
||||
public static Cmd getCmd(String id) {
|
||||
for (Cmd cmd : Cmd.values()) {
|
||||
if (cmd.id.equalsIgnoreCase(id)) {
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
return Cmd.HB;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package test.netty.nio.net.client;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
|
||||
/**
|
||||
* 自定义解码器,解决粘包和分包问题
|
||||
*
|
||||
* @author peakren
|
||||
* @date 07/12/2017 10:03 PM
|
||||
*/
|
||||
public class CustomDecoder extends LengthFieldBasedFrameDecoder {
|
||||
|
||||
public CustomDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength) {
|
||||
super(maxFrameLength, lengthFieldOffset, lengthFieldLength);
|
||||
}
|
||||
|
||||
public CustomDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip) {
|
||||
super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip);
|
||||
}
|
||||
|
||||
public CustomDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
|
||||
super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, failFast);
|
||||
}
|
||||
|
||||
public CustomDecoder(ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
|
||||
super(byteOrder, maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, failFast);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
|
||||
return super.decode(ctx, in);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package test.netty.nio.net.client;
|
||||
|
||||
/**
|
||||
* Created by hehelt on 16/2/26.
|
||||
*/
|
||||
public class DataConfig {
|
||||
|
||||
public static final int MAGIC_WORD = 0x9DDD;
|
||||
public static final int MAGIC_WORD_INDEX = 0;
|
||||
public static final int LENGTH_INDEX = 4;
|
||||
public static final int DATA_INDEX = 8;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package test.netty.nio.net.client;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by hehelt on 16/2/26.
|
||||
* <p/>
|
||||
* 解码器
|
||||
*/
|
||||
public class MessageDecoder extends ByteToMessageDecoder {
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
if (in.capacity() >= DataConfig.LENGTH_INDEX) {
|
||||
int magicWord = in.readInt();
|
||||
if (magicWord == DataConfig.MAGIC_WORD) {
|
||||
int length = in.readInt();
|
||||
byte[] msg = new byte[length];
|
||||
in.readBytes(msg);
|
||||
String message = new String(msg, "utf-8");
|
||||
out.add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析从服务器接受的消息
|
||||
*
|
||||
* @param buf
|
||||
* @return
|
||||
*/
|
||||
private String getMessage(ByteBuf buf) {
|
||||
|
||||
byte[] con = new byte[buf.readableBytes()];
|
||||
buf.readBytes(con);
|
||||
try {
|
||||
return new String(con, "utf-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package test.netty.nio.net.client;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import test.netty.nio.net.dto.JsonMapper;
|
||||
import test.netty.nio.net.dto.Message;
|
||||
|
||||
/**
|
||||
* 自定义编码器, 1个字节固定头+4个字节长度+内容
|
||||
*/
|
||||
public class MessageEncoder extends MessageToByteEncoder<Message> {
|
||||
|
||||
private String charset = "utf-8";
|
||||
private final static String TAG = "MessageEncoder";
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Message message, ByteBuf out) throws Exception {
|
||||
out.writeInt(DataConfig.MAGIC_WORD);
|
||||
String msg = JsonMapper.toJson(message);
|
||||
out.writeInt(msg.getBytes(charset).length);
|
||||
out.writeBytes(msg.getBytes(charset));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package test.netty.nio.net.dto;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
public class AddFaceResponse {
|
||||
|
||||
/**
|
||||
* code : 0
|
||||
* data : {"company_id":1,"id":4,"origin_url":"/static/upload/origin/2018-08-09/v2_1ea4b7847d1ea56b773aec99441af52dcbf9ca7d.jpg","quality":0.992649,"subject_id":null,"url":"/static/upload/photo/2018-08-09/v2_fa9dcfd045ff5232aa446f5645cbb031eef7ac74.jpg","version":7}
|
||||
* page : {}
|
||||
*/
|
||||
|
||||
@Expose
|
||||
private int code;
|
||||
|
||||
@Expose
|
||||
private DataBean data;
|
||||
|
||||
@Expose
|
||||
private PageBean page;
|
||||
|
||||
@Expose
|
||||
private String desc;
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public DataBean getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(DataBean data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public PageBean getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
public void setPage(PageBean page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public static class DataBean {
|
||||
/**
|
||||
* company_id : 1
|
||||
* id : 4
|
||||
* origin_url : /static/upload/origin/2018-08-09/v2_1ea4b7847d1ea56b773aec99441af52dcbf9ca7d.jpg
|
||||
* quality : 0.992649
|
||||
* subject_id : null
|
||||
* url : /static/upload/photo/2018-08-09/v2_fa9dcfd045ff5232aa446f5645cbb031eef7ac74.jpg
|
||||
* version : 7
|
||||
*/
|
||||
|
||||
@Expose
|
||||
private int company_id;
|
||||
@Expose
|
||||
private int id;
|
||||
@Expose
|
||||
private String origin_url;
|
||||
@Expose
|
||||
private double quality;
|
||||
@Expose
|
||||
private Object subject_id;
|
||||
@Expose
|
||||
private String url;
|
||||
@Expose
|
||||
private int version;
|
||||
|
||||
public int getCompany_id() {
|
||||
return company_id;
|
||||
}
|
||||
|
||||
public void setCompany_id(int company_id) {
|
||||
this.company_id = company_id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getOrigin_url() {
|
||||
return origin_url;
|
||||
}
|
||||
|
||||
public void setOrigin_url(String origin_url) {
|
||||
this.origin_url = origin_url;
|
||||
}
|
||||
|
||||
public double getQuality() {
|
||||
return quality;
|
||||
}
|
||||
|
||||
public void setQuality(double quality) {
|
||||
this.quality = quality;
|
||||
}
|
||||
|
||||
public Object getSubject_id() {
|
||||
return subject_id;
|
||||
}
|
||||
|
||||
public void setSubject_id(Object subject_id) {
|
||||
this.subject_id = subject_id;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(int version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PageBean {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package test.netty.nio.net.dto;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Created by peakren on 19/01/2018.
|
||||
*/
|
||||
|
||||
public class BaseDto implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 3139438146199448677L;
|
||||
|
||||
@Expose
|
||||
private String cmdId; //指令
|
||||
|
||||
@Expose
|
||||
private String deviceId; //设备ID
|
||||
|
||||
/**
|
||||
* 客户端IP
|
||||
*/
|
||||
@Expose
|
||||
private String clientIp;
|
||||
|
||||
/**
|
||||
* 状态码 0正常 1错误
|
||||
*/
|
||||
@Expose
|
||||
private int errorCode = 0;
|
||||
|
||||
/**
|
||||
* 错误返回信息
|
||||
*/
|
||||
@Expose
|
||||
private String errorMsg;
|
||||
|
||||
|
||||
public String getCmdId() {
|
||||
return cmdId;
|
||||
}
|
||||
|
||||
public void setCmdId(String cmdId) {
|
||||
this.cmdId = cmdId;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端IP,获取的内网IP
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getClientIp() {
|
||||
return clientIp;
|
||||
}
|
||||
|
||||
public void setClientIp(String clientIp) {
|
||||
this.clientIp = clientIp;
|
||||
}
|
||||
|
||||
public int getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public void setErrorCode(int errorCode) {
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public String getErrorMsg() {
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
public void setErrorMsg(String errorMsg) {
|
||||
this.errorMsg = errorMsg;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package test.netty.nio.net.dto;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* 会员基本信息
|
||||
* MemberDto.java
|
||||
*
|
||||
* @author peakren
|
||||
* @date 2018/12/20 8:39 PM
|
||||
*/
|
||||
public class FaceImageDto implements Serializable {
|
||||
|
||||
|
||||
@Expose
|
||||
private Integer faceId;
|
||||
|
||||
@Expose
|
||||
private Integer memberId;
|
||||
|
||||
|
||||
@Expose
|
||||
private String faceImage;
|
||||
|
||||
|
||||
public Integer getFaceId() {
|
||||
return faceId;
|
||||
}
|
||||
|
||||
public void setFaceId(Integer faceId) {
|
||||
this.faceId = faceId;
|
||||
}
|
||||
|
||||
public Integer getMemberId() {
|
||||
return memberId;
|
||||
}
|
||||
|
||||
public void setMemberId(Integer memberId) {
|
||||
this.memberId = memberId;
|
||||
}
|
||||
|
||||
public String getFaceImage() {
|
||||
return faceImage;
|
||||
}
|
||||
|
||||
public void setFaceImage(String faceImage) {
|
||||
this.faceImage = faceImage;
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,44 @@
|
||||
package test.netty.nio.net.dto;
|
||||
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 客户端心跳数据包
|
||||
* HeartBeat.java
|
||||
*
|
||||
* @author peakren
|
||||
* @date 07/12/2017 10:23 PM
|
||||
*/
|
||||
public class HeartBeat implements Serializable {
|
||||
|
||||
@Expose
|
||||
private String versionCode; //客户端版本号
|
||||
|
||||
@Expose
|
||||
private String apkUrl; //升级的软件下载地址
|
||||
|
||||
|
||||
public String getVersionCode() {
|
||||
return versionCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端版本号
|
||||
*
|
||||
* @param versionCode
|
||||
*/
|
||||
public void setVersionCode(String versionCode) {
|
||||
this.versionCode = versionCode;
|
||||
}
|
||||
|
||||
public String getApkUrl() {
|
||||
return apkUrl;
|
||||
}
|
||||
|
||||
public void setApkUrl(String apkUrl) {
|
||||
this.apkUrl = apkUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package test.netty.nio.net.dto;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by ranfi on 12/9/14.
|
||||
*/
|
||||
public class JsonMapper {
|
||||
|
||||
private final static Gson gson;
|
||||
|
||||
static {
|
||||
GsonBuilder builder = new GsonBuilder();
|
||||
builder.setDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
builder.excludeFieldsWithoutExposeAnnotation();
|
||||
gson = builder.create();
|
||||
}
|
||||
|
||||
private JsonMapper() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Object可以是POJO,也可以是Collection或数组。 如果对象为Null, 返回"null". 如果集合为空集合, 返回"[]".
|
||||
*
|
||||
* @param obj
|
||||
* @return
|
||||
*/
|
||||
public static String toJson(Object obj) {
|
||||
return gson.toJson(obj);
|
||||
}
|
||||
|
||||
|
||||
public static String toJson(Object obj, Type type) {
|
||||
return gson.toJson(obj, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化POJO或简单Collection如List<String>.
|
||||
* <p/>
|
||||
* 如果JSON字符串为Null或"null"字符串, 返回Null. 如果JSON字符串为"[]", 返回空集合.
|
||||
* <p/>
|
||||
* 如需反序列化复杂Collection如List<MyBean>, 请使用fromJson(String, JavaType)
|
||||
*/
|
||||
public static <T> T fromJson(String jsonValue, Class<T> clazz) {
|
||||
return gson.fromJson(jsonValue, clazz);
|
||||
}
|
||||
|
||||
public static <T> T fromJson(String jsonValue, Type type) {
|
||||
return gson.fromJson(jsonValue, type);
|
||||
}
|
||||
|
||||
public static <T> List<T> fromJson(String jsonValue) {
|
||||
return gson.fromJson(jsonValue, new TypeToken<List<T>>() {
|
||||
}.getType());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package test.netty.nio.net.dto;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
|
||||
/**
|
||||
* 会员基本信息
|
||||
* MemberDto.java
|
||||
*
|
||||
* @author peakren
|
||||
* @date 2018/12/20 8:39 PM
|
||||
*/
|
||||
public class MemberDto implements Serializable {
|
||||
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
@Expose
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
@Expose
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 手机号码
|
||||
*/
|
||||
@Expose
|
||||
private String mobile;
|
||||
|
||||
/**
|
||||
* 余额
|
||||
*/
|
||||
@Expose
|
||||
private BigDecimal amount;
|
||||
|
||||
/**
|
||||
* 场地名称
|
||||
*/
|
||||
@Expose
|
||||
private String placeName;
|
||||
|
||||
/**
|
||||
* 会员卡名称
|
||||
*/
|
||||
@Expose
|
||||
private String cardName;
|
||||
|
||||
@Expose
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 1成功进场 0不允许进场
|
||||
*/
|
||||
@Expose
|
||||
private int code;
|
||||
|
||||
@Expose
|
||||
private BigDecimal placePrice;
|
||||
|
||||
@Expose
|
||||
private boolean first;
|
||||
|
||||
public BigDecimal getPlacePrice() {
|
||||
return placePrice;
|
||||
}
|
||||
|
||||
public void setPlacePrice(BigDecimal placePrice) {
|
||||
this.placePrice = placePrice;
|
||||
}
|
||||
|
||||
public boolean isFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public void setFirst(boolean first) {
|
||||
this.first = first;
|
||||
}
|
||||
|
||||
public String getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(String avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getMobile() {
|
||||
return mobile;
|
||||
}
|
||||
|
||||
public void setMobile(String mobile) {
|
||||
this.mobile = mobile;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public String getPlaceName() {
|
||||
return placeName;
|
||||
}
|
||||
|
||||
public void setPlaceName(String placeName) {
|
||||
this.placeName = placeName;
|
||||
}
|
||||
|
||||
public String getCardName() {
|
||||
return cardName;
|
||||
}
|
||||
|
||||
public void setCardName(String cardName) {
|
||||
this.cardName = cardName;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package test.netty.nio.net.dto;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
/**
|
||||
* <pre>上位机与通讯服务器的数据协议格式</pre>
|
||||
*
|
||||
* @author peakren
|
||||
* @date 07/12/2017 9:57 PM
|
||||
*/
|
||||
public class Message extends BaseDto {
|
||||
|
||||
private static final long serialVersionUID = -7944124768291562453L;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
@Expose
|
||||
private MemberDto result;
|
||||
|
||||
|
||||
@Expose
|
||||
private FaceImageDto faceImage;
|
||||
|
||||
/**
|
||||
* 机器识别返回字符串
|
||||
*/
|
||||
@Expose
|
||||
private String content;
|
||||
|
||||
|
||||
public int getDoor() {
|
||||
return door;
|
||||
}
|
||||
|
||||
public void setDoor(int door) {
|
||||
this.door = door;
|
||||
}
|
||||
|
||||
@Expose
|
||||
private int door;
|
||||
|
||||
|
||||
public MemberDto getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(MemberDto result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public FaceImageDto getFaceImage() {
|
||||
return faceImage;
|
||||
}
|
||||
|
||||
public void setFaceImage(FaceImageDto faceImage) {
|
||||
this.faceImage = faceImage;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user