使用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);
}
}
825

被折叠的 条评论
为什么被折叠?



