SSM(二):Netty接收数据并存入数据库出现数据接收不完全的情况

版权声明:转载请注明出处 https://blog.csdn.net/u014402533/article/details/84675768

1.NettyServerStart

public class NettyServerStart {

    @Autowired
    public NettyServerStart(final NettyServer nettyServer) {
        System.out.println("------------Spring自动加载 ---------");
        System.out.println("------------启动Netty服务 ---------");
        //绑定端口
        nettyServer.setServerPort(6667);
        ExecutorService executorService= Executors.newCachedThreadPool();
        //执行nettyServer
        executorService.execute(nettyServer);
    }
}

2.NettyServerInitialize

public class NettyServerInitialize extends ChannelInitializer<SocketChannel> {

    @Autowired
    private NettyServerHandler nettyServerHandler;

    @Override
    public void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        //pipeline.addLast("decoder", new LengthFieldBasedFrameDecoder(8192,12,2,0,0));
       // pipeline.addLast("decoder", new CCYLengthFieldDecoder(8192,12,2,0,0));
       // pipeline.addLast("decoder",new CNPCDecoder());
        pipeline.addLast("handler", nettyServerHandler);
        System.out.println("SimpleChatClient:"+ch.remoteAddress() +"连接上");
    }
}

3.NettyServerHandler

重点要说一下这个channelRead0方法里面我遇到的问题。由于某些原因还未来得及系统学习Netty,但是又急需把数据收集起来存入数据库。情况是这样的:有一个1w多个字节的数据需要我来解析出真实数据,但是每次用网络测试助手往端口发送数据,收到的数据都会出现不完全的情况。刚开始,我也不知道啥状况,于是对于那些不完整的数据采取的是不处理。后来才知道,TCP是不会出现丢包的,当数据长度过长的时候,路由器转发的时候会对数据进行拆分,分多少次发,channelRead0这个方法就会执行多少次,也就是说当我们觉得收到的数据不完全的时候,不是数据丢失,只是channelRead0方法还在执行中,数据没有收发完。解决办法:自己建立一个动态数组,用于每次执行channelRead0方法存储数据,然后校验首位末尾,校验通过之后,解析完毕,立刻存入数据库,然后清空动态数组。下图可以看到,随着channelRead0不断执行,动态数组越来越长。

public class NettyServerHandler extends SimpleChannelInboundHandler<Object> {

    @Autowired
    private CollectedDataService collectedDataService;


    private Map<String, DynamicArray<Byte>> dynamicArrayMap = new HashMap<>();
    private int defaultDynamicCapacity = 1024 * 1024;



    /**
     * A thread-safe Set  Using ChannelGroup, you can categorize Channels into a meaningful group.
     * A closed Channel is automatically removed from the collection,
     */
    public static ChannelGroup channels =
            new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);


    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {  // (2)
        Channel incoming = ctx.channel();
        // Broadcast a message to multiple Channels
        channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n");
        channels.add(ctx.channel());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {  // (3)
        Channel incoming = ctx.channel();
        channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n");
    }


    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { // (4)


        ByteBuf in = (ByteBuf) msg;
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String ip = insocket.getAddress().getHostAddress();
        if(!dynamicArrayMap.containsKey(ip)){
            dynamicArrayMap.put(ip,new DynamicArray<Byte>(defaultDynamicCapacity));
        }
        DynamicArray<Byte> dynamicArray =  dynamicArrayMap.get(ip);


        while (in.isReadable()) {
            byte recByte =  in.readByte();
            dynamicArray.addLast(recByte);
        }
        System.out.println("------------------------------------");
        System.out.println("dynamicArray长度:"+dynamicArray.getSize());


        byte [] dataByte = new byte[dynamicArray.getSize()];

//从Byte[]获得char[]
        for (int i = 0;i<dynamicArray.getSize();i++) {
            byte b = dynamicArray.get(i);
            dataByte[i] = b;
        }

        char[]  byteToChars = getChars(dataByte);

        Integer validSuccessIndex = null;


        //todo 校验成功
        if(byteToChars[0]=='0' && byteToChars[1] == '1'
                && byteToChars[byteToChars.length-2]== '0'
                && byteToChars[byteToChars.length-1] == '3'
        ) {
            validSuccessIndex = byteToChars.length;
            System.out.println("成功位:"+ validSuccessIndex);
            System.out.println("成功位:"+ validSuccessIndex);
            collectedDataService.addCnpcTankData(String.valueOf(byteToChars));

        }

        if(byteToChars[0]=='1' && byteToChars[1] == 'B'
                && byteToChars[byteToChars.length-2]== '0'
                && byteToChars[byteToChars.length-1] == 'D'
        ) {
            validSuccessIndex = byteToChars.length;
            System.out.println("成功位:"+ validSuccessIndex);
            System.out.println("成功位:"+ validSuccessIndex);
            collectedDataService.addCnpcGunData(String.valueOf(byteToChars));
            System.out.println("清空dynamicArray之前:"+dynamicArray.getSize());
        }

        if(validSuccessIndex != null){
            // Byte[] validBuffer = dynamicArray.leftShift(validSuccessIndex);


            dynamicArray.leftShift(validSuccessIndex);

            System.out.println("清空dynamicArray之后:"+dynamicArray.getSize());

            //清空byteToChar


        }




        System.out.println("1st:"+byteToChars[0]+" 2nd:"+byteToChars[1]+
                " 3rd:"+byteToChars[byteToChars.length-2]+" 4th:"+byteToChars[byteToChars.length-1]);


        for (char byteTochar : byteToChars
                ) {
            System.out.print(byteTochar);
        }

      //  System.out.println("--------------------");

  //      System.out.println("dataByte.lenth:"+dataByte.length);

        //ValidSuccessIndex不为空则校验成功

    }

    byte[] toPrimitives(Byte[] oBytes)
    {
        byte[] bytes = new byte[oBytes.length];

        for(int i = 0; i < oBytes.length; i++) {
            bytes[i] = oBytes[i];
        }

        return bytes;
    }
    char[] getChars(byte[] bytes) {
        Charset cs = Charset.forName("UTF-8");
        ByteBuffer bb = ByteBuffer.allocate(bytes.length);
        bb.put(bytes);
        bb.flip();
        CharBuffer cb = cs.decode(bb);
        return cb.array();
    }







    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5)
        Channel incoming = ctx.channel();
        System.out.println("Client:" + incoming.remoteAddress() + "上线");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)
        Channel incoming = ctx.channel();
        System.out.println("Client:" + incoming.remoteAddress() + "掉线");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        Channel incoming = ctx.channel();
        System.out.println("Client:" + incoming.remoteAddress() + "异常");
        // 当出现异常就关闭连接
        //cause.printStackTrace();
        ctx.close();
    }
}

4.NettyServer

package com.cnpc.netty;

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;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Created by caihanbin on 2017/4/29.
 */

@Component
public class NettyServer implements Runnable {

    @Autowired
    private NettyServerInitialize nettyServerInitialize;

    private int port;
    //设置端口
    public void setServerPort(int port) {
        this.port = port;
    }

    public NettyServer(){}

    public void run()  {
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap(); // (2)
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class) // (3)
                    .childHandler(nettyServerInitialize)
                    .option(ChannelOption.SO_BACKLOG, 128)          // (5)
                    .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
            // 绑定端口,开始接收进来的连接
            ChannelFuture f = b.bind(port).sync(); // (7)
            // 等待服务器  socket 关闭 。
            // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/u014402533/article/details/84675768