netty(一) 利用 LineBasedFrameDecoder,StringDecoder解决TCP粘包/拆包问题

.一,TCP粘包/拆包问题图示

产生TCP粘包/拆包问题如下几点:

TCP粘包/拆包问题解决方案:

二,利用 LineBasedFrameDecoder,StringDecoder解决TCP粘包/拆包问题 

server端代码示例:

package nettyDemo3;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

public class TimeServerHanler extends ChannelHandlerAdapter {

	private int counter; // 计数器

	/**
	 * 抛异常会调用此方法
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		// TODO Auto-generated method stub
		// super.exceptionCaught(ctx, cause);
		cause.printStackTrace();
		ctx.close();
	}

	/**
	 * 读取客户端回应的消息
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		// TODO Auto-generated method stub
		// super.channelRead(ctx, msg);
		// ByteBuf buf = (ByteBuf) msg;
		// byte[] bytes = new byte[buf.readableBytes()];
		// buf.readBytes(bytes);
		// String body = new String(bytes, "UTF-8");
		// System.getProperty("line.separator") 换行符
		String body = (String) msg;
		System.out.println(body + "【第" + (++counter) + "次收到客户端的消息】");
		String result = "i am server" + System.getProperty("line.separator");
		ByteBuf buf2 = Unpooled.copiedBuffer(result.getBytes());
		ctx.write(buf2);
	}

	/**
	 * 读取完成之后会调用此方法
	 */
	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		// TODO Auto-generated method stub
		// super.channelReadComplete(ctx);
		ctx.flush();
		System.out.println("读取完成");
	}

	public static void main(String[] args) {

		// 配置服务端线程组,一个负责连接 ,一个负责读写
		EventLoopGroup boss = new NioEventLoopGroup();
		EventLoopGroup worker = new NioEventLoopGroup();
		try {

			// 创建服务端辅助启动类并设置参数
			ServerBootstrap bootstrap = new ServerBootstrap();
			bootstrap.group(boss, worker).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
                                .handler(new LoggingHandler(LogLevel.INFO))						
                                .childHandler(new ChannelInitializer<SocketChannel>() {

						@Override
						protected void initChannel(SocketChannel ch) throws Exception {
							// TODO Auto-generated method stub
							// 添加回车换行符解码器
							ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
							ch.pipeline().addLast(new StringDecoder());
							ch.pipeline().addLast(new TimeServerHanler());
						}
					});
			int inetPort = 8080;
			// 绑定端口,并开始阻塞等待客户端的连接
			ChannelFuture f = bootstrap.bind(inetPort).sync();
			f.channel().closeFuture().sync();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 优雅退出,释放线程池资源
			boss.shutdownGracefully();
			worker.shutdownGracefully();
		}
	}
}

client端代码示例:

package nettyDemo3;

import org.omg.PortableServer.POA;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

public class TimeClientHandler extends ChannelHandlerAdapter {

	private int counter;

	/**
	 * 抛异常会调用此方法
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		// TODO Auto-generated method stub
		// super.exceptionCaught(ctx, cause);
		ctx.close();
		cause.printStackTrace();

	}

	/**
	 * 当客户端成功连接上服务端之后会调用此方法
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		// TODO Auto-generated method stub
		// super.channelActive(ctx);
		byte[] msg = ("i am clien" + System.getProperty("line.separator")).getBytes();
		ByteBuf buf = null;
		for (int i = 0; i < 50; i++) {
			buf = Unpooled.buffer(msg.length);
			buf.writeBytes(msg);
			ctx.writeAndFlush(buf);
		}
	}

	/**
	 * 读取服务端回应的消息
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		// TODO Auto-generated method stub
		// super.channelRead(ctx, msg);
		// ByteBuf buf = (ByteBuf) msg;
		// byte[] bytes = new byte[buf.readableBytes()];
		// buf.readBytes(bytes);
		// String body = new String(bytes, "UTF-8");
		String body = (String) msg;
		System.out.println(body + "【第" + (++counter) + "次收到服务端的消息】");
	}

	public static void main(String[] args) {
		// 配置客户端端线程组负责读写
		EventLoopGroup work = new NioEventLoopGroup();
		try {
			// 创建客户端的辅助启动类并设置参数
			Bootstrap bootstrap = new Bootstrap();
			bootstrap.group(work).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
					.handler(new ChannelInitializer<SocketChannel>() {
						@Override
						protected void initChannel(SocketChannel ch) throws Exception {
							// TODO Auto-generated method stub
							// 添加解码器
							ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
							ch.pipeline().addLast(new StringDecoder());
							ch.pipeline().addLast(new TimeClientHandler());
						}
					});
			final String host = "127.0.0.1";
			final int port = 8080;
			// 连接服务端
			ChannelFuture future = bootstrap.connect(host, port).sync();
			future.channel().closeFuture().sync();
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			// 优雅退出,释放线程池资源
			work.shutdownGracefully();
		}
	}
}

分别运行客户端和服务端实例,看到控制台输出以下图片中的内容,说明成功解决tcp粘包/拆包问题。

附: netty利用DelimiterBasedFrameDecoder 和 FixedLengthFrameDecoder  也可以按需自定义解码实现粘包/拆包问题

//添加自定义以$_为分隔符的解码器
ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
//添加固定长度解码器
ch.pipeline().addLast(new FixedLengthFrameDecoder(13));

猜你喜欢

转载自blog.csdn.net/qq_31905135/article/details/81736896