Netty(二) Netty中進行Java對象傳輸---MsgPack編解碼(粘包拆包使用Head表示消息長度的方式)

高效編解碼有很多方式:json、protobuf、msgpack、hession1、hession2、XStream、默認序列化等

protobuf表現突出(碼流小、響應時間低)跨語言時應用廣泛。關於protobuf後續再介紹。

本章主要介紹使用MsgPack完成編解碼,在Netty中使用,達到可以傳輸的效果。

這裏要注意MsgPack反序列化的兩種方式:下面圖是官網粘的,Template可以換成被@Message標記的JavaBean類

這裏其實還隱藏一個問題,List的自定義對象 msgPack通過上面的方式好像無法做到反序列化。只找到兩個方式

https://github.com/msgpack/msgpack-java/blob/develop/msgpack-jackson/README.md

這種是通過jackson來做到的,總感覺很low

第二種:

1、MsgPackDecoder 解碼器

package com.back.codec;

import java.util.List;

import org.msgpack.MessagePack;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;

public class MsgPackDecoder extends MessageToMessageDecoder<ByteBuf>{

	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
		final byte[] array;
		int length = msg.readableBytes();
		array = new byte[length];
		msg.getBytes(msg.readerIndex(), array, 0, length);
		MessagePack msgPack = new MessagePack();
		out.add(msgPack.read(array));
	}

}

2、MsgPackEncoder 編碼器

package com.back.codec;

import org.msgpack.MessagePack;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
 * 編碼器
 * @author Administrator
 *
 */
public class MsgPackEncoder extends MessageToByteEncoder<Object>{

	@Override
	protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
		MessagePack msgPack = new MessagePack();
		byte[] raw = msgPack.write(msg);
		out.writeBytes(raw);
	}

}

3、RouteHandler  這裏使用了我們自定義的編解碼器 及兩個用於處理TCP粘包/拆包的處理器

package com.back.server;

import com.back.codec.MsgPackDecoder;
import com.back.codec.MsgPackEncoder;
import com.back.constant.NettyConstant;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
/**
 * 設置請求 要經過哪些處理的handler  
 * 公共的handler邏輯等 可以在此處添加  如:記錄日誌、消息編解碼等
 * LineBasedFrameDecoder+StringDecoder 來解決TCP粘包拆包問題原理
 * 前者識別換行符,同時設置單行最大字節 所以這兩個組合就是按行切換的文本解碼器
 * DelimiterBasedFrameDecoder+StringDecoder 則是通過制定分隔符(同時設置最大字節)來區分 每次消息末尾都要加指定分隔符
 * FixedLengthFrameDecoder+StringDecoder 則代表不管怎樣 每次讀取指定長度 字節的包 不在演示
 * 
 * LengthFieldPrepender會在消息頭處以兩個字節來標識消息長度
 * LengthFieldBasedFrameDecoder 按上面的標識和標識的長度來解碼消息
 * @author back
 *
 */
public class RouteHandler extends ChannelInitializer<SocketChannel> {

	@Override
	protected void initChannel(SocketChannel arg0) throws Exception {
		
		//arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));
		//ByteBuf delimiter = Unpooled.copiedBuffer(NettyConstant.DELIMITER.getBytes());
		//arg0.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
		//arg0.pipeline().addLast(new StringDecoder());
		arg0.pipeline().addLast("frameDecoder", 
				new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
		arg0.pipeline().addLast("msgpack decoder", new MsgPackDecoder());
		arg0.pipeline().addLast("frameEncoder",new LengthFieldPrepender(2));
		arg0.pipeline().addLast("msgpack encoder", new MsgPackEncoder());
		arg0.pipeline().addLast(new MessageHandler());
	}

}

4、MessageHandler

package com.back.server;

import org.msgpack.type.MapValue;
import org.msgpack.type.Value;
import org.msgpack.unpacker.Converter;

import com.back.test.UserInfo;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;


/**
 * 真實處理信息的handler
 * @author Administrator
 *
 */
public class MessageHandler extends ChannelHandlerAdapter {

	private RequestHandler handler = RequestHandler.getRequestHandler();
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		//String message = (String)msg;
		//System.out.println("請求報文 : " + message);
		//String dilimiter = System.getProperty("line.separator");
		//String resp = handler.simpleHandlerRequest(message)+NettyConstant.DELIMITER;
		//ByteBuf copiedBuffer = Unpooled.copiedBuffer(resp.getBytes());
		//ctx.writeAndFlush(copiedBuffer);
		Value req = (Value)msg;
		UserInfo read = new Converter(req).read(UserInfo.class);
		System.out.println("接收報文:" + read);
		UserInfo resp = handler.handlerUser(read);
		ctx.write(resp);
	}
	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
	}
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		ctx.close();
	}
}

5、NettyClient && ClientRouteHandler

package com.back.client;

import com.back.codec.MsgPackDecoder;
import com.back.codec.MsgPackEncoder;
import com.back.constant.NettyConstant;

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.ChannelOption;
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.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

public class SimpleNettyClient {

	public static void main(String[] args) {
		
		NioEventLoopGroup group = new NioEventLoopGroup();
		try {
			Bootstrap bootstrap = new Bootstrap();
			bootstrap.group(group).channel(NioSocketChannel.class)
			.option(ChannelOption.TCP_NODELAY, true)
			.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30000)
			.handler(new ClientRouteHandler());
			ChannelFuture future = bootstrap.connect("127.0.0.1", 8888).sync();
			System.out.println("client start!");
			future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			System.out.println(e.getCause());
		}finally{
			group.shutdownGracefully();
		}
		
	}
}
class ClientRouteHandler extends ChannelInitializer<SocketChannel>{

	@Override
	protected void initChannel(SocketChannel ch) throws Exception {
		//ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
		//ByteBuf delimiter = Unpooled.copiedBuffer(NettyConstant.DELIMITER.getBytes());
		//ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
		//ch.pipeline().addLast(new StringDecoder());
		ch.pipeline().addLast("frameDecoder", 
				new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
		ch.pipeline().addLast("msgpack decoder", new MsgPackDecoder());
		ch.pipeline().addLast("frameEncoder",new LengthFieldPrepender(2));
		ch.pipeline().addLast("msgpack encoder", new MsgPackEncoder());
		ch.pipeline().addLast(new ClientMessageHandler());
	}
	
}

6、ClientMessageHandler

package com.back.client;

import org.msgpack.type.Value;
import org.msgpack.unpacker.Converter;

import com.back.constant.NettyConstant;
import com.back.test.UserInfo;

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

public class ClientMessageHandler extends ChannelHandlerAdapter {

	//byte[] req = ("time"+System.getProperty("line.separator")).getBytes();
	
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		for (int i = 0; i < 100; i++) {
//			byte[] req = ("time"+NettyConstant.DELIMITER).getBytes();
//			ByteBuf buf = Unpooled.buffer(req.length);
//			buf.writeBytes(req);
//			ctx.writeAndFlush(buf);
			UserInfo user = new UserInfo(i, "back"+i, "1234", "[email protected]");
			ctx.write(user);
		}
		ctx.flush();
	}
	
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		
		Value req = (Value)msg;
		UserInfo response = new Converter(req).read(UserInfo.class);
		System.out.println("接收到報文:"+response);
	}

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章