Netty:解码器

本文介绍了Netty中解决TCP粘包拆包问题的一种方法,特别是使用LineBaseFrameDecoder进行解码。内容包括理解粘包拆包问题、结束符解包思路分析,以及LineBaseFrameDecoder的工作原理,同时提到了其他类型的解码器如DelimiterBaseFrameDecoder和FixedLengthFrameDecoder。
摘要由CSDN通过智能技术生成

一、解决粘包拆包问题的方法

   当客户端向服务端发送了一个大的数据包时(如600M),TCP几乎不会一次把这个包完整的发送到服务端,TCP分把这个包分包,分几次发给服务端,至于TCP要分多少次,这是不可预测的。应用层如何正确的去区别每一条信息呢?下面介绍第一种分包的方法:以回车换行符作为消息的结束符,FTP也是采用这种方式,这个结束符也有点像CString`\0`.

  二、利用结束符解包的思路

   分析过程:

   1.消息中包含回车换行符(\n\r\n),否则这条消息是不合规则的

   2.获取到回车换行符的位置,得到这个位置后,就可以知道这个消息的 长度,根据这个长度读取数据.

如下图:

  +


Server;

/********************************************************************
 * 
 ********************************************************************
 * Netty 学习
 ********************************************************************
 */
package com.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 *
 * @author Jack Lei
 * @email 895896736@qq.com
 * @date 2016年3月13日 下午7:58:09
 */
public class Server {
	public Server(int port) {
		EventLoopGroup boss = new NioEventLoopGroup();
		EventLoopGroup work = new NioEventLoopGroup();

		ServerBootstrap boot = new ServerBootstrap();
		boot.group(boss, work)
			.channel(NioServerSocketChannel.class)
			.option(ChannelOption.TCP_NODELAY, true)
			.childHandler(new ServerChannelInitializer());
		try {
			ChannelFuture future = boot.bind(port).sync();
			future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			boss.shutdownGracefully();
			work.shutdownGracefully();
		}
	}

	public static void main(String[] args) {
		new Server(8090);
	}
}

/********************************************************************
 * 
 ********************************************************************
 * Netty 学习
 ********************************************************************
 */
package com.netty;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;

import com.netty.codec.PacketDecoder;
import com.netty.handler.ServerHandler;

/**
 *
 * @author Jack Lei
 * @email 895896736@qq.com
 * @date 2016年3月16日 上午1:28:55
 */
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {

	@Override
	protected void initChannel(SocketChannel ch) throws Exception {
		ch.pipeline()
			.addLast(new PacketDecoder(30))
			.addLast(new ServerHandler());
	}

}

/********************************************************************
 * 
 ********************************************************************
 * Netty 学习
 ********************************************************************
 */
package com.netty.handler;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 *
 * @author Jack Lei
 * @email 895896736@qq.com
 * @date 2016年3月16日 上午1:30:03
 */
public class ServerHandler extends ChannelInboundHandlerAdapter {
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		ByteBuf buf = (ByteBuf) msg;
		short packetId = buf.readShort();
		int bodySize = buf.readableBytes();
		byte[] body = new byte[bodySize];
		buf.readBytes(body);
		System.out.println("recive packet from client packetId = " + packetId + " , body = " + new String(body));
	}

}


/********************************************************************
 * 
 ********************************************************************
 * Netty 学习
 ********************************************************************
 */
package com.netty.codec;

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

import java.util.List;

/**
 *
 * @author Jack Lei
 * @email 895896736@qq.com
 * @date 2016年3月16日 上午1:31:08
 */
public class PacketDecoder extends ByteToMessageDecoder {
	/* 一条消息的最大长度* */
	private int	lineLength;

	public PacketDecoder(int lineLenth) {
		this.lineLength = lineLenth;
	}

	@Override
	protected void decode(	ChannelHandlerContext ctx,
							ByteBuf in,
							List<Object> out) throws Exception {
		int endPostion = findEndPostion(in);

		if (endPostion != -1) {
			int endWordLength = in.getByte(endPostion) == '\n' ? 1 : 2;
			int bodySize = (endPostion - in.readerIndex()) + endWordLength;
			out.add(in.readBytes(bodySize));
		} else {
			// 在指定消息长度的内没有读到结束符,就说明读到的是不符合规则的数据包
			if (in.readableBytes() >= lineLength) {
				throw new Exception("消息不完整。");
			}
		}

	}

	private int findEndPostion(ByteBuf buf) {
		int size = buf.writerIndex();
		for (int index = buf.readerIndex(); index < size; index++) {
			if (buf.getByte(index) == '\n') {
				return index;
				// 13 10
			} else if (buf.getByte(index) == '\r' && index < size - 1 && buf.getByte(index + 1) == '\n') {
				return index;
			}
		}
		return -1;
	}
}

Client:

/********************************************************************
 * 
 ********************************************************************
 * Netty 学习
 ********************************************************************
 */
package com.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 *
 * @author Jack Lei
 * @email 895896736@qq.com
 * @date 2016年3月13日 下午8:05:24
 */
public class Client {
	final short	packetId	= 10001;

	public Client(String host, int port) {
		EventLoopGroup work = new NioEventLoopGroup();
		Bootstrap boot = new Bootstrap();
		boot.group(work)
			.channel(NioSocketChannel.class)
			.handler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
						@Override
						public void channelActive(ChannelHandlerContext ctx) throws Exception {
							for (int i = 0; i < 12000; i++) {
								ByteBuf buf = Unpooled.buffer();
								buf.writeShort(packetId);
								// 每条请求消息以换行符分隔
								String body = "测试请求->" + i + System.getProperty("line.separator");
								buf.writeBytes(body.getBytes());
								System.out.println("send packet to Server packetId " + packetId + ", size = " + (body.getBytes().length + 2) + " , body = " + body);
								ctx.writeAndFlush(buf);
							}
						}


					});
				}
			});
		try {
			ChannelFuture future = boot.connect(host, port).sync();
			future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			work.shutdownGracefully();
		}

	}

	public static void main(String[] args) {
		new Client("127.0.0.1", 8090);
	}
}


  三、LineBaseFrameDecoder

LineBaseFrameDecoder,Netty提供的一个可以解决TCP粘包拆包的类,也是以回车换行符作为消息结束的标志,经常搭配StringDecoder一起使用。为了方便理解解码器,我写的PacketDecoder就是简化了LineBaseFrameDecoder.关于解码器还有其他几种写法,如限定消息的长度,通过定义消息长度来标识消息的长度,想了解的可以看下这几个类的源码(DelimiterBaseFrameDecoder,FixedLengthFrameDecoder.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值