004 netty编解码技术_Netty_序列化_Marshalling使用

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:运行结果

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值