前言:
本文只是一个简单的小随笔,讲述了LengthFieldBasedFrameDecoder如何使用。
关于NIO和 netty,要弄懂(还不敢说是熟悉)还是要花一些时间的。近期会更新一系列的文章,专门讲述NIO和Netty,应该会比网上其他的文章讲解的更透彻明白一些。以前写过一本书叫做Java并发编程系统与模型
,对于初学者是绝对有用的,对整个JAVA 并发可以起到一个提纲挈领的作用,个人觉得"先整体,后局部”的学习方式要好一些(可以参考《金字塔原理》。
正所谓:“善删者字去而意留,善敷者辞殊而意显”,我写的文章都比较简单简洁。基本上都是为说明某个问题而来的,其思考也是按照先有骨架,后有肉的思考方式去思考的。
public class CustomMsg {
// 类型 系统编号 0xAB 表示A系统,0xBC 表示B系统
private byte type;
// 信息标志 0xAB 表示心跳包 0xBC 表示超时包 0xCD 业务信息包
private byte flag;
// 主题信息的长度
private int length;
// 主题信息
private String body;
public CustomMsg() {
}
public CustomMsg(byte type, byte flag, int length, String body) {
this.type = type;
this.flag = flag;
this.length = length;
this.body = body;
}
public byte getType() {
return type;
}
public void setType(byte type) {
this.type = type;
}
public byte getFlag() {
return flag;
}
public void setFlag(byte flag) {
this.flag = flag;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public byte[] getBytes() {
byte[] bodyBytes = this.body.getBytes();
int bodyLength = bodyBytes.length;
byte[] bytes = new byte[bodyLength + 6];
bytes[0] = this.type;
bytes[1] = this.flag;
bytes[2] = (byte)((bodyLength >> 24) & 0xFF);
bytes[3] = (byte)((bodyLength >> 16) & 0xFF);
bytes[4] = (byte)((bodyLength >> 8) & 0xFF);
bytes[5] = (byte)(bodyLength & 0xFF);
for (int i = 0; i < bodyBytes.length; i++) {
bytes[i + 6] = bodyBytes[i];
}
return bytes;
}
}
public class CustomServer {
private static final int MAX_FRAME_LENGTH = 1024 * 1024;
private static final int LENGTH_FIELD_LENGTH = 4;
private static final int LENGTH_FIELD_OFFSET = 2;
private static final int LENGTH_ADJUSTMENT = 0;
private static final int INITIAL_BYTES_TO_STRIP = 0;
private int port;
public CustomServer(int port) {
this.port = port;
}
public void start() {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap sbs = new ServerBootstrap().group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new CustomDecoder(MAX_FRAME_LENGTH, LENGTH_FIELD_LENGTH,
LENGTH_FIELD_OFFSET, LENGTH_ADJUSTMENT, INITIAL_BYTES_TO_STRIP, false));
ch.pipeline().addLast(new CustomServerHandler());
};
}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);
// 绑定端口,开始接收进来的连接
ChannelFuture future = sbs.bind(port).sync();
System.out.println("Server start listen at " + port);
future.channel().closeFuture().sync();
} catch (Exception e) {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new CustomServer(10000).start();
}
}
public class CustomServerHandler extends SimpleChannelInboundHandler<Object> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
if(msg instanceof CustomMsg) {
CustomMsg customMsg = (CustomMsg)msg;
System.out.println("Client->Server:"+ctx.channel().remoteAddress()+" send "+customMsg.getBody());
}
}
}
public class CustomDecoder extends LengthFieldBasedFrameDecoder {
//判断传送客户端传送过来的数据是否按照协议传输,头部信息的大小应该是 byte+byte+int = 1+1+4 = 6
private static final int HEADER_SIZE = 6;
private byte type;
private byte flag;
private int length;
private String body;
/**
*
* @param maxFrameLength 解码时,处理每个帧数据的最大长度
* @param lengthFieldOffset 该帧数据中,存放该帧数据的长度的数据的起始位置
* @param lengthFieldLength 记录该帧数据长度的字段本身的长度
* @param lengthAdjustment 修改帧数据长度字段中定义的值,可以为负数
* @param initialBytesToStrip 解析的时候需要跳过的字节数
* @param failFast 为true,当frame长度超过maxFrameLength时立即报TooLongFrameException异常,为false,读取完整个帧再报异常
*/
public CustomDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength,
int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
super(maxFrameLength, lengthFieldOffset, lengthFieldLength,
lengthAdjustment, initialBytesToStrip, failFast);
}
@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
if (in == null) {
return null;
}
if (in.readableBytes() < HEADER_SIZE) {
throw new Exception("可读信息段比头部信息都小,你在逗我?");
}
//注意在读的过程中,readIndex的指针也在移动
type = in.readByte();
flag = in.readByte();
length = in.readInt();
if (in.readableBytes() < length) {
throw new Exception("body字段你告诉我长度是"+length+",但是真实情况是没有这么多,你又逗我?");
}
ByteBuf buf = in.readBytes(length);
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
body = new String(req, "UTF-8");
CustomMsg customMsg = new CustomMsg(type,flag,length,body);
return customMsg;
}
}Client端如下:
public class Client {
public static void main(String[] args) throws InterruptedException, UnknownHostException, IOException {
final int port = 10000;
final String clientname = "clientThread";
Socket socket = new Socket("127.0.0.1", port);
final OutputStream out = socket.getOutputStream();
new Thread(clientname + "_write") {
@Override
public void run() {
int a = 0;
for(int i=0;i<100;i++) {
CustomMsg customMsg = new CustomMsg((byte)0xAB, (byte)0xCD, "Hello,Netty".length(), "Hello,Netty");
try {
out.write(customMsg.getBytes());
out.flush();
a++;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
}
}这样就自定义一个解码器。
观察如下代码:
public byte[] getBytes() {
byte[] bodyBytes = this.body.getBytes();
int bodyLength = bodyBytes.length;
byte[] bytes = new byte[bodyLength + 6];
bytes[0] = this.type;
bytes[1] = this.flag;
bytes[2] = (byte)((bodyLength >> 24) & 0xFF);
bytes[3] = (byte)((bodyLength >> 16) & 0xFF);
bytes[4] = (byte)((bodyLength >> 8) & 0xFF);
bytes[5] = (byte)(bodyLength & 0xFF);
for (int i = 0; i < bodyBytes.length; i++) {
bytes[i + 6] = bodyBytes[i];
}
return bytes;
}
}实际上与如下代码对应,相当于自己将一个对象变成byte数组:
String body = msg.getBody();
byte[] bodyBytes = body.getBytes(Charset.forName("utf-8"));
out.writeByte(msg.getType());
out.writeByte(msg.getFlag());
out.writeInt(bodyBytes.length);
out.writeBytes(bodyBytes);