netty分割符和定长解码器的应用

   tcp以流的方式进行数据传输,上层的应用协议为了对消息进行区分,往往采用如下4种方式:

     1、消息固定长度,累计读取到长度一定的报文段后,就读取下一个报文段。

     2、将回车换行符做为消息结束符,例如ftp协议,这种方式在文本协议中应用比较广泛。

     3、将特殊的分割符作为消息结束标志,回车换行就是一种特殊的结束分隔符;

     4、通过在消息头中定义长度字段来标识消息的总长度。

    Netty对上面的4种应用做了统一的抽象,提供了4种解码器来解决对应的问题,有了这些解码器,我们不用自己对读取的报文进行人工解码,也不需要考虑tcp的拆包和粘包问题。上一节中我们介绍了LineBaseFrameDecoder解决tcp的粘包问题,这一节将介绍另外两种使用的解码器--------DelimterBasedFrameDecoder和FixedLengthFrameDecoder,前者可以自动完成以分割符做结束标志的消息解码,后者可以完成对消息定长的解码,他们都能解决tcp粘包和拆包的读半包问题。

  DelimterBasedFrameDecoder的使用,指定分割符来进行消息的分割,其代码如下:

public class EchoServer {

	public void bind(int port) throws Exception {

		EventLoopGroup boosGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(boosGroup, workerGroup).childOption(ChannelOption.TCP_NODELAY, true).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100)
					.handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {
						@Override
						protected void initChannel(SocketChannel ch) throws Exception {
							ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());
							ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
							ch.pipeline().addLast(new StringDecoder());
							ch.pipeline().addLast(new EchoServerHandler());
						}
					});
			ChannelFuture f = b.bind(port).sync();
			f.channel().closeFuture().sync();

		} finally {
			boosGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}

	}

	public static void main(String[] args) throws Exception {
		int port = 8080;
		if (args != null && args.length > 0) {
			try {
				port = Integer.parseInt(args[0]);
			} catch (Exception e) {

			}
		}
		new EchoServer().bind(port);

	}

}
  如上使用了$_进行消息的分割,EchoServerHandler如下:

public class EchoServerHandler extends ChannelHandlerAdapter {

	int counter = 0;
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		String body = (String) msg;
		System.out.println("this is " + ++counter + " times receive client : [" + body + " ]");
		body += "$_";
		ByteBuf echo = Unpooled.copiedBuffer(body.getBytes());
		ctx.writeAndFlush(echo);
	}
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		ctx.close();
	}
}
EchoClient如下:

public class EchoClient {

	public void connect(int port, String host) throws Exception {
		EventLoopGroup group = new NioEventLoopGroup();

		try {
			Bootstrap b = new Bootstrap();
			b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());
					ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
					ch.pipeline().addLast(new StringDecoder());
					ch.pipeline().addLast(new EchoClientHandler());
				}
			});

			ChannelFuture f = b.connect(host, port).sync();
			f.channel().closeFuture().sync();

		} finally {
			group.shutdownGracefully();
		}
	}

	public static void main(String[] args) throws Exception {
		int port = 8080;
		if (args != null && args.length > 0) {
			try {
				port = Integer.parseInt(args[0]);
			} catch (Exception e) {

			}
		}
		new EchoClient().connect(port, "127.0.0.1");
	}

}
 其clientHandler如下:

public class EchoClientHandler extends ChannelHandlerAdapter {

	private static final String ECHO_REQ = "Hi ,zouxiaohu.Welcome to Netty.$_";
	private int counter;

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		System.out.println("this is " + ++counter + " times receive server :[ " + msg + "]");

	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		for (int i = 0; i < 10; i++) {
			ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_REQ.getBytes()));
		}

	}

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

}
客户端运行如下,能正常的读取tcp包:

this is 1 times receive server :[ Hi ,zouxiaohu.Welcome to Netty.]

this is 2 times receive server :[ Hi ,zouxiaohu.Welcome to Netty.]

this is 3 times receive server :[ Hi ,zouxiaohu.Welcome to Netty.]

this is 4 times receive server :[ Hi ,zouxiaohu.Welcome to Netty.]

this is 5 times receive server :[ Hi ,zouxiaohu.Welcome to Netty.]

this is 6 times receive server :[ Hi ,zouxiaohu.Welcome to Netty.]

this is 7 times receive server :[ Hi ,zouxiaohu.Welcome to Netty.]

this is 8 times receive server :[ Hi ,zouxiaohu.Welcome to Netty.]

this is 9 times receive server :[ Hi ,zouxiaohu.Welcome to Netty.]

this is 10 times receive server :[ Hi ,zouxiaohu.Welcome to Netty.]


FixedLengFrameDecoder的应用

 FixedLengFrameDecoder对固定长度的消息进行分割。使用如下:

protected void initChannel(SocketChannel ch) throws Exception {
							ch.pipeline().addLast(new FixedLengthFrameDecoder(20));
							ch.pipeline().addLast(new StringDecoder());
							ch.pipeline().addLast(new EchoServerHandler2());
						}
  指定固定的长度为20。

 对应的ServerHandler则读取如下:

@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		String body = (String) msg;
		System.out.println("this is " + ++counter + " times receive client : [" + body + " ]");
	}
   此时运行Server,然后通过telnet命令进行发送指令回看到,发送的指令是以固定长度20的进行线上的。不足20的不会进行显示。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值