在netty开发中,很多时候需要传递JAVA对象,这就需要序列化对象,虽然我们可以使用JAVA进行对象序列化,netty去传输,但是java序列化的硬伤太多,比如JAVA无法跨语言,序列化后码流太大,序列化性能太低等,
主流的编解码框架:
JBoss 的 Marshalling包
google 的 Protobuf
基于 Protobuf 的 Kyro
MessagePack 框架
我的例子是使用 Marshalling 的,首先需要引入marshalling 相关jar包
jboss-marshalling-serial-1.3.0.CR9.jar jboss-marshalling-1.3.0.CR9.jar
netty的jar包肯定是需要的,你要开发netty肯定有这些吧
我们首先来看server的代码:
package netty.serial;
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;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.nio.channels.ServerSocketChannel;
public class Server {
public static void main(String[] args) throws Exception{
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, worker)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.handler(new LoggingHandler(LogLevel.INFO)) // 设置日志
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
socketChannel.pipeline().addLast(new ServerHandler());
}
});
ChannelFuture f = bootstrap.bind(8765).sync();
f.channel().closeFuture().sync();
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
serverHander 代码:
package netty.serial;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import util.GzipUtil;
import java.io.File;
import java.io.FileOutputStream;
public class ServerHandler extends ChannelHandlerAdapter {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 由于确认了类型,可以直接转
Req req = (Req) msg;
byte[] attachment = GzipUtils.unZip(req.getAttachment());
String path = System.getProperty("user.dir") + File.separatorChar + "receive" + File.separatorChar + "111un zip.png";
FileOutputStream fos = new FileOutputStream(path);
fos.write(attachment);
fos.close();
System.out.println(req.toString());
Resp resp = new Resp();
resp.setId(req.getId());
resp.setName("resp:" + req.getName());
resp.setResponseMessage("responseMsg:" + req.getId());
// Client
ctx.writeAndFlush(resp);
}
}
请求类Req代码(实现Serializable 接口):
package netty.serial;
import java.io.Serializable;
import java.util.Arrays;
public class Req implements Serializable {
private static final Long SerialVersionUID = 1l;
private int id;
private String name;
private byte[] attachment;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public byte[] getAttachment() {
return attachment;
}
public void setAttachment(byte[] attachment) {
this.attachment = attachment;
}
@Override
public String toString() {
return "Req{" +
"id=" + id +
", name='" + name + '\'' +
", attachment=" + Arrays.toString(attachment) +
'}';
}
}
返回类 Resp 代码(实现Serializable):
package netty.serial;
import java.io.Serializable;
public class Resp implements Serializable {
private static final Long SerialVersionUID = 1l;
private int id;
private String name;
private String responseMessage;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(String responseMessage) {
this.responseMessage = responseMessage;
}
@Override
public String toString() {
return "Resp{" +
"id=" + id +
", name='" + name + '\'' +
", responseMessage='" + responseMessage + '\'' +
'}';
}
}
MarshallingCodeCFactory 工具类,主要是使用 Marshalling进行序列化:
package netty.serial;
import io.netty.handler.codec.marshalling.*;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
public class MarshallingCodeCFactory {
/**
*@Description: 创建 Marshalling解码器 MarshallingDecoder
*@Param:
*@Author: guanzhou.su, dont know what is mean? contact me at QQ:838951396, wechat:13824866769
*@Date: 2019/9/21
*@return:
*
**/
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;
}
/**
*@Description: 创建 Marshalling编码器 MarshallingEncoder
*@Param:
*@Author: guanzhou.su, dont know what is mean? contact me at QQ:838951396, wechat:13824866769
*@Date: 2019/9/21
*@return:
*
**/
public static MarshallingEncoder buildMarshallingEncoder() {
final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
MarshallingEncoder decoder = new MarshallingEncoder(provider);
return decoder;
}
}
由于示例中还包括了文件的压缩,所以也有一个文件工具类 GzipUtils
package netty.serial;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
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[] unZip(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;
}
}
接下来就是client端的代码:
package netty.serial;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import java.io.File;
import java.io.FileInputStream;
public class Client {
public static void main(String[] args) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
socketChannel.pipeline().addLast(new ClientHandler());
}
});
ChannelFuture cf1 = b.connect("127.0.0.1", 8765).sync();
for (int i = 0; i < 5; i++) {
Req req = new Req();
req.setId(i);
req.setName("pro" + i);
// 传递文件,把文件压缩后转 byte
String readPath = System.getProperty("user.dir") + File.separatorChar + "source" + File.separatorChar + "111.png";
File file = new File(readPath);
FileInputStream in = new FileInputStream(file);
byte[] data = new byte[in.available()];
in.read(data);
in.close();
req.setAttachment(GzipUtils.gzip(data));
cf1.channel().writeAndFlush(req);
}
cf1.channel().closeFuture().sync();
}
}
Client处理类 ClientHandler :
package netty.serial;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;
public class ClientHandler extends ChannelHandlerAdapter {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
Resp resp = (Resp) msg;
System.out.println("client recive:" + resp.toString());
} finally {
ReferenceCountUtil.release(msg);
}
}
}