netty分包的问题

使用netty做硬件设备通讯的服务端,有时候会出现一条数据被分成几个片段

解决办法:继承解码器ByteToMessageDecoder,覆盖decode方法,在decode方法中进行数据的验证和解析,验证通过后,decode方法参数List<Object> out,使用这个参数把解析的数据放进去,如何验证不通过,不做任何处理(也就是不放进out参数,netty会把下次接收的数据和这次的数据拼接,放进参数out中就会传递到Handler的ChannelInboundHandlerAdapter类的channelRead方法中做下一步的处理);decode方法中验证数据有2中方法可以参考:

(1)ByteBuf  in.bytesBefore(byte value),这个可以验证指定位置字节是否匹配和查找数据中指定字节的位置,进行验证,如果还有需要计算字节和的验证可以读取其中需要验证的字节

(2)另一种方式是把ByteBuf  in中的数据读取到指定的字节数组中进行验证

int length=in.readableBytes();
byte[] barray = new byte[length];
//把数据从bytebuf转移到byte[]
in.getBytes(0,barray); //此方法不修改此缓冲区的readerIndex或writerIndex
String hexStr=Convert.bytesToHexString(barray);

下面是我的netty代码

TcpServer

package com.test_netty;

import java.util.concurrent.TimeUnit;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
 
/**
 * Discards any incoming data.
 */
public class TcpServer implements Runnable {
 
    private int port;
    public TcpServer(int port) {
        this.port = port;
    }
//   public static ChannelGroup allChannels =
//	         new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    public void run()  {
    	 
    	  // 1 创建线两个事件循环组
        // 一个是用于处理服务器端接收客户端连接的
        // 一个是进行网络通信的(网络读写的)
        EventLoopGroup pGroup = new NioEventLoopGroup(1,new DefaultThreadFactory("my_tcp_server1", true));
        EventLoopGroup cGroup = new NioEventLoopGroup(2,new DefaultThreadFactory("my_tcp_server2", true));
        try {
        	 // 2 创建辅助工具类ServerBootstrap,用于服务器通道的一系列配置
            ServerBootstrap b = new ServerBootstrap();
            b.group(pGroup, cGroup) // 绑定俩个线程组
                    .channel(NioServerSocketChannel.class) // 指定NIO的模式.NioServerSocketChannel对应TCP, NioDatagramChannel对应UDP
                    .option(ChannelOption.SO_BACKLOG, 200) // 标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度
//                    .childOption(ChannelOption.SO_SNDBUF, 3 * 1024) // 设置发送缓冲大小,发送不设置会自动扩容
//                    .childOption(ChannelOption.SO_RCVBUF, 1024*10) // 这是接收缓冲大小,发送不设置会自动扩容
//                     .option(ChannelOption.SO_RCVBUF, 1024*10)
                    .childOption(ChannelOption.SO_KEEPALIVE, true) // 保持连接
//                    .childOption(ChannelOption.TCP_NODELAY, true) // 保持连接
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel sc) throws Exception {  //SocketChannel建立连接后的管道
                            // 3 在这里配置 通信数据的处理逻辑, 可以addLast多个...
                          ChannelPipeline pipeline = sc.pipeline();
                          pipeline.addLast(new MyDecoder());
                       	  pipeline.addLast("handler",new IdleStateHandler(5, 5, 5, TimeUnit.MINUTES));
                       	  pipeline.addLast(new TcpServerHandler());
                       	 
                        }
                    });

            // 4 绑定端口, bind返回future(异步), 加上sync阻塞在获取连接处
            System.out.println("准备绑定端口号:"+port);
            ChannelFuture cf1 = b.bind(port).sync();
//            ChannelFuture cf2 = b.bind(2345).sync();   //可以绑定多个端口
            // 5 等待关闭, 加上sync阻塞在关闭请求处
            cf1.channel().closeFuture().sync();
            
            System.out.println("关闭端口1");
//            cf2.channel().closeFuture().sync();
//            System.out.println("关闭端口2");
        }catch (Exception e) {
			e.printStackTrace();
		}finally {
			pGroup.shutdownGracefully();
	        cGroup.shutdownGracefully();
		}
       
        

	}
 
	/* public static void main(String[] args) throws Exception {
	//    	new Thread(new TestThread()).start();
	    int port;
	    if (args.length > 0) {
	        port = Integer.parseInt(args[0]);
	    } else {
	        port = 6400;
	    }
	    new TcpServer("192.168.0.163",port).run();
	    
	   
	    
	}*/
}

TcpServerHandler

package com.test_netty;

import java.io.UnsupportedEncodingException;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;

public class TcpServerHandler extends ChannelInboundHandlerAdapter {
	
	 
	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		System.out.println("channelReadComplete");
			super.channelReadComplete(ctx);
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		super.channelInactive(ctx);
		System.out.println("channelInactive关闭");
	}
	boolean ok=false;
	/*
	 * 该方法应该使用线程池处理
	 * 自身的接收线程数是固定的
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msgObject) throws Exception {
		System.out.println("channelRead-TcpServerHandler-Thread-name"+Thread.currentThread().getName());
		/*ByteBuf msg=(ByteBuf)msgObject;
		byte[] con = new byte[msg.readableBytes()];
		msg.readBytes(con);*/
		/*byte[] con =(byte[]) msgObject;
		
		try {
			String rjson= new String(con, "utf-8");
			System.out.println(rjson);
		} catch (UnsupportedEncodingException e) {
		    e.printStackTrace();
		}finally {
		//			ReferenceCountUtil.release(msg);//释放掉msg
		}*/
  
		String hexString=(String) msgObject;
		System.out.println("hexString:"+hexString);
	}
	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		if (evt instanceof IdleStateEvent) {  // 2
			IdleStateEvent event = (IdleStateEvent) evt;  
			 
			if (event.state() == IdleState.READER_IDLE) {
				//读空闲时间
				System.out.println("读空闲时间");
				ctx.close();
			} else if (event.state() == IdleState.WRITER_IDLE) {
				ctx.close();
				System.out.println("写空闲时间");
			} else if (event.state() == IdleState.ALL_IDLE) {
				System.out.println("读写空闲时间");
			}
		} else {
			super.userEventTriggered(ctx, evt);
		}
	}
	@Override
    public void channelActive(final ChannelHandlerContext ctx) { // (1)
    }   
	@Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        System.out.println("出现异常!");
        ctx.close();
    }
}

MyDecoder

package com.test_netty;

import java.util.List;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.ReferenceCountUtil;

public class MyDecoder extends ByteToMessageDecoder {

	/**
	 *数据不完整不处理,数据正常解析了List<Object> out.add(),添加数据自动传到ChannelInboundHandlerAdapter.channelRead()
	 */
	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
		 
		/*int p=in.bytesBefore((byte)0x68);
		System.out.println(p);*/
		//创建目标大小的数组
		int length=in.readableBytes();
		System.out.println("length:"+length);
		byte[] barray = new byte[length];
		//把数据从bytebuf转移到byte[]
		in.getBytes(0,barray); 
		String hexStr=Convert.bytesToHexString(barray);
		while (hexStr!=null&&hexStr.contains("68")&&hexStr.contains("16")&&hexStr.length()>=12*2){
			hexStr=hexStr.substring(hexStr.indexOf("68"));//第一个68位置
			String f2_68=hexStr.substring(14, 16);
			if(f2_68.contentEquals("68")){
				String r=data(hexStr);
				if(r==null){//不能解析的
					hexStr=hexStr.substring(hexStr.indexOf("68",2));
					continue;
				}else{//能解析,已经解析过退出
					out.add(hexStr);//添加报文,传递到channelRead
					in.skipBytes(in.readableBytes());//必须加上这个 ,不然netty会报错误
					return;
				}
			}else {
				hexStr=hexStr.substring(hexStr.indexOf("68",2));
				continue;
			}
		}
		
	}
	private static String data(String returndata){
		 
		if(returndata.contains("68")&&returndata.contains("16")&&returndata.length()>=12*2){
			returndata=returndata.substring(returndata.indexOf("68"));
			String address=returndata.substring(1*2, 7*2);
			String other68= returndata.substring(7*2,8*2);
			String control= returndata.substring(8*2,9*2);//控制码
			String dataLength= null;
			try {
				dataLength= returndata.substring(9*2,10*2);//数据长度
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
			
			String dataFlagR=null;
			try {
				dataFlagR= returndata.substring(10*2,12*2);//接收到的数据标示
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
			
			if(!(other68.equals("68"))){
				return null;
			}
			
			int length=0;
			try {
				length=Integer.parseInt(dataLength,16);	
			} catch (Exception e) {
				return null;
			}
			if(control.equals("81")){
				if(length<2){
					return "w";
				}
				length-=2;
			}
			if(control.equals("01")){
				return "w";
			}
			
			
			int totalLength=14+length;//数据的总字节数
			if(control.equals("84")||control.equalsIgnoreCase("c4")||control.equals("9c")){
				totalLength=12;
			}
			String end16=null;
			String cs= null;
			String csBeforeStr=null;
			
			
			try {
				 end16= returndata.substring((totalLength-1)*2,totalLength*2);//判断结尾16
				 cs= returndata.substring((totalLength-2)*2,(totalLength-1)*2);//cs校验和
				 csBeforeStr= returndata.substring(0,(totalLength-2)*2);//cs校验和
			} catch (Exception e) {
//				e.printStackTrace();
				return null;
			}
			if(!end16.equals("16")){
				return null;
			}
			if(!getCS(csBeforeStr).equalsIgnoreCase(cs)){//判断cs是否正确
				return null;
			}
			String data=null;
			
			try {
				data= returndata.substring((totalLength-2-length)*2,(totalLength-2)*2);//数据块
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
			if((control.equals("84")||control.equals("9c"))&&length==0){
				data="success";
			}else if(control.equalsIgnoreCase("c4")&&length==1){
				data="fail";
			}
			System.out.println(address+"  "+dataFlagR+"   "+data);
		}
		return "s";
	}
	public static String getCS(String csBeforeStr){
		byte[] csBeforeByte= Convert.hexStringToBytes(csBeforeStr);
		byte[] csB=new byte[1];
		for (int i = 0; i < csBeforeByte.length; i++) {
			csB[0]+=csBeforeByte[i];
		}
		return Convert.bytesToHexString(csB);
	}
}

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值