Netty(3) 编解码技术

236 阅读3分钟

Netty(4) 编解码技术

序列化的目的:

  1. 进行网络传输
  2. 对象持久化

主流的编解码框架

  1. JBoss的Marshalling包 2. 对JDK默认的序列化框架进行优化,保持跟java.io.Serializable接口兼容,同时增加一一些可调的参数和附加特性
  2. google的Protobuf
  3. 基于Protobuf的Kyro
  4. MessagePack框架

Marshalling

Server.java

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 Exception {
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workGroup = new NioEventLoopGroup();
		
		ServerBootstrap sb = new ServerBootstrap();
		sb.group(bossGroup, workGroup)
			.channel(NioServerSocketChannel.class)
			 .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 com.bfxy.netty.serialized.marshalling.ServerHandler());	
				}
			});
			//服务器端绑定端口并启动服务
			ChannelFuture cf = sb.bind(8765).sync();
			//使用channel级别的监听close端口 阻塞的方式
			cf.channel().closeFuture().sync();
			
			bossGroup.shutdownGracefully();
			workGroup.shutdownGracefully();
	}
}

ServerHandler.java

import java.io.File;
import java.io.FileOutputStream;
import com.bfxy.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.err.println("server channel active..");
    }
    
    /**
     * 真正的数据最终会走到这个方法进行处理
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        
    	//	读取客户端的数据
    	Request req = (Request)msg;
    	System.err.println("Server: " + req.getId() + ", " + req.getName() + ", " + req.getRequestMessage());
    	byte[] attachment = GzipUtils.ungzip(req.getAttachment());
    	String path = System.getProperty("user.dir") + File.separatorChar + "receive" + File.separatorChar + "001.jpg";
    	FileOutputStream fos = new FileOutputStream(path);
    	fos.write(attachment);
    	fos.close();
    	
    	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);
    }
}

Client.java

import java.io.File;
import java.io.FileInputStream;
import org.jboss.marshalling.Creator;
import com.bfxy.netty.quickstart.ClientHandler;
import com.bfxy.netty.utils.GzipUtils;
import io.netty.bootstrap.Bootstrap;
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.NioSocketChannel;

public class Client {	
	public static void main(String[] args) throws Exception {		
		//1. 创建两个线程组: 只需要一个线程组用于我们的实际处理(网络通信的读写)
		EventLoopGroup workGroup = new NioEventLoopGroup();
		
		//2 通过辅助类去构造server/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.buildMarshallingDecoder());
				ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
				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);
		}		
		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";
    	FileInputStream fis = new FileInputStream(new File(path));
    	byte[] data = new byte[fis.available()];
    	fis.read(data);
    	fis.close();
    	request.setAttachment(GzipUtils.gzip(data));
    	return request;
	}
}

ClientHandler.java

import io.netty.buffer.ByteBuf;
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.err.println("client channel active..");
    }
    
    /**
     * 真正的数据最终会走到这个方法进行处理
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    	try {
        	ByteBuf buf = (ByteBuf) msg;
            byte[] request = new byte[buf.readableBytes()];
            buf.readBytes(request);
            String requestBody = new String(request, "utf-8");
            System.err.println("Client: " + requestBody);			
		} finally {
			ReferenceCountUtil.release(msg);
		}
    }

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

Request.java

import java.io.Serializable;
public class Request implements Serializable {
	private static final long serialVersionUID = 8743187299189428700L;
	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;
	}
}

Response.java

import java.io.Serializable;
public class Response implements Serializable {
	private static final long serialVersionUID = -4639347903121316828L;
	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;
	}
}


import io.netty.handler.codec.marshalling.*;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;

/**
 * Marshalling工厂
 * @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;
    }
}