v2.5
尝试心跳 、数据压缩、TCP沾包拆包
-
心跳机制的实现 Netty源码解析-IdleStateHandler
实现通过用户设置 多少秒没读 多少秒没写 多少秒没读写进行报读空闲、写空闲、读写空闲
当客户端一定时间内没有收到相应的请求,就将客户端的连接关闭,服务端是需要持续-
注解创建 注解创建结束后配置都在common包中
-
注解编写
package annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //是否开启的工具类 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface HeartBeatTool { boolean isOpenFunction() default false; //是否开启的标志 int readerIdleTimeSeconds() default 5; //超过多少时间触发读空闲事件 int writerIdleTimeSeconds() default 5; //超过多少时间触发写空闲事件 int allIdleTimeSeconds() default 3; //超过多少时间触发读写空闲事件 }
-
注解到配置类上
package heartbeat; import annotation.HeartBeatTool; @HeartBeatTool(isOpenFunction = true, readerIdleTimeSeconds = 4, writerIdleTimeSeconds = 4, allIdleTimeSeconds = 2) public interface HeartBeat { }
-
-
心跳事件对应处理器添加构造
//上面的改造 private static HeartBeatTool heartBeatToolAnnotation = HeartBeat.class.getAnnotation(HeartBeatTool.class); /* v2.5更新添加读写空闲处理器 IdleStateHandler是netty提供的处理读写空闲的处理器 readerIdleTimeSeconds 多长时间没有读 就会传递一个心跳包进行检测 writerIdleTimeSeconds 多长时间没有写 就会传递一个心跳包进行检测 allIdleTimeSeconds 多长时间没有读写 就会传递一个心跳包进行检测 当事件触发后会传递给下一个处理器进行处理,只需要在下一个处理器中实现userEventTriggered事件即可 */ //时间和实不实现 根据注解 判断是否开启 //记住后面一定是要有一个处理器 用来处理触发事件 if (heartBeatToolAnnotation.isOpenFunction()) { pipeline.addLast(new IdleStateHandler( heartBeatToolAnnotation.readerIdleTimeSeconds(), heartBeatToolAnnotation.writerIdleTimeSeconds(), heartBeatToolAnnotation.allIdleTimeSeconds()) ); }
-
事件监听处理
//用来判断是否出现了空闲事件 如果出现了那就进行相应的处理 @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { IdleStateEvent event = (IdleStateEvent) evt; //设置一个事件字段 String eventType = null; switch (event.state()) { case READER_IDLE: eventType = "读空闲"; break; case WRITER_IDLE: eventType = "写空闲"; break; case ALL_IDLE: eventType = "读写空闲"; break; } System.out.println(ctx.channel().remoteAddress()+"发生超时事件"+eventType+":连接关闭"); ctx.close(); } }
-
可以实现保持连接
- 通过在客户端再设置一个处理器 超过几秒 没发送,就发送一次 ,保持心跳,因为我这边额外的逻辑,如果要实现得重新写一下,对应的处理器 所以下次实现
-
-
数据压缩
对于小体积反而会变大,如果是传输大的对象可以选择开启压缩-
书写注解 进行判断是否开启
package annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //进行判断解压缩功能是否开启 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface CompressFunction { boolean isOpenFunction(); }
//冠上注解 package compress; import annotation.CompressFunction; @CompressFunction(isOpenFunction = true) public interface Compress { }
-
各个解压缩工具类的实现(要知道它们的区别,同时了解它们的实现方式)
-
bzip
-
依赖引入
<!--Bzip依赖引入--> <dependency> <groupId>org.apache.ant</groupId> <artifactId>ant</artifactId> <version>1.10.6</version> </dependency>
-
bzip解压缩工具类
package compress.bzip; import compress.CompressTpye; import org.apache.tools.bzip2.CBZip2InputStream; import org.apache.tools.bzip2.CBZip2OutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; //apache下的一个解压缩工具 bzip public class BZipUtils implements CompressTpye { private static final int BUFFER_SIZE = 8192; @Override public byte[] compress(byte[] bytes) throws IOException { //数组输出流 ByteArrayOutputStream bos = new ByteArrayOutputStream(); CBZip2OutputStream cbZip2OutputStream = new CBZip2OutputStream(bos); cbZip2OutputStream.write(bytes); //finish 当压缩结束后 结束 cbZip2OutputStream.finish(); byte[] request = bos.toByteArray(); //关闭 cbZip2OutputStream.close(); bos.close(); return request; } @Override public byte[] deCompress(byte[] bytes) throws IOException { //输入进行解压 再将解压后的传入输出 ByteArrayInputStream bis = new ByteArrayInputStream(bytes); CBZip2InputStream cbZip2InputStream = new CBZip2InputStream(bis); //将输入流中的数据写入 ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buffer = new byte[BUFFER_SIZE]; int n; while ((n=cbZip2InputStream.read(buffer))>-1) { out.write(buffer,0,n); } byte[] response = out.toByteArray(); //关闭对应 out.close(); cbZip2InputStream.close(); bis.close(); return response; } }
-
结果
-
-
deflater
-
deflater解压缩工具类(因为这是java.util.zip下自带的工具类不用引入依赖)
package compress.deflater; import compress.CompressTpye; import java.io.ByteArrayOutputStream; import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; public class DeflaterUtils implements CompressTpye { //固定读取字节数组大小 private static final int BUFFER_SIZE = 8192; @Override public byte[] compress(byte[] bytes) { int length; Deflater deflater = new Deflater(); deflater.setInput(bytes); deflater.finish(); byte[] outputBytes = new byte[BUFFER_SIZE]; ByteArrayOutputStream bos = new ByteArrayOutputStream(); while (!deflater.finished()) { length = deflater.deflate(outputBytes); bos.write(outputBytes,0,length); } deflater.end(); return bos.toByteArray(); } //解压 @Override public byte[] deCompress(byte[] bytes) throws DataFormatException { int length; Inflater inflater = new Inflater(); inflater.setInput(bytes); byte[] outputBytes = new byte[BUFFER_SIZE]; ByteArrayOutputStream bos = new ByteArrayOutputStream(); while (!inflater.finished()) { length = inflater.inflate(outputBytes); bos.write(outputBytes,0,length); } inflater.end(); return bos.toByteArray(); } }
-
结果
-
-
gzip
-
gzip实现解压缩工具类 (这也是java.util.zip下面的类 不引入依赖 实现过程和)
package compress.gzip; import compress.CompressTpye; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; //gzip实现解压缩 实现过程和bzip很相似 public class GZipUtils implements CompressTpye { private static final int BUFFER_SIZE = 8192; @Override public byte[] compress(byte[] bytes) throws IOException { //数组输出流 ByteArrayOutputStream bos = new ByteArrayOutputStream(); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(bos); gzipOutputStream.write(bytes); gzipOutputStream.flush(); //finish 当压缩结束后 结束 gzipOutputStream.finish(); byte[] request = bos.toByteArray(); //关闭 gzipOutputStream.close(); bos.close(); return request; } @Override public byte[] deCompress(byte[] bytes) throws IOException { //输入进行解压 再将解压后的传入输出 ByteArrayInputStream bis = new ByteArrayInputStream(bytes); GZIPInputStream gzipInputStream = new GZIPInputStream(bis); //将输入流中的数据写入 ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buffer = new byte[BUFFER_SIZE]; int n; while ((n=gzipInputStream.read(buffer))>-1) { out.write(buffer,0,n); } byte[] response = out.toByteArray(); //关闭对应 out.close(); gzipInputStream.close(); bis.close(); return response; } }
-
结果
-
-
lz4
-
依赖引入
<!--Lz4依赖引入 进行解压缩--> <dependency> <groupId>org.lz4</groupId> <artifactId>lz4-java</artifactId> <version>1.7.1</version> </dependency>
-
lz4实现解压缩工具类
package compress.lz4; import compress.CompressTpye; import net.jpountz.lz4.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; public class Lz4Utils implements CompressTpye { private static final int BUFFER_SIZE = 8192; @Override public byte[] compress(byte[] bytes) throws IOException { //压缩的工具类吧 LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); LZ4BlockOutputStream lz4BlockOutputStream = new LZ4BlockOutputStream(outputStream,BUFFER_SIZE,compressor); lz4BlockOutputStream.write(bytes); lz4BlockOutputStream.close(); return outputStream.toByteArray(); } @Override public byte[] deCompress(byte[] bytes) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); LZ4FastDecompressor decompressor = LZ4Factory.fastestInstance().fastDecompressor(); ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); LZ4BlockInputStream decompressedInputStream = new LZ4BlockInputStream(inputStream,decompressor); int count; byte[] buffer = new byte[BUFFER_SIZE]; while ((count=decompressedInputStream.read(buffer))!=-1) { outputStream.write(buffer,0,count); } decompressedInputStream.close(); return outputStream.toByteArray(); } }
-
结果
-
-
zip
-
zip解压缩工具类实现
package compress.zip; import compress.CompressTpye; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; public class ZipUtils implements CompressTpye { private static final int BUFFER_SIZE = 8192; @Override public byte[] compress(byte[] bytes) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); ZipOutputStream zip = new ZipOutputStream(out); ZipEntry entry = new ZipEntry("zip"); entry.setSize(bytes.length); zip.putNextEntry(entry); zip.write(bytes); zip.closeEntry(); return out.toByteArray(); } @Override public byte[] deCompress(byte[] bytes) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); ZipInputStream zip = new ZipInputStream(new ByteArrayInputStream(bytes)); byte[] buffer = new byte[BUFFER_SIZE]; while (zip.getNextEntry()!=null) { int n; while ((n = zip.read(buffer))!=-1) { out.write(buffer,0,n); } } return out.toByteArray(); } }
-
结果
-
-
-
-
TCP沾包拆包
因为我直接是一次只传一个字节数组,通过对应对象转成的,所以暂时还遇不到沾包拆包问题,在nio的编写中我解决了沾包拆包的问题v1.1 通过阻塞io解决的
解决方案
- 使用自定义协议 + 编解码器 来解决
- 关键就是要解决 服务器端每次读取数据长度的问题,这个问题解决,就不会出现服务器多读或少读数据的问题,从而避免TCP粘包、拆包
-
测试压缩不压缩所用所占的大小是否变化 同时要比较不同的解压缩方法之间的差异 由来(未看各个方法的原理)
Java不同压缩算法的性能比较_