高效編解碼有很多方式: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();
}
}