1.编解码技术说明
编解码技术,说白了就是java序列化技术,序列化目的就两个,第一进行网络传输,第二对象持久化。
为什么使用netty编解码技术?虽然可以使用java进行对象序列化,netty去传输,但是java序列化的硬伤太多,比如java序列化没法跨语言、序列化后码流量太大、序列化性能太低等等;
主流的编解码框架:JBoss的Marshalling包,google的Protobuf,基于Protobuf的Kyro(dubbo),MessagePack框架;
2.jboss Marshalling
Jboss Marshalling是一个java对象序列化包,对JDK默认的序列化框架进行了优化,但又保持跟java.io.Serializable接口的兼容,同时增加了一些可调的参数和附加特性,类库jboss-marshalling-1.3.0、jboss-marshalling-serial-1.3.0;
下载地址:https://www.jboss.org/jbossmarshalling/downloads
Jboss Marshalling与Netty结合后进行序列化对象的代码编写非常简单。
step1:应用Marshalling提供的MarshallingCodeCFactory解码类
package com.cc.netty.serialized.marshalling;
import io.netty.handler.codec.marshalling.DefaultMarshallerProvider;
import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider;
import io.netty.handler.codec.marshalling.MarshallerProvider;
import io.netty.handler.codec.marshalling.MarshallingDecoder;
import io.netty.handler.codec.marshalling.MarshallingEncoder;
import io.netty.handler.codec.marshalling.UnmarshallerProvider;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
/**
* Marshalling工厂
* @author(alienware)
* @since 2014-12-16
*/
public final class MarshallingCodeCFactory {
/**
* 创建Jboss Marshalling解码器MarshallingDecoder
* @return MarshallingDecoder
*/
public static MarshallingDecoder buildMarshallingDecoder() {
// 首先通过Marshalling工具类的通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
// 创建了MarshallingConfiguration对象,配置了版本号为5
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
// 根据marshallerFactory和configuration创建provider
UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
// 构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024 * 1024 * 1);
return decoder;
}
/**
* 创建Jboss Marshalling编码器MarshallingEncoder
* @return MarshallingEncoder
*/
public static MarshallingEncoder buildMarshallingEncoder() {
final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
// 构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
MarshallingEncoder encoder = new MarshallingEncoder(provider);
return encoder;
}
}
step2:构建server/client
package com.cc.netty.serialized.marshalling;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class Server {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_RCVBUF, 1024 * 32)
.childOption(ChannelOption.SO_SNDBUF, 1024 * 32)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//在实际处理前进行编解码,服务器端接收消息先解码,然后返回消息的时候编码【约定】
ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
ch.pipeline().addLast(new ServerHandler());
}
});
//服务器端绑定端口并启动服务
ChannelFuture cf = serverBootstrap.bind(8765).sync();
//使用channel级别的监听close端口 阻塞的方式
cf.channel().closeFuture().sync();
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
package com.cc.netty.serialized.marshalling;
import java.io.File;
import java.io.FileOutputStream;
import com.cc.netty.utils.GzipUtils;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("server channel active...");
}
/**
* 真正的数据最终会走到这个方法进行处理
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
/* 1 读取客户端的数据*/
Request req = (Request) msg;
System.out.println("Server:"+req.getId()+","+req.getName()+","+req.getRequestMessage());
//使用GzipUtils压缩与解压缩工具类来接收压缩的字节数组
byte[] attachment = GzipUtils.ungzip(req.getAttachment());
//将接收的字节文件写入指定目录(持久化)
String path = System.getProperty("user.dir") + File.separatorChar + "receive" +File.separatorChar+"001.jpg";//给定文件名
FileOutputStream fileOutputStream = new FileOutputStream(path);
fileOutputStream.write(attachment);
fileOutputStream.close();
/* 2 返回响应数据*/
Response resp = new Response();
resp.setId(req.getId());
resp.setName("resp"+req.getName());
resp.setResponseMessage("响应内容:"+req.getRequestMessage());
ctx.writeAndFlush(resp);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.fireExceptionCaught(cause);
}
}
package com.cc.netty.serialized.marshalling;
import java.io.File;
import java.io.FileInputStream;
import com.cc.netty.utils.GzipUtils;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class Client {
public static void main(String[] args) throws Exception {
//1. 只需要一个线程组用于我们的实际处理(网络通信的读写)
EventLoopGroup workGroup = new NioEventLoopGroup();
//2 通过辅助类去构造client
Bootstrap b = new Bootstrap();
b.group(workGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
.option(ChannelOption.SO_RCVBUF, 1024 * 32)
.option(ChannelOption.SO_SNDBUF, 1024 * 32)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
ch.pipeline().addLast(new ClientHandler());
}
});
//客户端连接服务器端地址
ChannelFuture cf = b.connect("127.0.0.1", 8765).syncUninterruptibly();
//写出实际的对象数据
for(int i=0;i<10;i++) {
Request req = create(i);
cf.channel().writeAndFlush(req);
}
//使用channel级别的监听close端口 阻塞的方式
cf.channel().closeFuture().sync();
workGroup.shutdownGracefully();
}
/*构建请求数据*/
private static Request create(int seq) throws Exception {
Request request = new Request();
request.setId(seq +"");
request.setName("named:"+seq);
request.setRequestMessage("messaged"+seq);
String path = System.getProperty("user.dir") + File.separatorChar + "sources" +File.separatorChar+"001.jpg";//给定文件名,实际要传入filename变量
FileInputStream fis = new FileInputStream(new File(path));
byte[] data = new byte[fis.available()];//根据fis可用大小确定读入byte[] data字节数组大小
fis.read(data);
fis.close();
//将读到的data压缩,并封装到request
request.setAttachment(GzipUtils.gzip(data));
return request;
}
}
package com.cc.netty.serialized.marshalling;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
public class ClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client channel active...");
}
/**
* client处理响应数据
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
//读取服务器端返回数据
Response resp = (Response) msg;
System.out.println("Client"+resp.getId()+","+resp.getName()+","+resp.getResponseMessage());
} finally {
//只读必须关闭msg
ReferenceCountUtil.release(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.fireExceptionCaught(cause);
}
}
step3:压缩解压缩工具类
package com.cc.netty.utils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class GzipUtils {
public static byte[] gzip(byte[] data) throws Exception{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(bos);
gzip.write(data);
gzip.finish();
gzip.close();
byte[] ret = bos.toByteArray();
bos.close();
return ret;
}
public static byte[] ungzip(byte[] data) throws Exception{
ByteArrayInputStream bis = new ByteArrayInputStream(data);
GZIPInputStream gzip = new GZIPInputStream(bis);
byte[] buf = new byte[1024];
int num = -1;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while((num = gzip.read(buf, 0 , buf.length)) != -1 ){
bos.write(buf, 0, num);
}
gzip.close();
bis.close();
byte[] ret = bos.toByteArray();
bos.flush();
bos.close();
return ret;
}
public static void main(String[] args) throws Exception{
//读取文件
String readPath = System.getProperty("user.dir") + File.separatorChar + "sources" + File.separatorChar + "006.jpg";
File file = new File(readPath);
FileInputStream in = new FileInputStream(file);
byte[] data = new byte[in.available()];
in.read(data);
in.close();
System.out.println("文件原始大小:" + data.length);
//测试压缩
byte[] ret1 = GzipUtils.gzip(data);
System.out.println("压缩之后大小:" + ret1.length);
byte[] ret2 = GzipUtils.ungzip(ret1);
System.out.println("还原之后大小:" + ret2.length);
//写出文件
String writePath = System.getProperty("user.dir") + File.separatorChar + "receive" + File.separatorChar + "006.jpg";
FileOutputStream fos = new FileOutputStream(writePath);
fos.write(ret2);
fos.close();
}
}
step4:运行结果