记一次Netty的开发过程

本文介绍如何使用Netty框架构建银行保险类项目的接口,包括服务端搭建、编码器和解码器的实现,以及通道处理器的配置,旨在解决银行与保险系统间的数据通信问题。

银行对接的保险类项目,需要使用socket协议来做接口的对接,于是就想到了netty

  • 构建netty服务端
/**
 * netty服务端
 * 1.创建一个serverBootstrap的实例引导和绑定服务器
 * 2.创建并分配一个NioEventLoopGroup实例以进行事件的处理,比如接受连接以及读写数据
 * 3.指定服务器绑定的本地的InetSocketAddress
 * 4.使用一个EchoServerHandler的实例初始化每一个新的Channel
 * 5.调用ServerBootstrap.bind()方法以绑定服务器
 *
 * @auther quanming
 * @Date 2019/9/26 9:16
 */
@Slf4j
@Component
public class EchoServer {

    /**
     * NioEventLoop并不是一个纯粹的I/O线程,它除了负责I/O的读写之外
     * 创建了两个NioEventLoopGroup,
     * 它们实际是两个独立的Reactor线程池。
     * 一个用于接收客户端的TCP连接,
     * 另一个用于处理I/O相关的读写操作,或者执行系统Task、定时任务Task等。
     */
    private final EventLoopGroup bossGroup = new NioEventLoopGroup();

    private final EventLoopGroup workerGroup = new NioEventLoopGroup();

    private Channel channel;

    @Autowired
    private ServerChannelInitializer serverChannelInitializer;

    public ChannelFuture start(String host,int port)throws Exception{
        ChannelFuture channelFuture = null;
        try {
            // 服务端启动引导工具类
            ServerBootstrap b = new ServerBootstrap();
            // 组装NioEventLoopGroup
            b.group(bossGroup,workerGroup)
              // 设置channel类型为NIO类型
             .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(host,port))
                    // 配置入站、出站事件handler
                    .childHandler(serverChannelInitializer)
                    // 设置连接配置参数
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            // 绑定端口 接收进来的链接
            channelFuture = b.bind().sync();
            channel = channelFuture.channel();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            if (channelFuture != null && channelFuture.isSuccess()) {
                log.info("Netty server listening " + host + " on port " + port + " and ready for connections...");
            } else {
                log.error("Netty server start up Error!");
            }
        }
        return channelFuture;
    }

}
  • 构建编码器,解码器
  • 编码器
/**
 * 消息编码器 返回的消息进行编码
 *
 * @auther quanming
 * @Date 2019/9/29 17:09
 */
public class MyMessageEncoder extends MessageToByteEncoder<NettyMessageVO> {

    /** 报文标识 **/
    private static final int MSG_SIGN = 4;
    /** 交易代码 **/
    private static final int TRANS_CODE = 4;
    /** 交易流水号 **/
    private static final int TRANS_NO = 21;
    /** 报文体 **/
    private static final int XML_CONTEXT = 8;

    @Override
    protected void encode(ChannelHandlerContext ctx, NettyMessageVO msg, ByteBuf out) throws Exception {
        int packageLen = MSG_SIGN + TRANS_CODE + TRANS_NO + XML_CONTEXT + msg.getXmlContext().getBytes().length;
        ByteBuffer bf = ByteBuffer.allocate(packageLen);
        // 报文标示
        bf.put(msg.getMsgSign().getBytes(CharsetUtil.UTF_8));
        // 交易代码
        BeanPropertiesConfig beanPropertiesConfig = SpringBeanUtils.getBean("beanPropertiesConfig");
        bf.put(beanPropertiesConfig.getTransCodeMap().get(msg.getTransCode()).getBytes(CharsetUtil.UTF_8));
        // 交易流水号
        bf.put(msg.getTransNo().getBytes(CharsetUtil.UTF_8));
        // 报文体长度 8个字节 不够则采取左补0机制
        bf.put(String.format("%08d",msg.getXmlContext().getBytes().length).getBytes(CharsetUtil.UTF_8));
        // 报文体
        bf.put(msg.getXmlContext().getBytes(CharsetUtil.UTF_8));
        bf.flip();
        byte[] bytes = new byte[bf.limit()];
        bf.get(bytes);
        out.writeBytes(bytes);
    }
}
  • 解码器-1
/**
 *
 * 消息解码器, 请求的消息进行解码
 *
 * maxFrameLength:最大帧长度。也就是可以接收的数据的最大长度。如果超过,此次数据会被丢弃。
 * lengthFieldOffset:长度域偏移。就是说数据开始的几个字节可能不是表示数据长度,需要后移几个字节才是长度域。
 * lengthFieldLength:长度域字节数。用几个字节来表示数据长度。
 * lengthAdjustment:数据长度修正。因为长度域指定的长度可以是header+body的整个长度,也可以只是body的长度。如果表示header+body的整个长度,那么我们需要修正数据长度。
 * initialBytesToStrip:跳过的字节数。如果你需要接收header+body的所有数据,此值就是0,如果你只想接收body数据,那么需要跳过header所占用的字节数。
 *
 * 发送数据包长度 = 长度域的值 + lengthFieldOffset + lengthFieldLength + lengthAdjustment
 *
 * @auther quanming
 * @Date 2019/9/26 12:25
 */
@Slf4j
public class MyMessageDecoder extends LengthFieldBasedFrameDecoder {

    public MyMessageDecoder() {
        super(ByteOrder.BIG_ENDIAN, 1024*1024*10, 29, 8, 0, 0, true);
    }

    @Override
    protected NettyMessageVO decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        ByteBuf frame = (ByteBuf) super.decode(ctx, in);
        if (frame == null){
            log.info("客户端请求数据为空");
            return null;
        }

        // 报文标示
        byte[] bytes = new byte[4];
        frame.readBytes(bytes);
        String msgSign = new String(bytes);

        log.info("报文标示msgSign:{} ",msgSign);
        // 交易代码
        bytes = new byte[4];
        frame.readBytes(bytes);
        String transCode = new String(bytes);

        log.info("交易代码transCode:{} ",transCode);

        // 交易流水号
        bytes = new byte[21];
        frame.readBytes(bytes);
        String transNo = new String(bytes);
        log.info("交易流水号transNo:{} ",transNo);

        // 报文体长度
        bytes = new byte[8];
        frame.readBytes(bytes);
        String xmlLength = new String(bytes);
        log.info("报文体长度:{}",xmlLength);
        // 报文体
        byte[] data = new byte[frame.readableBytes()];
        frame.readBytes(data);
        String requestXml = new String(data);

        log.info("报文体xml:{}",requestXml);
        NettyMessageVO nettyMessageVO = new NettyMessageVO();
        nettyMessageVO.setMsgSign(msgSign);
        nettyMessageVO.setTransCode(transCode);
        nettyMessageVO.setTransNo(transNo);
        nettyMessageVO.setXmlLength(Integer.parseInt(xmlLength));
        nettyMessageVO.setXmlContext(requestXml);

        return nettyMessageVO;
    }

    @Override
    protected ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, int index, int length) {
        return super.extractFrame(ctx, buffer, index, length);
    }
  • 解码器-2
/**
 * @auther quanming
 * @Date 2019/10/16 12:06
 */
@Slf4j
public class StringDecoder extends ReplayingDecoder<MyDecoderState> {
    private  int length = 0;
    private NettyMessageVO nettyMessageVO = new NettyMessageVO();
    public StringDecoder() {
        // Set the initial state.
        super(MyDecoderState.READ_LENGTH);
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        MyDecoderState readSign = state();
        log.info("readSign = {}",readSign);
        switch (readSign) {
            case READ_LENGTH:
                nettyMessageVO = new NettyMessageVO();
                byte[] bytes = new byte[4];
                byteBuf.readBytes(bytes);
                String msgSign = new String(bytes,CharsetUtil.UTF_8);

                log.info("报文标示msgSign:{}",msgSign);
                // 交易代码
                bytes = new byte[4];
                byteBuf.readBytes(bytes);
                String transCode = new String(bytes,CharsetUtil.UTF_8);

                log.info("交易代码transCode:{}",transCode);

                // 交易流水号
                bytes = new byte[21];
                byteBuf.readBytes(bytes);
                String transNo = new String(bytes,CharsetUtil.UTF_8);
                log.info("交易流水号transNo:{} ",transNo);

                // 报文体长度
                bytes = new byte[8];
                byteBuf.readBytes(bytes);
                String xmlLength = new String(bytes,CharsetUtil.UTF_8);
                log.info("报文体长度:{}",xmlLength);
                length = Integer.valueOf(xmlLength);
                checkpoint(MyDecoderState.READ_CONTENT);
                nettyMessageVO.setMsgSign(msgSign);
                nettyMessageVO.setTransCode(transCode);
                nettyMessageVO.setTransNo(transNo);
                nettyMessageVO.setXmlLength(Integer.parseInt(xmlLength));
                break;
            case READ_CONTENT:
                bytes = new byte[length];
                byteBuf.readBytes(bytes);
                String requestXml = new String(bytes,CharsetUtil.UTF_8);
                log.info("报文体:{}",requestXml);
                checkpoint(MyDecoderState.READ_LENGTH);
                nettyMessageVO.setXmlContext(requestXml);
                list.add(nettyMessageVO);
                break;
            default:
                throw new Error("Shouldn't reach here.");
        }
    }
}
  • 注入通道处理器
/**
 * 通道处理器
 *
 * @auther quanming
 * @Date 2019/9/26 16:24
 */
@Component
@Slf4j
@ChannelHandler.Sharable
public class ServerHandler extends ChannelInboundHandlerAdapter {

    @Autowired
    private StrategyFactory strategyFactory;

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        NettyMessageVO nettyMessageVO = (NettyMessageVO) msg;
        NettyStrategy strategy = strategyFactory.createStrategy(nettyMessageVO.getTransCode());
        String responseXml = strategy.transToCgb(nettyMessageVO.getXmlContext());
        nettyMessageVO.setXmlContext(responseXml);
        ctx.writeAndFlush(nettyMessageVO);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        log.error("netty读取信息异常:{}",cause);
    }
}
  • 设置出站和入站的编码器和解码器
  • /**
     * 设置出站和入站的编码器和解码器
     *
     * @auther quanming
     * @Date 2019/9/26 9:43
     */
    @Component
    public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
    
        @Autowired
        private ServerHandler serverHandler;
    
        @Override
        protected void initChannel(SocketChannel channel) throws Exception {
            channel.pipeline().addLast(new MyMessageEncoder());
            channel.pipeline().addLast(new StringDecoder());
    
            channel.pipeline().addLast(serverHandler);
        }
    }
    

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值