JBoss Marshalling 是一个 Java 对象序列化包,对 JDK 默认的序列化框架进行了优化,但又保持与 Serializable 接口的兼容,同时增加了一些可调用的参数和附加的属性,这些参数可通过工厂类进行配置。
<dependency>
<groupId>io.nettygroupId>
<artifactId>netty-allartifactId>
<version>4.1.10.Finalversion>
dependency>
<dependency>
<groupId>org.jboss.marshallinggroupId>
<artifactId>jboss-marshallingartifactId>
<version>1.4.11.Finalversion>
dependency>
<dependency>
<groupId>org.jboss.marshallinggroupId>
<artifactId>jboss-marshalling-serialartifactId>
<version>1.4.11.Finalversion>
dependency>
// 1. 客户端发送5个Boy实例对象
五个蓝孩子组队去表白:
[name=同学 1, height=170, faceScore=91, isRich=false]
[name=同学 2, height=180, faceScore=89, isRich=true]
[name=同学 3, height=162, faceScore=93, isRich=false]
[name=同学 4, height=169, faceScore=96, isRich=true]
[name=同学 5, height=172, faceScore=84, isRich=false]
// 2. 服务端收到依次收到每个Boy实例对象
收到表白:[name=同学 1, height=170, faceScore=91, isRich=false]
收到表白:[name=同学 2, height=180, faceScore=89, isRich=true]
收到表白:[name=同学 3, height=162, faceScore=93, isRich=false]
收到表白:[name=同学 4, height=169, faceScore=96, isRich=true]
收到表白:[name=同学 5, height=172, faceScore=84, isRich=false]
// 3. 服务端(女孩)判断此boy有没有高富帅属性,给予回复
收到女生回复 :to 同学 1 : 中意你哦!
收到女生回复 :to 同学 2 : 中意你哦!
收到女生回复 :to 同学 3 : 中意你哦!
收到女生回复 :to 同学 4 : 中意你哦!
收到女生回复 :to 同学 5 : 骚年回家继续努力吧!
// Server 服务端主程序
public class Server
{
public void bind(int port) throws InterruptedException
{
EventLoopGroup boss=new NioEventLoopGroup();
EventLoopGroup worker=new NioEventLoopGroup();
try
{
ServerBootstrap b=new ServerBootstrap();
b.group(boss,worker)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1280)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer()
{
@Override
protected void initChannel(SocketChannel ch) throws Exception
{
ch.pipeline().addLast(
MarshallingCodeCFactory.buildMarshallingDecoder()
);
ch.pipeline().addLast(
MarshallingCodeCFactory.buildMarshallingEncoder()
);
ch.pipeline().addLast(
new ServerHandler()
);
}
});
ChannelFuture f=b.bind(port).sync();
f.channel().closeFuture().sync();
} finally
{
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException
{
int port = 8005;
new Server().bind(port);
}
}
// ServerHandler 服务端Handler
@ChannelHandler.Sharable
public class ServerHandler extends ChannelInboundHandlerAdapter
{
@Override
public void channelRead(ChannelHandlerContext ctx , Object msg) throws Exception
{
Boy boy= (Boy) msg;
System.out.println("收到表白:"+boy);
ctx.writeAndFlush(reply(boy));
}
private static GirlResponse reply(Boy boy){
GirlResponse gr=new GirlResponse();
if(boy.getFaceScore()>=90||boy.getHeight()>=180||boy.isRich()){
gr.setMsg("to "+boy.getName()+" : 中意你哦!");
}else{
gr.setMsg("to "+boy.getName()+" : 骚年回家继续努力吧!");
}
return gr;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx , Throwable cause) throws Exception
{
cause.printStackTrace();
ctx.close();
}
}
// 客户端主程序
public class Client
{
public void connect(int port, String host) throws InterruptedException
{
EventLoopGroup group = new NioEventLoopGroup();
try
{
Bootstrap b=new Bootstrap();
b.group(group)
.option(ChannelOption.TCP_NODELAY, true)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer()
{
@Override
protected void initChannel(SocketChannel ch) throws Exception
{
ch.pipeline().addLast(
MarshallingCodeCFactory.buildMarshallingDecoder()
);
ch.pipeline().addLast(
MarshallingCodeCFactory.buildMarshallingEncoder()
);
ch.pipeline().addLast(new ClientHandler());
}
});
ChannelFuture f= b.connect(host, port).sync();
f.channel().closeFuture().sync();
} finally
{
group.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException
{
int port = 8005;
String host="127.0.0.1";
new Client().connect(port, host);
}
}
// 客户端Handler
public class ClientHandler extends ChannelInboundHandlerAdapter
{
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception
{
System.out.println("五个蓝孩子组队去表白:");
Random r = new Random();
for (int i = 1; i <= 5; i++)
{
Boy boy = new Boy();
boy.setName("同学 " + i);
boy.setFaceScore(r.nextInt(20) + 80);
boy.setHeight(r.nextInt(25) + 160);
boy.setRich(i % 2 == 0);
ctx.write(boy);
System.out.println(boy);
}
ctx.flush();
}
@Override
public void channelRead(ChannelHandlerContext ctx , Object msg) throws Exception
{
GirlResponse gr= (GirlResponse) msg;
System.out.println("收到女生回复 :"+gr.getMsg());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx , Throwable cause) throws Exception
{
cause.printStackTrace();
ctx.close();
}
}
// Marshalling 编解码器工厂
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和单个消息序列化后的最大长度
return new MarshallingDecoder(provider ,
1024 * 1024 * 1);
}
/**
* 创建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对象序列化为二进制数组
return new MarshallingEncoder(provider);
}
}
要进行序列化的类一定要实现 Seriaizable接口,并且添加serialVersionUID
// Boy.java
public class Boy implements Serializable
{
private static final long serialVersionUID = 3333140133888151024L;
/**
* 姓名
*/
private String name;
/**
* 身高
*/
private int height;
/**
* 颜值
*/
private int faceScore;
/**
* 是否富有
*/
private boolean isRich;
@Override
public String toString()
{
return "[name="+name+", height="+height+", faceScore="+faceScore+", isRich="+isRich+"]";
}
// GirlResponse.java
public class GirlResponse implements Serializable
{
private static final long serialVersionUID = -2619994978640439932L;
private int code;
private String msg;
测试了下粘包拆包的场景,发现程序运行依旧正常,说明Marshalling 编解码器本身是支持粘包拆包的处理。
究其原因,来看下源码:
// MarshallingEncoder 部分源码
@Sharable
public class MarshallingEncoder extends MessageToByteEncoder<Object> {
private static final byte[] LENGTH_PLACEHOLDER = new byte[4];
。。。。
。。。。
。。。。
@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
Marshaller marshaller = provider.getMarshaller(ctx);
int lengthPos = out.writerIndex();
out.writeBytes(LENGTH_PLACEHOLDER);
可以看到MarshallingEncoder在编码时会在报文头部加上4个字节(LENGTH_PLACEHOLDER 用来记录报文长度)。
// MarshallingDecoder 部分源码
public class MarshallingDecoder extends LengthFieldBasedFrameDecoder {
。。。
。。。
。。。
public MarshallingDecoder(UnmarshallerProvider provider, int maxObjectSize) {
// 默认头部长度为4字节
super(maxObjectSize, 0, 4, 0, 4);
this.provider = provider;
}
从上面源码可以发现MarshallingDecoder 继承自LengthFieldBaseFrameDecoder, 这是netty 提供的一个用于解决粘包拆包问题的解码器,主要原理就是在报文头部记录报文长度。
关于粘包拆包问题可以查看》》》
// 在版本大于2的情况下,以下获取到的MarshallerFactory 为null
final MarshallerFactory marshallerFactory = Marshalling
.getProvidedMarshallerFactory("serial");