@@ -45,6 +45,7 @@ | |||
<artifactId>flyway-core</artifactId> | |||
<version>5.2.4</version> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
@@ -1,9 +1,13 @@ | |||
package com.iformall; | |||
import com.iformall.annotation.BaseEnableSwagger; | |||
import com.iformall.haikang.netty.NioSocketServer; | |||
import org.mybatis.spring.annotation.MapperScan; | |||
import org.rocketmq.starter.annotation.EnableRocketMQ; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.boot.CommandLineRunner; | |||
import org.springframework.boot.SpringApplication; | |||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||
import org.springframework.context.annotation.Bean; | |||
@@ -23,7 +27,7 @@ import org.springframework.scheduling.annotation.EnableAsync; | |||
@EnableAsync | |||
@EnableRocketMQ | |||
@EnableAspectJAutoProxy(exposeProxy = true) | |||
public class CApplication { | |||
public class CApplication implements CommandLineRunner{ | |||
@Value("${fm.exception}") | |||
private boolean fmException; | |||
@@ -45,6 +49,15 @@ public class CApplication { | |||
@Value("${tcp.server.port}") | |||
private int tcpServerPort; | |||
@Autowired | |||
private NioSocketServer nioSocketServer; | |||
@Override | |||
public void run(String... args) throws Exception { | |||
nioSocketServer.start(tcpServerPort); | |||
} | |||
@Bean | |||
public boolean isFmException() { | |||
@@ -13,7 +13,7 @@ import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.stereotype.Component; | |||
@Component | |||
//@Component | |||
public class TcpClient { | |||
private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
@@ -23,7 +23,7 @@ import com.alibaba.fastjson.JSONArray; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.iformall.haikang.HKPeopleNumberFactory; | |||
@Component | |||
//@Component | |||
public class TcpServer { | |||
private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
@@ -54,26 +54,42 @@ public class TcpServer { | |||
} | |||
private void handleClient(Socket clientSocket) { | |||
try ( | |||
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); | |||
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true) | |||
) { | |||
BufferedReader in = null; | |||
PrintWriter out = null; | |||
try { | |||
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); | |||
out = new PrintWriter(clientSocket.getOutputStream(), true); | |||
String message; | |||
while ((message = in.readLine()) != null) { | |||
log.info("Received: {}", message); | |||
// | |||
handleMessage(out, message); | |||
//handleMessage(out, message); | |||
out.flush(); | |||
// Echo back to client | |||
//out.println("Server: " + message); | |||
} | |||
} catch (IOException e) { | |||
log.error("Client connection error: {}", e.getMessage()); | |||
log.error("Client connection error: {}", e); | |||
} finally { | |||
try { | |||
clientSocket.close(); | |||
} catch (IOException e) { | |||
log.error("Failed to close socket: {}", e.getMessage()); | |||
} | |||
// try { | |||
// if (null != in) { | |||
// in.close(); | |||
// } | |||
// } catch (Exception e) { | |||
// log.error("Failed to close socket: {}", e); | |||
// } | |||
// try { | |||
// if (null != out) { | |||
// out.close(); | |||
// } | |||
// } catch (Exception e) { | |||
// log.error("Failed to close socket: {}", e); | |||
// } | |||
// try { | |||
// clientSocket.close(); | |||
// } catch (IOException e) { | |||
// log.error("Failed to close socket: {}", e); | |||
// } | |||
} | |||
} | |||
@@ -52,6 +52,13 @@ | |||
<artifactId>jsoup</artifactId> | |||
<version>1.10.2</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>io.netty</groupId> | |||
<artifactId>netty-all</artifactId> | |||
<version>4.1.68.Final</version> | |||
<!-- 使用适合你项目的版本 --> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -0,0 +1,17 @@ | |||
package com.iformall.haikang.netty; | |||
import java.util.List; | |||
import io.netty.buffer.ByteBuf; | |||
import io.netty.channel.ChannelHandlerContext; | |||
import io.netty.handler.codec.MessageToMessageDecoder; | |||
public class HexMessageDecoder extends MessageToMessageDecoder<ByteBuf>{ | |||
@Override | |||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) { | |||
byte[] bytes = new byte[in.readableBytes()]; | |||
in.readBytes(bytes); | |||
String hexString = HexUtils.byteArrayToHexString(bytes); | |||
out.add(hexString); | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
package com.iformall.haikang.netty; | |||
import io.netty.buffer.ByteBuf; | |||
import io.netty.buffer.Unpooled; | |||
import io.netty.channel.ChannelHandlerContext; | |||
import io.netty.handler.codec.MessageToByteEncoder; | |||
public class HexMessageEncoder extends MessageToByteEncoder<String>{ | |||
@Override | |||
protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception { | |||
byte[] bytes = HexUtils.hexStringToByteArray(msg); | |||
ByteBuf buffer = Unpooled.wrappedBuffer(bytes); | |||
out.writeBytes(buffer); | |||
} | |||
} |
@@ -0,0 +1,93 @@ | |||
package com.iformall.haikang.netty; | |||
public class HexUtils { | |||
public static String byteArrayToHexString(byte[] data) { | |||
StringBuilder hexString = new StringBuilder(); | |||
for (byte b : data) { | |||
hexString.append(String.format("%02X ", b)); | |||
} | |||
return hexString.toString().trim(); | |||
} | |||
public static byte[] hexStringToByteArray(String s) { | |||
s = s.replaceAll("\\s", ""); // 去除所有空白字符 | |||
int len = s.length(); | |||
if (len % 2 != 0) { | |||
throw new IllegalArgumentException("Invalid hexadecimal string length"); | |||
} | |||
byte[] data = new byte[len / 2]; | |||
for (int i = 0; i < len; i += 2) { | |||
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) | |||
+ Character.digit(s.charAt(i + 1), 16)); | |||
} | |||
return data; | |||
} | |||
/** | |||
* 将16进制字符串转换为ASCII字符串 | |||
* @param hexString | |||
* @return | |||
*/ | |||
public static String hexStringToAscii(String hexString) { | |||
byte[] bytes = hexStringToByteArray(hexString); | |||
return new String(bytes); | |||
} | |||
/** | |||
* 将16进制字符串转换为十进制整型 | |||
* @param hexString | |||
* @return | |||
*/ | |||
public static long hexStringToDecimal(String hexString) { | |||
hexString = hexString.replaceAll("\\s", ""); // 去除所有空白字符 | |||
long decimalValue = 0; | |||
for (int i = 0; i < hexString.length(); i++) { | |||
char c = hexString.charAt(i); | |||
int value = Character.digit(c, 16); // 获取当前字符的10进制值 | |||
decimalValue = decimalValue * 16 + value; | |||
} | |||
return decimalValue; | |||
} | |||
/** | |||
* 将16进制字符串转换为浮点数 | |||
* @param hexString | |||
* @return | |||
*/ | |||
public static float hexStringToFloat(String hexString) { | |||
hexString = hexString.replaceAll("\\s", ""); // 去除所有空白字符 | |||
byte[] bytes = hexStringToByteArray(hexString); | |||
return bytesToFloat(bytes); | |||
} | |||
/** | |||
* 格式化16进制字符串 | |||
* @param hexString | |||
* @return | |||
*/ | |||
public static String formatHexString(String hexString) { | |||
StringBuilder formattedString = new StringBuilder(); | |||
for (int i = 0; i < hexString.length(); i += 2) { | |||
if (i > 0) { | |||
formattedString.append(" "); | |||
} | |||
formattedString.append(hexString.substring(i, i + 2)); | |||
} | |||
return formattedString.toString(); | |||
} | |||
private static float bytesToFloat(byte[] bytes) { | |||
int intBits = 0; | |||
for (int i = 0; i < 4; i++) { | |||
intBits |= (bytes[i] & 0xFF) << (24 - 8 * i); | |||
} | |||
return Float.intBitsToFloat(intBits); | |||
} | |||
} |
@@ -0,0 +1,41 @@ | |||
package com.iformall.haikang.netty; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import io.netty.channel.ChannelHandlerContext; | |||
import io.netty.channel.ChannelInboundHandlerAdapter; | |||
public class IpRejectHandler extends ChannelInboundHandlerAdapter{ | |||
private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
// 拒绝连接的IP列表 | |||
private static final String[] BLOCKED_IPS = {}; | |||
@Override | |||
public void channelActive(ChannelHandlerContext ctx) throws Exception { | |||
String remoteAddress = ctx.channel().remoteAddress().toString(); | |||
if (isBlocked(remoteAddress)) { | |||
log.error("netty tcp 拒绝连接: " + remoteAddress); | |||
ctx.close(); | |||
} else { | |||
log.info("netty tcp 允许连接: " + remoteAddress); | |||
} | |||
} | |||
@Override | |||
public void channelInactive(ChannelHandlerContext ctx) throws Exception { | |||
String remoteAddress = ctx.channel().remoteAddress().toString(); | |||
log.info("netty tcp 设备已断开连接: " + remoteAddress); | |||
super.channelInactive(ctx); | |||
} | |||
private boolean isBlocked(String remoteAddress) { | |||
for (String blockedIp : BLOCKED_IPS) { | |||
if (remoteAddress.contains(blockedIp)) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
} |
@@ -0,0 +1,132 @@ | |||
package com.iformall.haikang.netty; | |||
import java.io.PrintWriter; | |||
import java.util.HashMap; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | |||
import org.springframework.stereotype.Component; | |||
import com.alibaba.fastjson.JSON; | |||
import com.alibaba.fastjson.JSONArray; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.iformall.haikang.HKPeopleNumberFactory; | |||
import io.netty.channel.ChannelHandlerContext; | |||
import io.netty.channel.SimpleChannelInboundHandler; | |||
@Component | |||
public class NioSocketHandler extends SimpleChannelInboundHandler<String>{ | |||
private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
private String hexData = ""; | |||
private HKPeopleNumberFactory peopleNumberFactory; | |||
private final ThreadPoolTaskExecutor threadPoolTaskExecutor; | |||
public NioSocketHandler(ThreadPoolTaskExecutor threadPoolTaskExecutor,HKPeopleNumberFactory peopleNumberFactory) { | |||
this.threadPoolTaskExecutor = threadPoolTaskExecutor; | |||
this.peopleNumberFactory = peopleNumberFactory; | |||
} | |||
@Override | |||
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { | |||
// 异步处理消息 | |||
threadPoolTaskExecutor.execute(() -> { | |||
// 你的处理逻辑 | |||
String clientId = ctx.channel().id().asShortText(); | |||
log.info("netty tcp 接收到用户id为"+clientId+"的消息: " + msg); | |||
// 应答报文,根据要求应答 | |||
String repeak = msg; | |||
handleMessage(ctx,msg); | |||
// 应答 | |||
//ctx.writeAndFlush(repeak); | |||
}); | |||
} | |||
@Override | |||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { | |||
cause.printStackTrace(); | |||
ctx.close(); | |||
} | |||
//编号2代表需要等待时长,编号3代表当前排队人数,编号4代表当前场内出租车,编号5代表在途出租车 | |||
private static final int[] types = new int[] {2,3,4,5}; | |||
private void handleMessage(ChannelHandlerContext ctx,String message) { | |||
if (StringUtils.isBlank(message)) { | |||
return ; | |||
} | |||
JSONObject mo = JSON.parseObject(message); | |||
if (null == mo) { | |||
return ; | |||
} | |||
String func = mo.getString("func"); | |||
//心跳 | |||
if ("KeepOnline".equals(func)) { | |||
Map keepMap = new LinkedHashMap(); | |||
String dest = mo.getString("dest"); | |||
String source = mo.getString("source"); | |||
keepMap.put("dest", source); | |||
keepMap.put("source", dest); | |||
keepMap.put("func", "AckKeepOnline"); | |||
JSONArray datas = new JSONArray(); | |||
Map dm = new HashMap(); | |||
dm.put("node", "OK"); | |||
datas.add(dm); | |||
keepMap.put("data", datas); | |||
String xintiaoresult = JSON.toJSONString(keepMap); | |||
log.debug("haikang keepOnline result:"+xintiaoresult); | |||
ctx.writeAndFlush(xintiaoresult); | |||
//给灯箱发送 | |||
for (int i = 0 ; i < types.length ; i++) { | |||
Map dxobj = new LinkedHashMap(); | |||
dxobj.put("dest", source); | |||
dxobj.put("source", dest); | |||
dxobj.put("func", "ControlData"); | |||
JSONArray datas1 = new JSONArray(); | |||
Map dm1 = new HashMap(); | |||
dm1.put("node", generateNode(types[i],source)); | |||
datas1.add(dm1); | |||
dxobj.put("data", datas1); | |||
String dengxiangcontent = JSON.toJSONString(dxobj); | |||
log.debug("haikang send result:"+dengxiangcontent); | |||
ctx.writeAndFlush(dengxiangcontent); | |||
} | |||
} | |||
} | |||
//编号2代表需要等待时长,编号3代表当前排队人数,编号4代表当前场内出租车,编号5代表在途出租车 | |||
private String generateNode(int type,String sn) { | |||
StringBuffer sb = new StringBuffer("0-5-").append(type).append("-"); | |||
int value = 0; | |||
switch (type) { | |||
case 2: | |||
value = 1;//查询等待时长 | |||
break; | |||
case 3: | |||
value = peopleNumberFactory.getPeopleNumber();;//查询当前排队人数 | |||
break; | |||
case 4: | |||
value = 1;//查询场内出租车 | |||
break; | |||
case 5: | |||
value = 1;//查询在途出租车 | |||
break; | |||
default: | |||
break; | |||
} | |||
sb.append(value).append("-0-1-"+sn); | |||
return sb.toString(); | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
package com.iformall.haikang.netty; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | |||
import org.springframework.stereotype.Component; | |||
import com.iformall.haikang.HKPeopleNumberFactory; | |||
import io.netty.channel.ChannelInitializer; | |||
import io.netty.channel.ChannelPipeline; | |||
import io.netty.channel.socket.SocketChannel; | |||
import io.netty.handler.codec.string.StringDecoder; | |||
import io.netty.handler.codec.string.StringEncoder; | |||
@Component | |||
public class NioSocketInitializer extends ChannelInitializer<SocketChannel>{ | |||
private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
@Autowired | |||
private ThreadPoolTaskExecutor threadPoolTaskExecutor; | |||
@Autowired | |||
private HKPeopleNumberFactory peopleNumberFactory; | |||
public NioSocketInitializer(ThreadPoolTaskExecutor threadPoolTaskExecutor) { | |||
this.threadPoolTaskExecutor = threadPoolTaskExecutor; | |||
} | |||
@Override | |||
protected void initChannel(SocketChannel ch) { | |||
ChannelPipeline pipeline = ch.pipeline(); | |||
// tcp报文解码器,接收后自动调用解码 | |||
//pipeline.addLast(new HexMessageDecoder()); | |||
// tcp报文编码器,应答时自动编码 | |||
//pipeline.addLast(new HexMessageEncoder()); | |||
// 如果socket传输的是字符串类型,请使用StringDecoder和StringEncoder | |||
pipeline.addLast(new StringDecoder()); | |||
pipeline.addLast(new StringEncoder()); | |||
// 初始化并添加你的业务处理类 | |||
//ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); | |||
//threadPoolTaskExecutor.initialize(); // 初始化线程池 | |||
// 使用自定义线程池处理业务逻辑 | |||
log.info(">>>>peopleNumberFactory:"+peopleNumberFactory); | |||
pipeline.addLast(new NioSocketHandler(threadPoolTaskExecutor,peopleNumberFactory)); | |||
// 连接策略 | |||
pipeline.addLast(new IpRejectHandler()); | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
package com.iformall.haikang.netty; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | |||
import org.springframework.stereotype.Component; | |||
import io.netty.bootstrap.ServerBootstrap; | |||
import io.netty.channel.ChannelFuture; | |||
import io.netty.channel.ChannelOption; | |||
import io.netty.channel.EventLoopGroup; | |||
import io.netty.channel.nio.NioEventLoopGroup; | |||
import io.netty.channel.socket.nio.NioServerSocketChannel; | |||
@Component | |||
public class NioSocketServer { | |||
private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
@Autowired | |||
private ThreadPoolTaskExecutor threadPoolTaskExecutor; | |||
private EventLoopGroup bossGroup; | |||
private EventLoopGroup workerGroup; | |||
private ChannelFuture channelFuture; | |||
public void start(int port) throws Exception { | |||
bossGroup = new NioEventLoopGroup(1); // 主线程组 | |||
workerGroup = new NioEventLoopGroup(); // 工作线程组 | |||
try { | |||
ServerBootstrap b = new ServerBootstrap(); | |||
b.group(bossGroup, workerGroup) | |||
.channel(NioServerSocketChannel.class) | |||
.childHandler(new NioSocketInitializer(threadPoolTaskExecutor)) | |||
.option(ChannelOption.SO_BACKLOG, 128) | |||
.childOption(ChannelOption.SO_KEEPALIVE, true); | |||
channelFuture = b.bind(port).sync(); | |||
log.info("netty tcp NIO Socket服务器已启动,监听端口:" + port); | |||
channelFuture.channel().closeFuture().sync(); | |||
} finally { | |||
bossGroup.shutdownGracefully(); | |||
workerGroup.shutdownGracefully(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
package com.iformall.haikang.netty; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | |||
@Configuration | |||
public class ThreadPoolConfig { | |||
@Bean | |||
public ThreadPoolTaskExecutor threadPoolTaskExecutor() { | |||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); | |||
executor.setCorePoolSize(10); // 核心线程数 | |||
executor.setMaxPoolSize(20); // 最大线程数 | |||
executor.setQueueCapacity(100); // 队列容量 | |||
executor.setThreadNamePrefix("NIOSocket-ThreadPool-"); // 线程前缀 | |||
executor.initialize(); | |||
return executor; | |||
} | |||
} |