25 Netty框架 结合 JBoss-Marshalling编解码框架实现数据对象的传输

Netty编解码技术

编解码技术,说白了就是java序列化技术,序列化目的就两个。第一进行网络传输,第二进行对象持久化。

虽然我们可以使用java进行对象序列化,netty去传输,但是java序列化的硬伤太多,比如java序列化没法跨语言、序列化后码流太大、序列化性能太低等等。

主流的编解码框架:

JBossMarshalling

GoogleProtobuf

基于ProtobufKyro

MessagePack框架

 

 JBoss Marshalling

JBoss Marshalling是一个java对象序列化包,对JDK默认的序列化框架进行了优化,但又保持跟java.io.Serializable接口的兼容,同时增加了一些可可调的参数和附加特性。

类库:jboss-marshalling-1.3.0jboss-marshalling-serial-1.3.0

下载地址:https://www.jboss.org/jbossmarshalling/downloads

JBoss MarshallingNetty结合后进行序列化对象的代码编写非常简单。

 

使用Marshalling来对java对象序列化编码和解码,这样可以直接传输数据对象,且再获得数据的时候直接强转为对应的类性即可。


/**
 * 客户端需要发送的包装类
 */
package edu.sdut.marshalling;

import java.io.Serializable;
import java.util.Arrays;

public class Req implements Serializable{
	
	private static final long serialVersionUID = 1L;
	
	private String id;
	private String name;
	private String requestMessage;
	private byte[] attachment;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getRequestMessage() {
		return requestMessage;
	}
	public void setRequestMessage(String requestMessage) {
		this.requestMessage = requestMessage;
	}
	public byte[] getAttachment() {
		return attachment;
	}
	public void setAttachment(byte[] attachment) {
		this.attachment = attachment;
	}
	@Override
	public String toString() {
		return "Req [id=" + id + ", name=" + name + ", requestMessage=" + requestMessage + ", attachment="
				+ Arrays.toString(attachment) + "]";
	}
	
	
}

/**
 * 服务端需要回应的包装类
 */
package edu.sdut.marshalling;

import java.io.Serializable;

public class Resp implements Serializable{

	private static final long serialVersionUID = 1L;

	private String id;
	private String name;
	private String responseMessage;
	public String getId() {
		return id;
	}
	public void setId(String 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 + "]";
	}
	
}


/**
 * 创建
 * Marshalling编码器和解码器
 */
package edu.sdut.marshalling;

import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;

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;

public class MarshallingCodeCFactory {

	/**
	 * 创建Jboss Marshalling解码器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 
	 */
	public static MarshallingEncoder buildMarshallingEncoder() {
		
		//首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象
		final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
		//创建了MarshallingConfiguration对象  配置了版本号为5
		final MarshallingConfiguration configuration = new MarshallingConfiguration();
		configuration.setVersion(5);
		//根据marshallerFactory和configuration创建provider
		MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory,configuration);
				
		//创建Netty的MarshallingEncoder对象 MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制
		MarshallingEncoder encoder = new MarshallingEncoder(provider);
			
				
		return encoder;
	}
}
/**
 * 服务端处理类
 */
package edu.sdut.marshalling;

import java.io.File;
import java.io.FileOutputStream;
import java.util.Scanner;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;

public class ServerHandler extends ChannelHandlerAdapter{

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		/**
		 * 因为使用了Marshalling解码器,所以这里可以直接对传输过来的数据进行强转类型
		 * 而不再使用ByteBuf转换
		 */
		Req req = (Req)msg;
		System.out.println("Server : " + req.getId() + "," + req.getName() + "," + req.getRequestMessage());
		/****
		 * 对传过来的byte数据进行数据数据转换,写到文件中
		 * System.getProperty("user.dir")为特定写法,表示当前项目的目录
		 * File.separator表示文件之间的分隔符:windows中是"/",Linux中是"\";
		 */
		byte[] data = req.getAttachment();
		String path = System.getProperty("user.dir") + File.separator + "image2" + File.separator + "001.jpg";
		File file = new File(path);
		FileOutputStream out = new FileOutputStream(file);
		out.write(data);
		out.close();
		
		/**
		 * Server端得到数据后在返回数据给Client端
		 */
		Resp resp = new Resp();
		resp.setId(req.getId());
		resp.setName("resp" + req.getId());
		resp.setResponseMessage("响应内容 " + req.getId());
		ctx.writeAndFlush(resp);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
	}

}

/**
 * 客户端处理类
 */
package edu.sdut.marshalling;

import java.util.Scanner;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;

public class ClientHandler extends ChannelHandlerAdapter{

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		/**
		 * 注释见ServerHandler
		 */
		Resp resp = (Resp)msg;
		System.out.println("Client : " + resp.getId() + "," + resp.getName() + "," + resp.getResponseMessage());

	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
	}
	
}

/***
 * 服务端netty实现与配置
 */
package edu.sdut.marshalling;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

public class Server {
	public static void main(String[] args) throws Exception {
		
		//1、第一个线程组是用于接收Client端的接收的
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		//2、第二个线程组是用于实际的的业务处理操作的
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		
		//3、创建一个辅助类,对Server端进行一系列的配置
		ServerBootstrap b = new ServerBootstrap();
		//将两个线程组加入进来
		b.group(bossGroup,workerGroup)
		//执行使用NioServerSocketChannel类型的通道
		.channel(NioServerSocketChannel.class)
		//在这添加一个handler作为打印日志使用
		.handler(new LoggingHandler(LogLevel.INFO))
		//一定要使用childHandler去保定具体的事件处理器
		.childHandler(new ChannelInitializer<SocketChannel>() {

			@Override
			protected void initChannel(SocketChannel sc) throws Exception {
				/**
				 * 重点
				 * 对数据对象进行编码解码
				 */
				sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
				sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
				//ServerHandler数据处理类
				sc.pipeline().addLast(new ServerHandler());
			}
			
		})
		.option(ChannelOption.SO_BACKLOG,128)
		.option(ChannelOption.SO_SNDBUF, 32*1024)   //设置发送缓冲大小
		.option(ChannelOption.SO_RCVBUF, 32*1024)   //设置接收缓冲大小
		//保持连接
		.childOption(ChannelOption.SO_KEEPALIVE, true);
		
		
		//绑定指定的端口进行监听,可以绑定多个端口
		ChannelFuture cf = b.bind(8765).sync(); //异步的方法
		
		//相当于一个阻塞的一个作用,不能让主程序停止,长连接
		cf.channel().closeFuture().sync();
		workerGroup.shutdownGracefully();
		bossGroup.shutdownGracefully();
		
	}
}

/**
 * 客户端netty实现and配置
 */
package edu.sdut.marshalling;

import java.io.File;
import java.io.FileInputStream;

import io.netty.bootstrap.Bootstrap;
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;
/**
 * Client端
 * @author Vision_TXG
 *
 */
public class Client {

	public static void main(String[] args) throws Exception {
		
		//建立线程组
		EventLoopGroup group = new NioEventLoopGroup();
		//创建一个辅助类,对Server端进行一系列的配置
		Bootstrap b = new Bootstrap();
		b.group(group)
		.channel(NioSocketChannel.class)
		.handler(new ChannelInitializer<SocketChannel>() {

			@Override
			protected void initChannel(SocketChannel sc) throws Exception {
				
				/**
				 * 重点
				 * 配置 使用Marshalling对数据对象进行解码编码
				 */
				sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
				sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
				//ClientHandler处理类
				sc.pipeline().addLast(new ClientHandler());
			}
			
		});
		
		//非阻塞的异步通道,与Server端建立连接
		ChannelFuture cf = 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);
			req.setRequestMessage("数据信息"+i);
			//***这里是对一个图片转换为byte类型进行传输
			String path = System.getProperty("user.dir") + File.separator + "image1" + File.separator + "loginbk.jpg";
			File file = new File(path);
			FileInputStream in = new FileInputStream(file);
			byte[] data = new byte[in.available()];
			in.read(data);
			in.close();
			//在传送之前可以先压缩数据
			req.setAttachment(data);
			//***
			//将数据对象写入通道并刷新
			cf.channel().writeAndFlush(req);
		}
		
		//发送完数据后保持与服务器的连接,长连接
		cf.channel().closeFuture().sync();
		group.shutdownGracefully();
	}
}





猜你喜欢

转载自blog.csdn.net/txgANG/article/details/80992066