Netty实战流程(服务提供者)

一、配置文件application.properties添加配置

#netty 配置

server.ip=127.0.0.1

server.ip.port=6060

二、创建NettyServer

@Slf4j
@Component
public class NettyServer {
	 @Autowired
	 private ServerChannelInitializer serverInit;
	 private final EventLoopGroup parentGroup = new NioEventLoopGroup();
	 private final EventLoopGroup childGroup = new NioEventLoopGroup();
	 
	 private Channel channel;
	
	 /**
	  * 启动服务
	  * @param address
	  * @return
	  */
	public ChannelFuture start(InetSocketAddress address) {
		ChannelFuture f = null;
		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(parentGroup, childGroup)
			.channel(NioServerSocketChannel.class)
			.childHandler(serverInit)
			.childOption(ChannelOption.SO_KEEPALIVE, true)
            .childOption(ChannelOption.TCP_NODELAY, true);
			b.option(ChannelOption.SO_BACKLOG, 1024);
			b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
            f = b.bind(address).syncUninterruptibly();
            
            channel = f.channel();
            
            f.channel().closeFuture().sync();
            
		} catch (Exception e) {
			log.error("netty server 发生错误 :{}",e.getMessage());
		}finally {
			if(f!=null && f.isSuccess()) {
				log.info("Netty server 监听 {} 端口号 :{}",address.getHostName(),address.getPort());
			}
		}
		return f;
		
	}
	public void destroy() {
		log.info("关闭netty");
        if(channel != null) { channel.close();}
        childGroup.shutdownGracefully();
        parentGroup.shutdownGracefully();
        log.info("成功关闭netty");
    }
	
}

三、创建主启动类,并在启动类中添加netty的服务监听的启动。

@SpringBootApplication
public class Application implements CommandLineRunner{

    private static final Logger logger = LoggerFactory.getLogger(Application.class);
	@Autowired
	private NettyServer nettyServer;
	@Value("${server.ip}")
	private String ip;
	@Value("${server.ip.port}")
	private int port;
    public static void main(String[] args) {
         SpringApplication.run(Application.class, args);
    }


	@Override
	public void run(String... args) throws Exception {
		logger.info("服务器ip:{} ,端口号 port:{}",ip,port);
		
		InetSocketAddress address = new InetSocketAddress(ip, port);
		ChannelFuture f =  nettyServer.start(address);
		Runtime.getRuntime().addShutdownHook(new Thread() {
			@Override
			public void run() {
				try {
					nettyServer.destroy();
				} catch (Exception e) {
					logger.error(e.getMessage());
				}
			}
		});
		//服务端管道关闭的监听器并同步阻塞,直到channel关闭,线程才会往下执行,结束进程
		if(f!=null)
			f.channel().closeFuture().syncUninterruptibly();
		
	}

四、通道初始化

@Component
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel>{

	@Autowired
	private ApplicationContext appContext;
	
	@Override
	protected void initChannel(SocketChannel socketChannel) throws Exception {
		socketChannel.pipeline().addLast(LengthFieldBasedFrameDecoder());
		socketChannel.pipeline().addLast(LengthFieldBasedFrameDecoder3());
		socketChannel.pipeline().addLast(LengthFieldBasedFrameDecoder2());		
		socketChannel.pipeline().addLast(MessageToMessageEncoder());
		socketChannel.pipeline().addLast(MessageToMessageEncoder3());
		socketChannel.pipeline().addLast(MessageToMessageEncoder2());		
		socketChannel.pipeline().addLast(new IdleStateHandler(35, 35, 35));
		socketChannel.pipeline().addLast(serverHandler());
		
	}
	
	private ServiceHandler serverHandler() {
		ServiceHandler serverHandler = appContext.getBean(ServiceHandler.class);		
		return serverHandler;
	}
	
	private LengthFieldBasedFrameDecoder LengthFieldBasedFrameDecoder() {
		RechargeLengthFieldBasedFrameDecoder r =appContext.getBean(LengthFieldBasedFrameDecoder.class);
		return r;
	}
	
	private LengthFieldBasedFrameDecoder2 LengthFieldBasedFrameDecoder2() {
		LengthFieldBasedFrameDecoder2 r =appContext.getBean(LengthFieldBasedFrameDecoder2.class);
		return r;
	}

	private LengthFieldBasedFrameDecoder3 LengthFieldBasedFrameDecoder3() {
		LengthFieldBasedFrameDecoder3 r =appContext.getBean(LengthFieldBasedFrameDecoder3.class);
		return r;
	}
	
	private MessageToMessageEncoder MessageToMessageEncoder() {
		MessageToMessageEncoder r =appContext.getBean(MessageToMessageEncoder.class);
		return r;
	}
	
	private MessageToMessageEncoder2 MessageToMessageEncoder2() {
		MessageToMessageEncoder2 r =appContext.getBean(MessageToMessageEncoder2.class);
		return r;
	}
	
	private MessageToMessageEncoder3 MessageToMessageEncoder3() {
		MessageToMessageEncoder3 r =appContext.getBean(MessageToMessageEncoder3.class);
		return r;
	}
}

五、自定义解码器

@Scope("prototype")
@Component
public class LengthFieldBasedFrameDecoder3 extends LengthFieldBasedFrameDecoder {
	private static final Logger log = LoggerFactory
			.getLogger(com.it.foundwater.util.LengthFieldBasedFrameDecoder3.class);

	public DALengthFieldBasedFrameDecoder3() {
		super(262144, 2, 1, -2, 0);
	}

	protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
		ByteBuf inByteData = (ByteBuf) super.decode(ctx, in);

		if (inByteData == null || inByteData.readableBytes() == 0) {
			log.info("没有信息,跳过");
			ByteBuf frame = in.retainedDuplicate();
			in.skipBytes(in.readableBytes());
			return frame;
		}
		try {
			String data = PackageDataUtils.decodeByteBuff(inByteData);
			log.info("收到数据:{}", data);
			data = PackageDataUtils.DACheckDataFormat(data);
			if (StringUtils.isEmpty(data)) {
				log.info("验证失败!");
				return in;
			}
			Object obj = filterDA(data);
			if (obj != null) {
				return obj;
			}
			return in;
		} catch (Exception e) {
			log.error("解析协议失败", e);
			return in;
		}
	}

	private Object filterDA(String data) {
		DADataBean bean = new DADataBean();
		PackageDataUtils.DataBean(bean, data);
		return bean;
	}

	protected long getUnadjustedFrameLength(ByteBuf buf, int offset, int length, ByteOrder order) {
		log.info("自定义长度解码器offset:" + offset + " length:" + length + " dataSize:" + buf.readableBytes());
		long frameLength = 0L;
		try {
			buf = buf.order(order);
			String lenStr = "";
			String temp = null;
			int i, len;
			for (i = 0, len = length; i < len; i++) {
				temp = Integer.toHexString(buf.getUnsignedByte(i) & 0xFF);
				if (temp.length() < 2) {
					temp = "0" + temp;
				}
				lenStr = String.valueOf(lenStr) + temp;
			}
			log.info("帧头数据:" + lenStr);
			lenStr = "";
			temp = null;
			for (i = 0, len = length; i < len; i++) {
				temp = Integer.toHexString(buf.getUnsignedByte(offset + i) & 0xFF);
				if (temp.length() < 2) {
					temp = "0" + temp;
				}
				lenStr = String.valueOf(lenStr) + temp;
			}
			lenStr = PackageDataUtils.convert(lenStr);
			frameLength = Long.parseLong(PackageDataUtils.hexToLongString(lenStr));
			log.info("帧长度:" + frameLength + " lenStr:" + lenStr);
		} catch (Exception e) {
			throw new DecoderException("unsupported lengthFieldLength: " + length + " (expected: 1, 2, 3, 4, or 8)");
		}
		offset = 0;
		return frameLength;
	}
}

六、数据处理工具类

@Slf4j
public class PackageDataUtils {

    //解析报文(将字报文转换为字符串报文)
    public static String decodeByteBuff(ByteBuf in) {
		log.debug("解析开始");
		StringBuilder stringBuilder = new StringBuilder();
		StringBuilder str = new StringBuilder();
		try {
			byte[] b = new byte[in.readableBytes()];
			in.readBytes(b);
			for (int i = 0; i < b.length; i++) {
				str.append(Byte.toString(b[i]));
				int v = b[i] & 0xFF;
				String hv = Integer.toHexString(v);
				if (hv.length() < 2) {
					stringBuilder.append(0);
				}
				stringBuilder.append(hv);
			}
		}catch (Exception e) {
			log.error("数据解码异常:",e);
		}finally {
			//ReferenceCountUtil.release(in);
		}
		String data = stringBuilder.toString();
		log.info("原始数据:" + str.toString());
		return data;
	}

    //校验报文
    public static String CheckDataFormat(String data) {
		     String head = data.substring(0, 4);
		     if (!ConstantUtils.HEAD.equals(head)) {
		       log.info("帧起始符校验失败!");
		       return null;
		     } 
		     int lenth = data.length();
		     String Totalstr = data.substring(8, 10);
		     String checkStr = intToHexString(lenth / 2, 2);
		     
		     log.info("{}计算后的报文长度:{}", Totalstr, Integer.valueOf(lenth));
		     if (!Totalstr.equals(checkStr)) {
		       log.info("报文长度校验失败!");
		       return null;
		     } 
		     
		     String responseCode = dataFormatting(data.substring(lenth - 8, lenth - 4));
		     String dataCode = data.substring(2, lenth - 8);
		     
		     String crcCode = CRC16ToString(dataCode);
		     crcCode = StringUtil.formatString(crcCode, 4);
		     if (!responseCode.equalsIgnoreCase(crcCode)) {
		       log.info("心跳包的校验码:{}", responseCode);
		       log.info("CRC生成的校验码:{}", crcCode);
		       log.info("校验码校验失败!");
		       return null;
		     } 
		     return data;
		   }

        //将报文解析bean
        public static void DataBean(DADataBean dataBean, String data) {
		     int length = data.length();
		     dataBean.setStartFrame(data.substring(0, 4));
		     dataBean.setResponseTotal(hexValToInt(data.substring(8, 10)).toString());
		     dataBean.setResponseVersion(data.substring(10, 12));
		     dataBean.setDeviceNoLength("6");
		             dataBean.setDeviceNo(data.substring(20,22)+data.substring(18,20)+data.substring(4,6));
		     dataBean.setData(data.substring(22, length - 8));
		     dataBean.setCheckCode(data.substring(length - 8, length - 4));
		     dataBean.setEndFlag(data.substring(length - 4, length));
		   }

}

七、定义适配器处理

@Slf4j
@Scope("prototype")
@Component
public class ServiceHandler extends ChannelInboundHandlerAdapter{
	@Autowired
    private AbnormalAnalysisService abnormalAnalysisService;

	@Autowired
	private DeviceMapper deviceMapper;
	@Autowired
    private DeviceReadDataHandleSecondService deviceReadDataHandleSecondService;
	
	   @Autowired
	   private ProcessDeviceService processDeviceService;
	/**
	 * 接收到数据完成后回调,注意粘包和拆包问题
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		if(msg instanceof DataBean ) {
			DataBean dataBean =  (DataBean)msg;
			processDeviceService.processCYdevice(dataBean);
			if(ctx.channel()!=null) {
				//PackageDataUtils.sendMessageClient(bean, ctx.channel());
				dataBean.sendSocketToClient(ctx.channel());
			}
		} else if (msg instanceof DataBean1) {
			DataBean1 dataBean1= (DataBean1)msg;
		    processDeviceService.processSYdevice(DataBean1);
		       if (ctx.channel() != null)
		       {
			         dataBean1.sendSocketToClient(ctx.channel());
		       }
		}else {
			log.info("接收不到数据或格式错误");
		}
	}



	/**
	 * 在读取操作期间,有异常抛出时会调用。
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
	}

	/**
	 * 通道激活时触发,当客户端连接成功后调用
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		String clientIP = "";
		int port =0000;
		try {
			InetSocketAddress ipScoketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
			clientIP = ipScoketAddress.getAddress().getHostAddress();
			port = ipScoketAddress.getPort();
		} catch (Exception e) {
			log.error(e.getMessage());
		}
		log.info("客户端连接的ip地址:{} ,端口号 :{}",clientIP,port);
	
	}
	
	
	/**
	 * 超时时间发生回调
	 */
	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		super.userEventTriggered(ctx, evt);
		if (evt instanceof IdleStateEvent) {  // 2
            IdleStateEvent event = (IdleStateEvent) evt;  
            String type = "";
            if (event.state() == IdleState.READER_IDLE) {
                type = "read idle timeout";
            } else if (event.state() == IdleState.WRITER_IDLE) {
                type = "write idle timeout";
            } else if (event.state() == IdleState.ALL_IDLE) {
                type = "all idle timeout";
                if(ctx!=null) {
                	ctx.close();
                }
            }
            log.debug( ctx.channel().id().asLongText()+"超时类型:" + type);
        } else {
            super.userEventTriggered(ctx, evt);
        }
	}
	
}

八、消息编码器(应答客户端)

@Slf4j
public class MessageToMessageEncoder extends MessageToMessageEncoder<DataBean> {

	protected void encode(ChannelHandlerContext ctx, DataBean msg, List<Object> out) throws Exception {
		try {
			String dataStr = PackageDataUtils.syBuilderSocketDataStr(msg);
			log.info("应答客户端:{}", dataStr);
			ByteBuf bb = ctx.channel().alloc().buffer(dataStr.length() / 2);
			for (int i = 0, len = dataStr.length(); i < len; i += 2) {
				bb.writeByte((byte) Integer.parseInt(dataStr.substring(i, i + 2), 16));
			}
			out.add(bb);
		} catch (Exception e) {

			e.printStackTrace();
		}
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值